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 %}
+
+
+
+
+
+
+
+
+
+ {{ 'name'|_ }} |
+ {{ 'earned'|_ }} |
+ {{ 'spent'|_ }} |
+
+
+
+ {% for account in accounts %}
+
+
+ {{ 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 %}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Uitgaven per budget voor de gevonden transacties
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ 'name'|_ }} |
+ {{ 'earned'|_ }} |
+ {{ 'spent'|_ }} |
+
+
+
+ {% for tag in tags %}
+
+
+ {{ tag.tag }}
+ |
+ {% if tagSummary[tag.id] %}
+ {{ tagSummary[tag.id].earned|formatAmount }} |
+ {% else %}
+ {{ 0|formatAmount }} |
+ {% endif %}
+ {% if tagSummary[tag.id] %}
+ {{ tagSummary[tag.id].spent|formatAmount }} |
+ {% else %}
+ {{ 0|formatAmount }} |
+ {% endif %}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Uitgaven per category voor de gevonden transacties
+
+
+
+
+
+
+
+
+ {% if averageExpenses|length > 0 %}
+
+
+
+
+
+
+
+ {{ 'account'|_ }} |
+ {{ 'spent_average'|_ }} |
+ {{ 'total'|_ }} |
+ {{ 'transaction_count'|_ }} |
+
+
+
+ {% for row in averageExpenses %}
+ {% if loop.index > listLength %}
+
+ {% else %}
+
+ {% endif %}
+
+ {{ row.name }}
+ |
+
+ {{ row.average|formatAmount }}
+ |
+
+ {{ row.sum|formatAmount }}
+ |
+
+ {{ row.count }}
+ |
+
+ {% endfor %}
+
+
+ {% if averageExpenses|length > listLength %}
+
+
+ {{ trans('firefly.show_full_list',{number:incomeTopLength}) }}
+ |
+
+ {% endif %}
+
+
+
+
+
+ {% endif %}
+ {% if topExpenses.count > 0 %}
+
+ {% endif %}
+
+
+ {% if averageIncome|length > 0 %}
+
+
+
+
+
+
+
+ {{ 'account'|_ }} |
+ {{ 'income_average'|_ }} |
+ {{ 'total'|_ }} |
+ {{ 'transaction_count'|_ }} |
+
+
+
+ {% for row in averageIncome %}
+
+
+ {{ row.name }}
+ |
+
+ {{ row.average|formatAmount }}
+ |
+
+ {{ row.sum|formatAmount }}
+ |
+
+ {{ row.count }}
+ |
+
+ {% endfor %}
+
+
+
+
+
+ {% endif %}
+
+ {% if topIncome.count > 0 %}
+
+ {% 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
*/