diff --git a/app/Generator/Report/Budget/MonthReportGenerator.php b/app/Generator/Report/Budget/MonthReportGenerator.php index 87557c8682..0934ef20c6 100644 --- a/app/Generator/Report/Budget/MonthReportGenerator.php +++ b/app/Generator/Report/Budget/MonthReportGenerator.php @@ -141,52 +141,10 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface return $this; } - /** - * @param Collection $collection - * @param int $sortFlag - * - * @return array - */ - private function getAverages(Collection $collection, int $sortFlag): array - { - $result = []; - /** @var Transaction $transaction */ - foreach ($collection as $transaction) { - // opposing name and ID: - $opposingId = $transaction->opposing_account_id; - - // is not set? - if (!isset($result[$opposingId])) { - $name = $transaction->opposing_account_name; - $result[$opposingId] = [ - 'name' => $name, - 'count' => 1, - 'id' => $opposingId, - 'average' => $transaction->transaction_amount, - 'sum' => $transaction->transaction_amount, - ]; - continue; - } - $result[$opposingId]['count']++; - $result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount); - $result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count'])); - } - - // sort result by average: - $average = []; - foreach ($result as $key => $row) { - $average[$key] = floatval($row['average']); - } - - array_multisort($average, $sortFlag, $result); - - return $result; - } - /** * @return Collection */ - private function getExpenses(): Collection + protected function getExpenses(): Collection { if ($this->expenses->count() > 0) { Log::debug('Return previous set of expenses.'); @@ -208,34 +166,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface return $transactions; } - /** - * @return Collection - */ - private function getTopExpenses(): Collection - { - $transactions = $this->getExpenses()->sortBy('transaction_amount'); - - return $transactions; - } - - /** - * @param Collection $collection - * - * @return array - */ - private function summarizeByAccount(Collection $collection): array - { - $result = []; - /** @var Transaction $transaction */ - foreach ($collection as $transaction) { - $accountId = $transaction->account_id; - $result[$accountId] = $result[$accountId] ?? '0'; - $result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]); - } - - return $result; - } - /** * @param Collection $collection * diff --git a/app/Generator/Report/Category/MonthReportGenerator.php b/app/Generator/Report/Category/MonthReportGenerator.php index 0a3c60be3a..654f67befc 100644 --- a/app/Generator/Report/Category/MonthReportGenerator.php +++ b/app/Generator/Report/Category/MonthReportGenerator.php @@ -151,52 +151,10 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface return $this; } - /** - * @param Collection $collection - * @param int $sortFlag - * - * @return array - */ - private function getAverages(Collection $collection, int $sortFlag): array - { - $result = []; - /** @var Transaction $transaction */ - foreach ($collection as $transaction) { - // opposing name and ID: - $opposingId = $transaction->opposing_account_id; - - // is not set? - if (!isset($result[$opposingId])) { - $name = $transaction->opposing_account_name; - $result[$opposingId] = [ - 'name' => $name, - 'count' => 1, - 'id' => $opposingId, - 'average' => $transaction->transaction_amount, - 'sum' => $transaction->transaction_amount, - ]; - continue; - } - $result[$opposingId]['count']++; - $result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount); - $result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count'])); - } - - // sort result by average: - $average = []; - foreach ($result as $key => $row) { - $average[$key] = floatval($row['average']); - } - - array_multisort($average, $sortFlag, $result); - - return $result; - } - /** * @return Collection */ - private function getExpenses(): Collection + protected function getExpenses(): Collection { if ($this->expenses->count() > 0) { Log::debug('Return previous set of expenses.'); @@ -221,7 +179,7 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface /** * @return Collection */ - private function getIncome(): Collection + protected function getIncome(): Collection { if ($this->income->count() > 0) { return $this->income; @@ -240,85 +198,6 @@ class MonthReportGenerator extends Support implements ReportGeneratorInterface return $transactions; } - /** - * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five. - * @param array $spent - * @param array $earned - * - * @return array - */ - private function getObjectSummary(array $spent, array $earned): array - { - $return = []; - - /** - * @var int $accountId - * @var string $entry - */ - foreach ($spent as $objectId => $entry) { - if (!isset($return[$objectId])) { - $return[$objectId] = ['spent' => 0, 'earned' => 0]; - } - - $return[$objectId]['spent'] = $entry; - } - unset($entry); - - /** - * @var int $accountId - * @var string $entry - */ - foreach ($earned as $objectId => $entry) { - if (!isset($return[$objectId])) { - $return[$objectId] = ['spent' => 0, 'earned' => 0]; - } - - $return[$objectId]['earned'] = $entry; - } - - - return $return; - } - - - /** - * @return Collection - */ - private function getTopExpenses(): Collection - { - $transactions = $this->getExpenses()->sortBy('transaction_amount'); - - return $transactions; - } - - /** - * @return Collection - */ - private function getTopIncome(): Collection - { - $transactions = $this->getIncome()->sortByDesc('transaction_amount'); - - return $transactions; - } - - /** - * @param Collection $collection - * - * @return array - */ - private function summarizeByAccount(Collection $collection): array - { - $result = []; - /** @var Transaction $transaction */ - foreach ($collection as $transaction) { - $accountId = $transaction->account_id; - $result[$accountId] = $result[$accountId] ?? '0'; - $result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]); - } - - return $result; - } - /** * @param Collection $collection * diff --git a/app/Generator/Report/Category/MultiYearReportGenerator.php b/app/Generator/Report/Category/MultiYearReportGenerator.php index 62b0e32af9..0f57b6b888 100644 --- a/app/Generator/Report/Category/MultiYearReportGenerator.php +++ b/app/Generator/Report/Category/MultiYearReportGenerator.php @@ -17,7 +17,7 @@ namespace FireflyIII\Generator\Report\Category; /** * Class MultiYearReportGenerator * - * @package FireflyIII\Generator\Report\Audit + * @package FireflyIII\Generator\Report\Category */ class MultiYearReportGenerator extends MonthReportGenerator { diff --git a/app/Generator/Report/Category/YearReportGenerator.php b/app/Generator/Report/Category/YearReportGenerator.php index a118c4c5b0..e4f62dba0f 100644 --- a/app/Generator/Report/Category/YearReportGenerator.php +++ b/app/Generator/Report/Category/YearReportGenerator.php @@ -17,7 +17,7 @@ namespace FireflyIII\Generator\Report\Category; /** * Class YearReportGenerator * - * @package FireflyIII\Generator\Report\Audit + * @package FireflyIII\Generator\Report\Category */ class YearReportGenerator extends MonthReportGenerator { diff --git a/app/Generator/Report/Support.php b/app/Generator/Report/Support.php index 573129ef97..9125031579 100644 --- a/app/Generator/Report/Support.php +++ b/app/Generator/Report/Support.php @@ -80,4 +80,124 @@ class Support return $result; } + /** + * @param Collection $collection + * @param int $sortFlag + * + * @return array + */ + protected function getAverages(Collection $collection, int $sortFlag): array + { + $result = []; + /** @var Transaction $transaction */ + foreach ($collection as $transaction) { + // opposing name and ID: + $opposingId = $transaction->opposing_account_id; + + // is not set? + if (!isset($result[$opposingId])) { + $name = $transaction->opposing_account_name; + $result[$opposingId] = [ + 'name' => $name, + 'count' => 1, + 'id' => $opposingId, + 'average' => $transaction->transaction_amount, + 'sum' => $transaction->transaction_amount, + ]; + continue; + } + $result[$opposingId]['count']++; + $result[$opposingId]['sum'] = bcadd($result[$opposingId]['sum'], $transaction->transaction_amount); + $result[$opposingId]['average'] = bcdiv($result[$opposingId]['sum'], strval($result[$opposingId]['count'])); + } + + // sort result by average: + $average = []; + foreach ($result as $key => $row) { + $average[$key] = floatval($row['average']); + } + + array_multisort($average, $sortFlag, $result); + + return $result; + } + + /** + * @SuppressWarnings(PHPMD.CyclomaticComplexity) // it's exactly five. + * @param array $spent + * @param array $earned + * + * @return array + */ + protected function getObjectSummary(array $spent, array $earned): array + { + $return = []; + + /** + * @var int $accountId + * @var string $entry + */ + foreach ($spent as $objectId => $entry) { + if (!isset($return[$objectId])) { + $return[$objectId] = ['spent' => 0, 'earned' => 0]; + } + + $return[$objectId]['spent'] = $entry; + } + unset($entry); + + /** + * @var int $accountId + * @var string $entry + */ + foreach ($earned as $objectId => $entry) { + if (!isset($return[$objectId])) { + $return[$objectId] = ['spent' => 0, 'earned' => 0]; + } + + $return[$objectId]['earned'] = $entry; + } + + + return $return; + } + + /** + * @return Collection + */ + public function getTopExpenses(): Collection + { + $transactions = $this->getExpenses()->sortBy('transaction_amount'); + + return $transactions; + } + + /** + * @return Collection + */ + public function getTopIncome(): Collection + { + $transactions = $this->getIncome()->sortByDesc('transaction_amount'); + + return $transactions; + } + + /** + * @param Collection $collection + * + * @return array + */ + protected function summarizeByAccount(Collection $collection): array + { + $result = []; + /** @var Transaction $transaction */ + foreach ($collection as $transaction) { + $accountId = $transaction->account_id; + $result[$accountId] = $result[$accountId] ?? '0'; + $result[$accountId] = bcadd($transaction->transaction_amount, $result[$accountId]); + } + + return $result; + } + } diff --git a/app/Generator/Report/Tag/MonthReportGenerator.php b/app/Generator/Report/Tag/MonthReportGenerator.php new file mode 100644 index 0000000000..d58598c8f9 --- /dev/null +++ b/app/Generator/Report/Tag/MonthReportGenerator.php @@ -0,0 +1,219 @@ +expenses = new Collection; + $this->income = new Collection; + } + + /** + * @return string + */ + public function generate(): string + { + $accountIds = join(',', $this->accounts->pluck('id')->toArray()); + $tagTags = join(',', $this->tags->pluck('tag')->toArray()); + $reportType = 'tag'; + $expenses = $this->getExpenses(); + $income = $this->getIncome(); + $accountSummary = $this->getObjectSummary($this->summarizeByAccount($expenses), $this->summarizeByAccount($income)); + $tagSummary = $this->getObjectSummary($this->summarizeByTag($expenses), $this->summarizeByTag($income)); + $averageExpenses = $this->getAverages($expenses, SORT_ASC); + $averageIncome = $this->getAverages($income, SORT_DESC); + $topExpenses = $this->getTopExpenses(); + $topIncome = $this->getTopIncome(); + + + // render! + return view( + 'reports.tag.month', compact( + 'accountIds', 'tagTags', 'reportType', 'accountSummary', 'tagSummary', 'averageExpenses', 'averageIncome', 'topIncome', + 'topExpenses' + ) + )->with('start', $this->start)->with('end', $this->end)->with('tags', $this->tags)->with('accounts', $this->accounts)->render(); + } + + /** + * @param Collection $accounts + * + * @return ReportGeneratorInterface + */ + public function setAccounts(Collection $accounts): ReportGeneratorInterface + { + $this->accounts = $accounts; + + return $this; + } + + /** + * @param Collection $budgets + * + * @return ReportGeneratorInterface + */ + public function setBudgets(Collection $budgets): ReportGeneratorInterface + { + return $this; + } + + /** + * @param Collection $categories + * + * @return ReportGeneratorInterface + */ + public function setCategories(Collection $categories): ReportGeneratorInterface + { + return $this; + } + + /** + * @param Carbon $date + * + * @return ReportGeneratorInterface + */ + public function setEndDate(Carbon $date): ReportGeneratorInterface + { + $this->end = $date; + + return $this; + } + + /** + * @param Carbon $date + * + * @return ReportGeneratorInterface + */ + public function setStartDate(Carbon $date): ReportGeneratorInterface + { + $this->start = $date; + + return $this; + } + + /** + * @param Collection $tags + * + * @return ReportGeneratorInterface + */ + public function setTags(Collection $tags): ReportGeneratorInterface + { + $this->tags = $tags; + + return $this; + } + + /** + * @return Collection + */ + protected function getExpenses(): Collection + { + if ($this->expenses->count() > 0) { + Log::debug('Return previous set of expenses.'); + + return $this->expenses; + } + + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) + ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->setTags($this->tags)->withOpposingAccount()->disableFilter(); + + $accountIds = $this->accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $transactions = self::filterExpenses($transactions, $accountIds); + $this->expenses = $transactions; + + return $transactions; + } + + /** + * @return Collection + */ + protected function getIncome(): Collection + { + if ($this->income->count() > 0) { + return $this->income; + } + + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts($this->accounts)->setRange($this->start, $this->end) + ->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ->setTags($this->tags)->withOpposingAccount(); + $accountIds = $this->accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $transactions = self::filterIncome($transactions, $accountIds); + $this->income = $transactions; + + return $transactions; + } + + /** + * @param Collection $collection + * + * @return array + */ + protected function summarizeByTag(Collection $collection): array + { + $result = []; + /** @var Transaction $transaction */ + foreach ($collection as $transaction) { + $journal = $transaction->transactionJournal; + $journalTags = $journal->tags; + /** @var Tag $journalTag */ + foreach ($journalTags as $journalTag) { + $journalTagId = $journalTag->id; + $result[$journalTagId] = $result[$journalTagId] ?? '0'; + $result[$journalTagId] = bcadd($transaction->transaction_amount, $result[$journalTagId]); + } + } + + return $result; + } +} diff --git a/app/Generator/Report/Tag/MultiYearReportGenerator.php b/app/Generator/Report/Tag/MultiYearReportGenerator.php new file mode 100644 index 0000000000..3f92a96e7a --- /dev/null +++ b/app/Generator/Report/Tag/MultiYearReportGenerator.php @@ -0,0 +1,25 @@ +query->leftJoin('tag_transaction_journal', 'tag_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id'); } } + + /** + * @param Collection $tags + * + * @return JournalCollectorInterface + */ + public function setTags(Collection $tags): JournalCollectorInterface + { + $this->joinTagTables(); + $tagIds = $tags->pluck('id')->toArray(); + $this->query->whereIn('tag_transaction_journal.tag_id', $tagIds); + + return $this; + } } diff --git a/app/Helpers/Collector/JournalCollectorInterface.php b/app/Helpers/Collector/JournalCollectorInterface.php index 80e41eb781..05306699a7 100644 --- a/app/Helpers/Collector/JournalCollectorInterface.php +++ b/app/Helpers/Collector/JournalCollectorInterface.php @@ -141,6 +141,13 @@ interface JournalCollectorInterface */ public function setTag(Tag $tag): JournalCollectorInterface; + /** + * @param Collection $tags + * + * @return JournalCollectorInterface + */ + public function setTags(Collection $tags): JournalCollectorInterface; + /** * @param array $types * diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php index 94f6cfb370..faffd3124f 100644 --- a/app/Http/Controllers/Chart/CategoryReportController.php +++ b/app/Http/Controllers/Chart/CategoryReportController.php @@ -331,22 +331,4 @@ class CategoryReportController extends Controller return $grouped; } - - /** - * @param Collection $set - * - * @return array - */ - private function groupByOpposingAccount(Collection $set): array - { - $grouped = []; - /** @var Transaction $transaction */ - foreach ($set as $transaction) { - $accountId = $transaction->opposing_account_id; - $grouped[$accountId] = $grouped[$accountId] ?? '0'; - $grouped[$accountId] = bcadd($transaction->transaction_amount, $grouped[$accountId]); - } - - return $grouped; - } } diff --git a/app/Http/Controllers/Chart/TagReportController.php b/app/Http/Controllers/Chart/TagReportController.php new file mode 100644 index 0000000000..afa6fb8c31 --- /dev/null +++ b/app/Http/Controllers/Chart/TagReportController.php @@ -0,0 +1,216 @@ +generator = app(GeneratorInterface::class); + } + + /** + * @param Collection $accounts + * @param Collection $tags + * @param Carbon $start + * @param Carbon $end + * + * @return \Illuminate\Http\JsonResponse + */ + public function mainChart(Collection $accounts, Collection $tags, Carbon $start, Carbon $end) + { + $cache = new CacheProperties; + $cache->addProperty('chart.category.report.main'); + $cache->addProperty($accounts); + $cache->addProperty($tags); + $cache->addProperty($start); + $cache->addProperty($end); + if ($cache->has()) { + return Response::json($cache->get()); + } + + $format = Navigation::preferredCarbonLocalizedFormat($start, $end); + $function = Navigation::preferredEndOfPeriod($start, $end); + $chartData = []; + $currentStart = clone $start; + + // prep chart data: + foreach ($tags as $tag) { + $chartData[$tag->id . '-in'] = [ + 'label' => $tag->tag . ' (' . strtolower(strval(trans('firefly.income'))) . ')', + 'type' => 'bar', + 'yAxisID' => 'y-axis-0', + 'entries' => [], + ]; + $chartData[$tag->id . '-out'] = [ + 'label' => $tag->tag . ' (' . strtolower(strval(trans('firefly.expenses'))) . ')', + 'type' => 'bar', + 'yAxisID' => 'y-axis-0', + 'entries' => [], + ]; + // total in, total out: + $chartData[$tag->id . '-total-in'] = [ + 'label' => $tag->tag . ' (' . strtolower(strval(trans('firefly.sum_of_income'))) . ')', + 'type' => 'line', + 'fill' => false, + 'yAxisID' => 'y-axis-1', + 'entries' => [], + ]; + $chartData[$tag->id . '-total-out'] = [ + 'label' => $tag->tag . ' (' . 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(); + $expenses = $this->groupByTag($this->getExpenses($accounts, $tags, $currentStart, $currentEnd)); + $income = $this->groupByTag($this->getIncome($accounts, $tags, $currentStart, $currentEnd)); + $label = $currentStart->formatLocalized($format); + + /** @var Tag $tag */ + foreach ($tags as $tag) { + $labelIn = $tag->id . '-in'; + $labelOut = $tag->id . '-out'; + $labelSumIn = $tag->id . '-total-in'; + $labelSumOut = $tag->id . '-total-out'; + $currentIncome = $income[$tag->id] ?? '0'; + $currentExpense = $expenses[$tag->id] ?? '0'; + + + // add to sum: + $sumOfIncome[$tag->id] = $sumOfIncome[$tag->id] ?? '0'; + $sumOfExpense[$tag->id] = $sumOfExpense[$tag->id] ?? '0'; + $sumOfIncome[$tag->id] = bcadd($sumOfIncome[$tag->id], $currentIncome); + $sumOfExpense[$tag->id] = bcadd($sumOfExpense[$tag->id], $currentExpense); + + // add to chart: + $chartData[$labelIn]['entries'][$label] = $currentIncome; + $chartData[$labelOut]['entries'][$label] = $currentExpense; + $chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$tag->id]; + $chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$tag->id]; + } + $currentStart = clone $currentEnd; + $currentStart->addDay(); + } + // remove all empty entries to prevent cluttering: + $newSet = []; + foreach ($chartData as $key => $entry) { + if (!array_sum($entry['entries']) == 0) { + $newSet[$key] = $chartData[$key]; + } + } + if (count($newSet) === 0) { + $newSet = $chartData; + } + $data = $this->generator->multiSet($newSet); + $cache->store($data); + + return Response::json($data); + } + + + /** + * @param Collection $accounts + * @param Collection $tags + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function getExpenses(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): Collection + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) + ->setTags($tags)->withOpposingAccount()->disableFilter(); + $accountIds = $accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $set = MonthReportGenerator::filterExpenses($transactions, $accountIds); + + return $set; + } + + /** + * @param Collection $accounts + * @param Collection $tags + * @param Carbon $start + * @param Carbon $end + * + * @return Collection + */ + private function getIncome(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): Collection + { + /** @var JournalCollectorInterface $collector */ + $collector = app(JournalCollectorInterface::class); + $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) + ->setTags($tags)->withOpposingAccount(); + $accountIds = $accounts->pluck('id')->toArray(); + $transactions = $collector->getJournals(); + $set = MonthReportGenerator::filterIncome($transactions, $accountIds); + + return $set; + } + + /** + * @param Collection $set + * + * @return array + */ + private function groupByTag(Collection $set): array + { + // group by category ID: + $grouped = []; + /** @var Transaction $transaction */ + foreach ($set as $transaction) { + $journal = $transaction->transactionJournal; + $journalTags = $journal->tags; + /** @var Tag $journalTag */ + foreach ($journalTags as $journalTag) { + $journalTagId = $journalTag->id; + $grouped[$journalTagId] = $grouped[$journalTagId] ?? '0'; + $grouped[$journalTagId] = bcadd($transaction->transaction_amount, $grouped[$journalTagId]); + } + } + + return $grouped; + } + +} \ No newline at end of file diff --git a/app/Http/breadcrumbs.php b/app/Http/breadcrumbs.php index 05bf0acb56..62d7311e0a 100644 --- a/app/Http/breadcrumbs.php +++ b/app/Http/breadcrumbs.php @@ -556,6 +556,19 @@ Breadcrumbs::register( } ); +Breadcrumbs::register( + 'reports.report.tag', function (BreadCrumbGenerator $breadcrumbs, string $accountIds, string $tagTags, Carbon $start, Carbon $end) { + $breadcrumbs->parent('reports.index'); + + $monthFormat = (string)trans('config.month_and_day'); + $startString = $start->formatLocalized($monthFormat); + $endString = $end->formatLocalized($monthFormat); + $title = (string)trans('firefly.report_tag', ['start' => $startString, 'end' => $endString]); + + $breadcrumbs->push($title, route('reports.report.tag', [$accountIds, $tagTags, $start->format('Ymd'), $end->format('Ymd')])); +} +); + Breadcrumbs::register( 'reports.report.category', function (BreadCrumbGenerator $breadcrumbs, string $accountIds, string $categoryIds, Carbon $start, Carbon $end) { $breadcrumbs->parent('reports.index'); diff --git a/public/js/ff/reports/tag/all.js b/public/js/ff/reports/tag/all.js new file mode 100644 index 0000000000..25a412d1c5 --- /dev/null +++ b/public/js/ff/reports/tag/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/tag/month.js b/public/js/ff/reports/tag/month.js new file mode 100644 index 0000000000..1f1584655c --- /dev/null +++ b/public/js/ff/reports/tag/month.js @@ -0,0 +1,66 @@ +/* + * month.js + * Copyright (c) 2017 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. + */ + +/** global: tagIncomeUri, tagExpenseUri, accountIncomeUri, accountExpenseUri, tagBudgetUri, tagCategoryUri, mainUri */ + +$(function () { + "use strict"; + drawChart(); + + $('#tags-in-pie-chart-checked').on('change', function () { + redrawPieChart('tags-in-pie-chart', tagIncomeUri); + }); + + $('#tags-out-pie-chart-checked').on('change', function () { + redrawPieChart('tags-out-pie-chart', tagExpenseUri); + }); + + $('#accounts-in-pie-chart-checked').on('change', function () { + redrawPieChart('accounts-in-pie-chart', accountIncomeUri); + }); + + $('#accounts-out-pie-chart-checked').on('change', function () { + redrawPieChart('accounts-out-pie-chart', accountExpenseUri); + }); + + // two extra charts: + pieChart(tagBudgetUri, 'budgets-out-pie-chart'); + pieChart(tagCategoryUri, 'categories-out-pie-chart'); + +}); + + +function drawChart() { + "use strict"; + + // month view: + doubleYChart(mainUri, 'in-out-chart'); + + // draw pie chart of income, depending on "show other transactions too": + redrawPieChart('tags-in-pie-chart', tagIncomeUri); + redrawPieChart('tags-out-pie-chart', tagExpenseUri); + redrawPieChart('accounts-in-pie-chart', accountIncomeUri); + redrawPieChart('accounts-out-pie-chart', accountExpenseUri); + + +} + +function redrawPieChart(container, uri) { + "use strict"; + var checkbox = $('#' + container + '-checked'); + + var others = '0'; + // check if box is checked: + if (checkbox.prop('checked')) { + others = '1'; + } + uri = uri.replace('OTHERS', others); + + pieChart(uri, container); + +} diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index d4e5fb0c73..3fe45e6dd0 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -667,6 +667,7 @@ return [ 'report_audit' => 'Transaction history overview between :start and :end', 'report_category' => 'Category report between :start and :end', 'report_budget' => 'Budget report between :start and :end', + 'report_tag' => 'Tag report between :start and :end', 'quick_link_reports' => 'Quick links', 'quick_link_default_report' => 'Default financial report', 'quick_link_audit_report' => 'Transaction history overview', diff --git a/resources/views/reports/tag/month.twig b/resources/views/reports/tag/month.twig new file mode 100644 index 0000000000..4ba5515c30 --- /dev/null +++ b/resources/views/reports/tag/month.twig @@ -0,0 +1,434 @@ +{% extends "./layout/default" %} + +{% block breadcrumbs %} + {{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, accountIds, tagTags, start, end) }} +{% endblock %} + +{% block content %} + +
+
+
+
+

{{ 'accounts'|_ }}

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

{{ 'income_per_tag'|_ }}

+
+
+ + +
+
+
+
+
+
+

{{ 'expense_per_tag'|_ }}

+
+
+ + +
+
+
+
+
+
+

{{ 'expense_per_budget'|_ }}

+
+
+ + Uitgaven per budget voor de gevonden transacties +
+
+
+
+
+
+
+
+

{{ 'tags'|_ }}

+
+
+ + + + + + + + + + {% for tag in tags %} + + + {% if tagSummary[tag.id] %} + + {% else %} + + {% endif %} + {% if tagSummary[tag.id] %} + + {% else %} + + {% endif %} + + {% endfor %} + +
{{ 'name'|_ }}{{ 'earned'|_ }}{{ 'spent'|_ }}
+ {{ tag.tag }} + {{ tagSummary[tag.id].earned|formatAmount }}{{ 0|formatAmount }}{{ tagSummary[tag.id].spent|formatAmount }}{{ 0|formatAmount }}
+
+
+
+
+
+
+

{{ 'income_per_account'|_ }}

+
+
+ + +
+
+
+
+
+
+

{{ 'expense_per_account'|_ }}

+
+
+ + +
+
+
+ +
+
+
+

{{ 'expense_per_category'|_ }}

+
+
+ + Uitgaven per category voor de gevonden transacties +
+
+
+ +
+ +
+
+
+
+

{{ 'income_and_expenses'|_ }}

+
+
+ +
+
+
+
+
+ {% if averageExpenses|length > 0 %} +
+
+
+

{{ 'average_spending_per_account'|_ }}

+
+
+ + + + + + + + + + + {% for row in averageExpenses %} + {% if loop.index > listLength %} + + {% else %} + + {% endif %} + + + + + + {% endfor %} + + + {% if averageExpenses|length > listLength %} + + + + {% endif %} + +
{{ 'account'|_ }}{{ 'spent_average'|_ }}{{ 'total'|_ }}{{ 'transaction_count'|_ }}
+ {{ row.name }} + + {{ row.average|formatAmount }} + + {{ row.sum|formatAmount }} + + {{ row.count }} +
+ {{ trans('firefly.show_full_list',{number:incomeTopLength}) }} +
+
+
+
+ {% endif %} + {% if topExpenses.count > 0 %} +
+ +
+
+

{{ 'expenses'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})

+
+
+ + + + + + + + + + + {% for row in topExpenses %} + {% if loop.index > listLength %} + + {% else %} + + {% endif %} + + + + + + {% endfor %} + + + {% if topExpenses|length > listLength %} + + + + {% endif %} + +
{{ 'description'|_ }}{{ 'date'|_ }}{{ 'account'|_ }}{{ 'amount'|_ }}
+ + {% if row.transaction_description|length > 0 %} + {{ row.transaction_description }} ({{ row.description }}) + {% else %} + {{ row.description }} + {% endif %} + + + {{ row.date.formatLocalized(monthAndDayFormat) }} + + + {{ row.opposing_account_name }} + + + {{ row.transaction_amount|formatAmount }} +
+ {{ trans('firefly.show_full_list',{number:incomeTopLength}) }} +
+
+
+
+ {% endif %} +
+
+ {% if averageIncome|length > 0 %} +
+
+
+

{{ 'average_income_per_account'|_ }}

+
+
+ + + + + + + + + + + {% for row in averageIncome %} + + + + + + + {% endfor %} + +
{{ 'account'|_ }}{{ 'income_average'|_ }}{{ 'total'|_ }}{{ 'transaction_count'|_ }}
+ {{ row.name }} + + {{ row.average|formatAmount }} + + {{ row.sum|formatAmount }} + + {{ row.count }} +
+
+
+
+ {% endif %} +
+ {% if topIncome.count > 0 %} +
+
+

{{ 'income'|_ }} ({{ trans('firefly.topX', {number: listLength}) }})

+
+
+ + + + + + + + + + + {% for row in topIncome %} + {% if loop.index > listLength %} + + {% else %} + + {% endif %} + + + + + + {% endfor %} + + + {% if topIncome.count > listLength %} + + + + {% endif %} + +
{{ 'description'|_ }}{{ 'date'|_ }}{{ 'account'|_ }}{{ 'amount'|_ }}
+ + {% if row.transaction_description|length > 0 %} + {{ row.transaction_description }} ({{ row.description }}) + {% else %} + {{ row.description }} + {% endif %} + + + {{ row.date.formatLocalized(monthAndDayFormat) }} + + + {{ row.opposing_account_name }} + + + {{ row.transaction_amount|formatAmount }} +
+ {{ trans('firefly.show_full_list',{number:incomeTopLength}) }} +
+
+
+ {% endif %} +
+
+ +{% endblock %} + +{% block scripts %} + + + + + + + + + + + +{% endblock %} + +{% block styles %} + +{% endblock %} diff --git a/routes/web.php b/routes/web.php index 92a108e5fe..9317011312 100755 --- a/routes/web.php +++ b/routes/web.php @@ -310,6 +310,47 @@ Route::group( } ); +/** + * Chart\Tag Controller + */ +Route::group( + ['middleware' => 'user-full-auth', 'namespace' => 'Chart', 'prefix' => 'chart/tag', 'as' => 'chart.tag.'], function () { + + // these charts are used in reports (tag reports): + Route::get( + 'tag/income/{accountList}/{tagList}/{start_date}/{end_date}/{others}', + ['uses' => 'TagReportController@tagIncome', 'as' => 'tag-income'] + ); + Route::get( + 'tag/expense/{accountList}/{tagList}/{start_date}/{end_date}/{others}', + ['uses' => 'TagReportController@tagExpense', 'as' => 'tag-expense'] + ); + Route::get( + 'account/income/{accountList}/{tagList}/{start_date}/{end_date}/{others}', + ['uses' => 'TagReportController@accountIncome', 'as' => 'account-income'] + ); + Route::get( + 'account/expense/{accountList}/{tagList}/{start_date}/{end_date}/{others}', + ['uses' => 'TagReportController@accountExpense', 'as' => 'account-expense'] + ); + Route::get( + 'budget/expense/{accountList}/{tagList}/{start_date}/{end_date}', + ['uses' => 'TagReportController@budgetExpense', 'as' => 'budget-expense'] + ); + Route::get( + 'category/expense/{accountList}/{tagList}/{start_date}/{end_date}', + ['uses' => 'TagReportController@categoryExpense', 'as' => 'category-expense'] + ); + + + Route::get( + 'operations/{accountList}/{tagList}/{start_date}/{end_date}', + ['uses' => 'TagReportController@mainChart', 'as' => 'main'] + ); + +} +); + /** * Chart\PiggyBank Controller */