diff --git a/app/Http/Controllers/Chart/BudgetReportController.php b/app/Http/Controllers/Chart/BudgetReportController.php
index 7f22f41a12..2de4fa6ab1 100644
--- a/app/Http/Controllers/Chart/BudgetReportController.php
+++ b/app/Http/Controllers/Chart/BudgetReportController.php
@@ -198,7 +198,7 @@ class BudgetReportController extends Controller
$spentKey = sprintf('%d-spent', $currency['currency_id']);
$chartData[$spentKey] = $chartData[$spentKey] ?? [
'label' => sprintf(
- '%s (%s)', (string)trans('firefly.spent_in_specific_budget', ['budget' => $budget['name']]), $currency['currency_name']
+ '%s (%s)', (string)trans('firefly.spent_in_specific_budget', ['budget' => $budget->name]), $currency['currency_name']
),
'type' => 'bar',
'currency_symbol' => $currency['currency_symbol'],
diff --git a/app/Http/Controllers/Chart/CategoryReportController.php b/app/Http/Controllers/Chart/CategoryReportController.php
index 58f89dd1b6..0c28b340f8 100644
--- a/app/Http/Controllers/Chart/CategoryReportController.php
+++ b/app/Http/Controllers/Chart/CategoryReportController.php
@@ -24,10 +24,9 @@ namespace FireflyIII\Http\Controllers\Chart;
use Carbon\Carbon;
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
-use FireflyIII\Helpers\Chart\MetaPieChartInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Category;
-use FireflyIII\Support\CacheProperties;
+use FireflyIII\Repositories\Category\OperationsRepositoryInterface;
use FireflyIII\Support\Http\Controllers\AugumentData;
use FireflyIII\Support\Http\Controllers\TransactionCalculation;
use Illuminate\Http\JsonResponse;
@@ -44,9 +43,12 @@ class CategoryReportController extends Controller
/** @var GeneratorInterface Chart generation methods. */
private $generator;
+ /** @var OperationsRepositoryInterface */
+ private $opsRepository;
/**
* CategoryReportController constructor.
+ *
* @codeCoverageIgnore
*/
public function __construct()
@@ -54,240 +56,405 @@ class CategoryReportController extends Controller
parent::__construct();
$this->middleware(
function ($request, $next) {
- $this->generator = app(GeneratorInterface::class);
+ $this->generator = app(GeneratorInterface::class);
+ $this->opsRepository = app(OperationsRepositoryInterface::class);
return $next($request);
}
);
}
+ //
+ // /**
+ // * Chart for expenses grouped by expense account.
+ // *
+ // * TODO this chart is not multi-currency aware.
+ // *
+ // * @param Collection $accounts
+ // * @param Collection $categories
+ // * @param Carbon $start
+ // * @param Carbon $end
+ // * @param string $others
+ // *
+ // * @return JsonResponse
+ // */
+ // public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others): JsonResponse
+ // {
+ // /** @var MetaPieChartInterface $helper */
+ // $helper = app(MetaPieChartInterface::class);
+ // $helper->setAccounts($accounts)->setCategories($categories)->setStart($start)->setEnd($end)->setCollectOtherObjects(1 === (int)$others);
+ //
+ // $chartData = $helper->generate('expense', 'account');
+ // $data = $this->generator->pieChart($chartData);
+ //
+ // return response()->json($data);
+ // }
+
+ //
+ // /**
+ // * Chart for income grouped by revenue account.
+ // *
+ // * TODO this chart is not multi-currency aware.
+ // *
+ // * @param Collection $accounts
+ // * @param Collection $categories
+ // * @param Carbon $start
+ // * @param Carbon $end
+ // * @param string $others
+ // *
+ // * @return JsonResponse
+ // */
+ // public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others): JsonResponse
+ // {
+ // /** @var MetaPieChartInterface $helper */
+ // $helper = app(MetaPieChartInterface::class);
+ // $helper->setAccounts($accounts);
+ // $helper->setCategories($categories);
+ // $helper->setStart($start);
+ // $helper->setEnd($end);
+ // $helper->setCollectOtherObjects(1 === (int)$others);
+ // $chartData = $helper->generate('income', 'account');
+ // $data = $this->generator->pieChart($chartData);
+ //
+ // return response()->json($data);
+ // }
/**
- * Chart for expenses grouped by expense account.
- *
- * TODO this chart is not multi-currency aware.
- *
- * @param Collection $accounts
- * @param Collection $categories
- * @param Carbon $start
- * @param Carbon $end
- * @param string $others
- *
- * @return JsonResponse
- */
- public function accountExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others): JsonResponse
- {
- /** @var MetaPieChartInterface $helper */
- $helper = app(MetaPieChartInterface::class);
- $helper->setAccounts($accounts)->setCategories($categories)->setStart($start)->setEnd($end)->setCollectOtherObjects(1 === (int)$others);
-
- $chartData = $helper->generate('expense', 'account');
- $data = $this->generator->pieChart($chartData);
-
- return response()->json($data);
- }
-
-
- /**
- * Chart for income grouped by revenue account.
- *
- * TODO this chart is not multi-currency aware.
- *
- * @param Collection $accounts
- * @param Collection $categories
- * @param Carbon $start
- * @param Carbon $end
- * @param string $others
- *
- * @return JsonResponse
- */
- public function accountIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others): JsonResponse
- {
- /** @var MetaPieChartInterface $helper */
- $helper = app(MetaPieChartInterface::class);
- $helper->setAccounts($accounts);
- $helper->setCategories($categories);
- $helper->setStart($start);
- $helper->setEnd($end);
- $helper->setCollectOtherObjects(1 === (int)$others);
- $chartData = $helper->generate('income', 'account');
- $data = $this->generator->pieChart($chartData);
-
- return response()->json($data);
- }
-
-
- /**
- * Chart for expenses grouped by expense account.
- *
- * TODO this chart is not multi-currency aware.
- *
- * @param Collection $accounts
- * @param Collection $categories
- * @param Carbon $start
- * @param Carbon $end
- * @param string $others
- *
- * @return JsonResponse
- */
- public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others): JsonResponse
- {
- /** @var MetaPieChartInterface $helper */
- $helper = app(MetaPieChartInterface::class);
- $helper->setAccounts($accounts);
- $helper->setCategories($categories);
- $helper->setStart($start);
- $helper->setEnd($end);
- $helper->setCollectOtherObjects(1 === (int)$others);
- $chartData = $helper->generate('expense', 'category');
- $data = $this->generator->pieChart($chartData);
-
- return response()->json($data);
- }
-
-
- /**
- * Piechart for income grouped by account.
- *
- * TODO this chart is not multi-currency aware.
- *
- * @param Collection $accounts
- * @param Collection $categories
- * @param Carbon $start
- * @param Carbon $end
- * @param string $others
- *
- * @return JsonResponse
- *
- */
- public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end, string $others): JsonResponse
- {
- /** @var MetaPieChartInterface $helper */
- $helper = app(MetaPieChartInterface::class);
- $helper->setAccounts($accounts);
- $helper->setCategories($categories);
- $helper->setStart($start);
- $helper->setEnd($end);
- $helper->setCollectOtherObjects(1 === (int)$others);
- $chartData = $helper->generate('income', 'category');
- $data = $this->generator->pieChart($chartData);
-
- return response()->json($data);
- }
-
-
-
- /**
- * Main report category chart.
- *
- * TODO this chart is not multi-currency aware.
- *
* @param Collection $accounts
* @param Collection $categories
* @param Carbon $start
* @param Carbon $end
*
* @return JsonResponse
- *
*/
- public function mainChart(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
+ public function budgetExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
{
- $cache = new CacheProperties;
- $cache->addProperty('chart.category.report.main');
- $cache->addProperty($accounts);
- $cache->addProperty($categories);
- $cache->addProperty($start);
- $cache->addProperty($end);
- if ($cache->has()) {
- return response()->json($cache->get()); // @codeCoverageIgnore
- }
+ $result = [];
+ $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
- $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
- $function = app('navigation')->preferredEndOfPeriod($start, $end);
- $chartData = [];
- $currentStart = clone $start;
-
- // prep chart data:
- foreach ($categories as $category) {
- $chartData[$category->id . '-in'] = [
- 'label' => $category->name . ' (' . strtolower((string)trans('firefly.income')) . ')',
- 'type' => 'bar',
- 'yAxisID' => 'y-axis-0',
- 'entries' => [],
- ];
- $chartData[$category->id . '-out'] = [
- 'label' => $category->name . ' (' . strtolower((string)trans('firefly.expenses')) . ')',
- 'type' => 'bar',
- 'yAxisID' => 'y-axis-0',
- 'entries' => [],
- ];
- // total in, total out:
- $chartData[$category->id . '-total-in'] = [
- 'label' => $category->name . ' (' . strtolower((string)trans('firefly.sum_of_income')) . ')',
- 'type' => 'line',
- 'fill' => false,
- 'yAxisID' => 'y-axis-1',
- 'entries' => [],
- ];
- $chartData[$category->id . '-total-out'] = [
- 'label' => $category->name . ' (' . strtolower((string)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->groupByCategory($this->getExpensesInCategories($accounts, $categories, $currentStart, $currentEnd));
- $income = $this->groupByCategory($this->getIncomeForCategories($accounts, $categories, $currentStart, $currentEnd));
- $label = $currentStart->formatLocalized($format);
-
- /** @var Category $category */
- foreach ($categories as $category) {
- $labelIn = $category->id . '-in';
- $labelOut = $category->id . '-out';
- $labelSumIn = $category->id . '-total-in';
- $labelSumOut = $category->id . '-total-out';
- $currentIncome = $income[$category->id] ?? '0';
- $currentExpense = $expenses[$category->id] ?? '0';
-
- // add to sum:
- $sumOfIncome[$category->id] = $sumOfIncome[$category->id] ?? '0';
- $sumOfExpense[$category->id] = $sumOfExpense[$category->id] ?? '0';
- $sumOfIncome[$category->id] = bcadd($sumOfIncome[$category->id], $currentIncome);
- $sumOfExpense[$category->id] = bcadd($sumOfExpense[$category->id], $currentExpense);
-
- // add to chart:
- $chartData[$labelIn]['entries'][$label] = $currentIncome;
- $chartData[$labelOut]['entries'][$label] = $currentExpense;
- $chartData[$labelSumIn]['entries'][$label] = $sumOfIncome[$category->id];
- $chartData[$labelSumOut]['entries'][$label] = $sumOfExpense[$category->id];
+ // loop expenses.
+ foreach ($spent as $currency) {
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $objectName = $journal['budget_name'] ?? trans('firefly.no_budget');
+ $title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
+ $result[$title] = $result[$title] ?? [
+ 'amount' => '0',
+ 'currency_symbol' => $currency['currency_symbol'],
+ ];
+ $amount = app('steam')->positive($journal['amount']);
+ $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
+ }
}
- /** @var Carbon $currentStart */
+ }
+
+ $data = $this->generator->multiCurrencyPieChart($result);
+
+ return response()->json($data);
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return JsonResponse
+ */
+ public function categoryExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
+ {
+ $result = [];
+ $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
+
+ // loop expenses.
+ foreach ($spent as $currency) {
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ $title = sprintf('%s (%s)', $category['name'], $currency['currency_name']);
+ $result[$title] = $result[$title] ?? [
+ 'amount' => '0',
+ 'currency_symbol' => $currency['currency_symbol'],
+ ];
+ foreach ($category['transaction_journals'] as $journal) {
+ $amount = app('steam')->positive($journal['amount']);
+ $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
+ }
+ }
+ }
+
+ $data = $this->generator->multiCurrencyPieChart($result);
+
+ return response()->json($data);
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ * @param string $others
+ *
+ * @return JsonResponse
+ *
+ */
+ public function categoryIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
+ {
+ $result = [];
+ $earned = $this->opsRepository->listIncome($start, $end, $accounts, $categories);
+
+ // loop expenses.
+ foreach ($earned as $currency) {
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ $title = sprintf('%s (%s)', $category['name'], $currency['currency_name']);
+ $result[$title] = $result[$title] ?? [
+ 'amount' => '0',
+ 'currency_symbol' => $currency['currency_symbol'],
+ ];
+ foreach ($category['transaction_journals'] as $journal) {
+ $amount = app('steam')->positive($journal['amount']);
+ $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
+ }
+ }
+ }
+
+ $data = $this->generator->multiCurrencyPieChart($result);
+
+ return response()->json($data);
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return JsonResponse
+ */
+ public function destinationExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
+ {
+ $result = [];
+ $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
+
+ // loop expenses.
+ foreach ($spent as $currency) {
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $objectName = $journal['destination_account_name'] ?? trans('firefly.empty');
+ $title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
+ $result[$title] = $result[$title] ?? [
+ 'amount' => '0',
+ 'currency_symbol' => $currency['currency_symbol'],
+ ];
+ $amount = app('steam')->positive($journal['amount']);
+ $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
+ }
+ }
+ }
+
+ $data = $this->generator->multiCurrencyPieChart($result);
+
+ return response()->json($data);
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return JsonResponse
+ */
+ public function destinationIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
+ {
+ $result = [];
+ $spent = $this->opsRepository->listIncome($start, $end, $accounts, $categories);
+
+ // loop expenses.
+ foreach ($spent as $currency) {
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $objectName = $journal['destination_account_name'] ?? trans('firefly.empty');
+ $title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
+ $result[$title] = $result[$title] ?? [
+ 'amount' => '0',
+ 'currency_symbol' => $currency['currency_symbol'],
+ ];
+ $amount = app('steam')->positive($journal['amount']);
+ $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
+ }
+ }
+ }
+
+ $data = $this->generator->multiCurrencyPieChart($result);
+
+ return response()->json($data);
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Category $category
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return JsonResponse
+ *
+ */
+ public function mainChart(Collection $accounts, Category $category, Carbon $start, Carbon $end): JsonResponse
+ {
+ $chartData = [];
+ $spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection([$category]));
+ $earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection([$category]));
+ $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
+
+ // loop expenses.
+ foreach ($spent as $currency) {
+ // add things to chart Data for each currency:
+ $spentKey = sprintf('%d-spent', $currency['currency_id']);
+ $chartData[$spentKey] = $chartData[$spentKey] ?? [
+ 'label' => sprintf(
+ '%s (%s)', (string)trans('firefly.spent_in_specific_category', ['category' => $category->name]), $currency['currency_name']
+ ),
+ 'type' => 'bar',
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_id' => $currency['currency_id'],
+ 'entries' => $this->makeEntries($start, $end),
+ ];
+
+ foreach ($currency['categories'] as $currentCategory) {
+ foreach ($currentCategory['transaction_journals'] as $journal) {
+ $key = $journal['date']->formatLocalized($format);
+ $amount = app('steam')->positive($journal['amount']);
+ $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0';
+ $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount);
+ }
+ }
+ }
+
+ // loop income.
+ foreach ($earned as $currency) {
+ // add things to chart Data for each currency:
+ $spentKey = sprintf('%d-earned', $currency['currency_id']);
+ $chartData[$spentKey] = $chartData[$spentKey] ?? [
+ 'label' => sprintf(
+ '%s (%s)', (string)trans('firefly.earned_in_specific_category', ['category' => $category->name]), $currency['currency_name']
+ ),
+ 'type' => 'bar',
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_id' => $currency['currency_id'],
+ 'entries' => $this->makeEntries($start, $end),
+ ];
+
+ foreach ($currency['categories'] as $currentCategory) {
+ foreach ($currentCategory['transaction_journals'] as $journal) {
+ $key = $journal['date']->formatLocalized($format);
+ $amount = app('steam')->positive($journal['amount']);
+ $chartData[$spentKey]['entries'][$key] = $chartData[$spentKey]['entries'][$key] ?? '0';
+ $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $amount);
+ }
+ }
+ }
+
+ $data = $this->generator->multiSet($chartData);
+
+ return response()->json($data);
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return JsonResponse
+ */
+ public function sourceExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
+ {
+ $result = [];
+ $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
+
+ // loop expenses.
+ foreach ($spent as $currency) {
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $objectName = $journal['source_account_name'] ?? trans('firefly.empty');
+ $title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
+ $result[$title] = $result[$title] ?? [
+ 'amount' => '0',
+ 'currency_symbol' => $currency['currency_symbol'],
+ ];
+ $amount = app('steam')->positive($journal['amount']);
+ $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
+ }
+ }
+ }
+
+ $data = $this->generator->multiCurrencyPieChart($result);
+
+ return response()->json($data);
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return JsonResponse
+ */
+ public function sourceIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
+ {
+ $result = [];
+ $earned = $this->opsRepository->listIncome($start, $end, $accounts, $categories);
+
+ // loop expenses.
+ foreach ($earned as $currency) {
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $objectName = $journal['source_account_name'] ?? trans('firefly.empty');
+ $title = sprintf('%s (%s)', $objectName, $currency['currency_name']);
+ $result[$title] = $result[$title] ?? [
+ 'amount' => '0',
+ 'currency_symbol' => $currency['currency_symbol'],
+ ];
+ $amount = app('steam')->positive($journal['amount']);
+ $result[$title]['amount'] = bcadd($result[$title]['amount'], $amount);
+ }
+ }
+ }
+
+ $data = $this->generator->multiCurrencyPieChart($result);
+
+ return response()->json($data);
+ }
+
+ /**
+ * TODO duplicate function
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return array
+ */
+ private function makeEntries(Carbon $start, Carbon $end): array
+ {
+ $return = [];
+ $format = app('navigation')->preferredCarbonLocalizedFormat($start, $end);
+ $preferredRange = app('navigation')->preferredRangeFormat($start, $end);
+ $currentStart = clone $start;
+ while ($currentStart <= $end) {
+ $currentEnd = app('navigation')->endOfPeriod($currentStart, $preferredRange);
+ $key = $currentStart->formatLocalized($format);
+ $return[$key] = '0';
$currentStart = clone $currentEnd;
- $currentStart->addDay();
- $currentStart->startOfDay();
+ $currentStart->addDay()->startOfDay();
}
- // remove all empty entries to prevent cluttering:
- $newSet = [];
- foreach ($chartData as $key => $entry) {
- if (0 === !array_sum($entry['entries'])) {
- $newSet[$key] = $chartData[$key]; // @codeCoverageIgnore
- }
- }
- if (0 === count($newSet)) {
- $newSet = $chartData;
- }
- $data = $this->generator->multiSet($newSet);
- $cache->store($data);
- return response()->json($data);
+ return $return;
}
-
}
diff --git a/app/Http/Controllers/Report/BudgetController.php b/app/Http/Controllers/Report/BudgetController.php
index ac82971151..65d12c0805 100644
--- a/app/Http/Controllers/Report/BudgetController.php
+++ b/app/Http/Controllers/Report/BudgetController.php
@@ -66,6 +66,8 @@ class BudgetController extends Controller
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
+ *
+ * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function accountPerBudget(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end)
{
@@ -171,6 +173,8 @@ class BudgetController extends Controller
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
+ *
+ * @return array|string
*/
public function avgExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end)
{
@@ -181,7 +185,8 @@ class BudgetController extends Controller
foreach ($currency['budgets'] as $budget) {
foreach ($budget['transaction_journals'] as $journal) {
$destinationId = $journal['destination_account_id'];
- $result[$destinationId] = $result[$destinationId] ?? [
+ $key = sprintf('%d-%d', $destinationId, $currency['currency_id']);
+ $result[$key] = $result[$key] ?? [
'transactions' => 0,
'sum' => '0',
'avg' => '0',
@@ -193,10 +198,10 @@ class BudgetController extends Controller
'currency_symbol' => $currency['currency_symbol'],
'currency_decimal_places' => $currency['currency_decimal_places'],
];
- $result[$destinationId]['transactions']++;
- $result[$destinationId]['sum'] = bcadd($journal['amount'], $result[$destinationId]['sum']);
- $result[$destinationId]['avg'] = bcdiv($result[$destinationId]['sum'], (string)$result[$destinationId]['transactions']);
- $result[$destinationId]['avg_float'] = (float)$result[$destinationId]['avg'];
+ $result[$key]['transactions']++;
+ $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
+ $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
+ $result[$key]['avg_float'] = (float)$result[$key]['avg'];
}
}
}
@@ -373,6 +378,8 @@ class BudgetController extends Controller
* @param Collection $budgets
* @param Carbon $start
* @param Carbon $end
+ *
+ * @return array|string
*/
public function topExpenses(Collection $accounts, Collection $budgets, Carbon $start, Carbon $end)
{
diff --git a/app/Http/Controllers/Report/CategoryController.php b/app/Http/Controllers/Report/CategoryController.php
index e1bcec6fdc..2d06bb21c3 100644
--- a/app/Http/Controllers/Report/CategoryController.php
+++ b/app/Http/Controllers/Report/CategoryController.php
@@ -25,6 +25,7 @@ namespace FireflyIII\Http\Controllers\Report;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
+use FireflyIII\Models\Category;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Category\NoCategoryRepositoryInterface;
use FireflyIII\Repositories\Category\OperationsRepositoryInterface;
@@ -62,6 +63,105 @@ class CategoryController extends Controller
);
}
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
+ */
+ public function accountPerCategory(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
+ {
+ $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
+ $earned = $this->opsRepository->listIncome($start, $end, $accounts, $categories);
+ $report = [];
+ /** @var Account $account */
+ foreach ($accounts as $account) {
+ $accountId = $account->id;
+ $report[$accountId] = $report[$accountId] ?? [
+ 'name' => $account->name,
+ 'id' => $account->id,
+ 'iban' => $account->iban,
+ 'currencies' => [],
+ ];
+ }
+
+ // loop expenses.
+ foreach ($spent as $currency) {
+ $currencyId = $currency['currency_id'];
+
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $sourceAccountId = $journal['source_account_id'];
+ $report[$sourceAccountId]['currencies'][$currencyId] = $report[$sourceAccountId]['currencies'][$currencyId] ?? [
+ 'currency_id' => $currency['currency_id'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ 'categories' => [],
+ ];
+
+ $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]
+ = $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]
+ ??
+ [
+ 'spent' => '0',
+ 'earned' => '0',
+ 'sum' => '0',
+ ];
+ $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]['spent'] = bcadd(
+ $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]['spent'], $journal['amount']
+ );
+ $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]['sum'] = bcadd(
+ $report[$sourceAccountId]['currencies'][$currencyId]['categories'][$category['id']]['sum'], $journal['amount']
+ );
+ }
+ }
+ }
+
+
+ // loop income.
+ foreach ($earned as $currency) {
+ $currencyId = $currency['currency_id'];
+
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $destinationId = $journal['destination_account_id'];
+ $report[$destinationId]['currencies'][$currencyId]
+ = $report[$destinationId]['currencies'][$currencyId]
+ ?? [
+ 'currency_id' => $currency['currency_id'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ 'categories' => [],
+ ];
+
+
+ $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]
+ = $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]
+ ??
+ [
+ 'spent' => '0',
+ 'earned' => '0',
+ 'sum' => '0',
+ ];
+ $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]['earned'] = bcadd(
+ $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]['earned'], $journal['amount']
+ );
+ $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]['sum'] = bcadd(
+ $report[$destinationId]['currencies'][$currencyId]['categories'][$category['id']]['sum'], $journal['amount']
+ );
+ }
+ }
+ }
+
+ return view('reports.category.partials.account-per-category', compact('report', 'categories'));
+ }
+
/**
* @param Collection $accounts
* @param Collection $categories
@@ -153,8 +253,8 @@ class CategoryController extends Controller
$report[$destinationAccountId]['currencies'][$currencyId]['sum'] = bcadd(
$report[$destinationAccountId]['currencies'][$currencyId]['sum'], $journal['amount']
);
- $sums[$currencyId]['earned_sum'] = bcadd($sums[$currencyId]['earned_sum'], $journal['amount']);
- $sums[$currencyId]['total_sum'] = bcadd($sums[$currencyId]['total_sum'], $journal['amount']);
+ $sums[$currencyId]['earned_sum'] = bcadd($sums[$currencyId]['earned_sum'], $journal['amount']);
+ $sums[$currencyId]['total_sum'] = bcadd($sums[$currencyId]['total_sum'], $journal['amount']);
}
}
}
@@ -162,6 +262,110 @@ class CategoryController extends Controller
return view('reports.category.partials.accounts', compact('sums', 'report'));
}
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
+ */
+ public function categories(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
+ {
+ $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
+ $earned = $this->opsRepository->listIncome($start, $end, $accounts, $categories);
+ $sums = [];
+ $report = [];
+ /** @var Category $category */
+ foreach ($categories as $category) {
+ $categoryId = $category->id;
+ $report[$categoryId] = $report[$categoryId] ?? [
+ 'name' => $category->name,
+ 'id' => $category->id,
+ 'currencies' => [],
+ ];
+ }
+ foreach ($spent as $currency) {
+ $currencyId = $currency['currency_id'];
+ $sums[$currencyId] = $sums[$currencyId] ?? [
+ 'currency_id' => $currency['currency_id'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ 'earned_sum' => '0',
+ 'spent_sum' => '0',
+ 'total_sum' => '0',
+ ];
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ $categoryId = $category['id'];
+
+ foreach ($category['transaction_journals'] as $journal) {
+ // add currency info to report array:
+ $report[$categoryId]['currencies'][$currencyId] = $report[$categoryId]['currencies'][$currencyId] ?? [
+ 'spent' => '0',
+ 'earned' => '0',
+ 'sum' => '0',
+ 'currency_id' => $currency['currency_id'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ ];
+ $report[$categoryId]['currencies'][$currencyId]['spent'] = bcadd(
+ $report[$categoryId]['currencies'][$currencyId]['spent'], $journal['amount']
+ );
+ $report[$categoryId]['currencies'][$currencyId]['sum'] = bcadd(
+ $report[$categoryId]['currencies'][$currencyId]['sum'], $journal['amount']
+ );
+
+ $sums[$currencyId]['spent_sum'] = bcadd($sums[$currencyId]['spent_sum'], $journal['amount']);
+ $sums[$currencyId]['total_sum'] = bcadd($sums[$currencyId]['total_sum'], $journal['amount']);
+ }
+ }
+ }
+
+ foreach ($earned as $currency) {
+ $currencyId = $currency['currency_id'];
+ $sums[$currencyId] = $sums[$currencyId] ?? [
+ 'currency_id' => $currency['currency_id'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ 'earned_sum' => '0',
+ 'spent_sum' => '0',
+ 'total_sum' => '0',
+ ];
+ /** @var array $category */
+ foreach ($currency['categories'] as $category) {
+ $categoryId = $category['id'];
+
+ foreach ($category['transaction_journals'] as $journal) {
+ // add currency info to report array:
+ $report[$categoryId]['currencies'][$currencyId] = $report[$categoryId]['currencies'][$currencyId] ?? [
+ 'spent' => '0',
+ 'earned' => '0',
+ 'sum' => '0',
+ 'currency_id' => $currency['currency_id'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ ];
+ $report[$categoryId]['currencies'][$currencyId]['earned'] = bcadd(
+ $report[$categoryId]['currencies'][$currencyId]['earned'], $journal['amount']
+ );
+ $report[$categoryId]['currencies'][$currencyId]['sum'] = bcadd(
+ $report[$categoryId]['currencies'][$currencyId]['sum'], $journal['amount']
+ );
+
+ $sums[$currencyId]['earned_sum'] = bcadd($sums[$currencyId]['earned_sum'], $journal['amount']);
+ $sums[$currencyId]['total_sum'] = bcadd($sums[$currencyId]['total_sum'], $journal['amount']);
+ }
+ }
+ }
+
+ return view('reports.category.partials.categories', compact('sums', 'report'));
+ }
+
/**
* Show overview of expenses in category.
*
@@ -371,7 +575,6 @@ class CategoryController extends Controller
return $result;
}
-
/**
* Show overview of operations.
*
@@ -538,6 +741,212 @@ class CategoryController extends Controller
return $result;
}
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return array|string
+ */
+ public function topExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
+ {
+ $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
+ $result = [];
+ foreach ($spent as $currency) {
+ $currencyId = $currency['currency_id'];
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $result[] = [
+ 'description' => $journal['description'],
+ 'transaction_group_id' => $journal['transaction_group_id'],
+ 'amount_float' => (float)$journal['amount'],
+ 'amount' => $journal['amount'],
+ 'date' => $journal['date']->formatLocalized($this->monthAndDayFormat),
+ 'destination_account_name' => $journal['destination_account_name'],
+ 'destination_account_id' => $journal['destination_account_id'],
+ 'currency_id' => $currency['currency_id'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ 'category_id' => $category['id'],
+ 'category_name' => $category['name'],
+ ];
+ }
+ }
+ }
+ // sort by amount_float
+ // sort temp array by amount.
+ $amounts = array_column($result, 'amount_float');
+ array_multisort($amounts, SORT_ASC, $result);
+
+ try {
+ $result = view('reports.category.partials.top-expenses', compact('result'))->render();
+ // @codeCoverageIgnoreStart
+ } catch (Throwable $e) {
+ Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
+ $result = sprintf('Could not render view: %s', $e->getMessage());
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return array|string
+ */
+ public function topIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
+ {
+ $spent = $this->opsRepository->listIncome($start, $end, $accounts, $categories);
+ $result = [];
+ foreach ($spent as $currency) {
+ $currencyId = $currency['currency_id'];
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $result[] = [
+ 'description' => $journal['description'],
+ 'transaction_group_id' => $journal['transaction_group_id'],
+ 'amount_float' => (float)$journal['amount'],
+ 'amount' => $journal['amount'],
+ 'date' => $journal['date']->formatLocalized($this->monthAndDayFormat),
+ 'source_account_name' => $journal['source_account_name'],
+ 'source_account_id' => $journal['source_account_id'],
+ 'currency_id' => $currency['currency_id'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ 'category_id' => $category['id'],
+ 'category_name' => $category['name'],
+ ];
+ }
+ }
+ }
+ // sort by amount_float
+ // sort temp array by amount.
+ $amounts = array_column($result, 'amount_float');
+ array_multisort($amounts, SORT_DESC, $result);
+
+ try {
+ $result = view('reports.category.partials.top-income', compact('result'))->render();
+ // @codeCoverageIgnoreStart
+ } catch (Throwable $e) {
+ Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
+ $result = sprintf('Could not render view: %s', $e->getMessage());
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return array|string
+ */
+ public function avgExpenses(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
+ {
+ $spent = $this->opsRepository->listExpenses($start, $end, $accounts, $categories);
+ $result = [];
+ foreach ($spent as $currency) {
+ $currencyId = $currency['currency_id'];
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $destinationId = $journal['destination_account_id'];
+ $key = sprintf('%d-%d', $destinationId, $currency['currency_id']);
+ $result[$key] = $result[$key] ?? [
+ 'transactions' => 0,
+ 'sum' => '0',
+ 'avg' => '0',
+ 'avg_float' => 0,
+ 'destination_account_name' => $journal['destination_account_name'],
+ 'destination_account_id' => $journal['destination_account_id'],
+ 'currency_id' => $currency['currency_id'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ ];
+ $result[$key]['transactions']++;
+ $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
+ $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
+ $result[$key]['avg_float'] = (float)$result[$key]['avg'];
+ }
+ }
+ }
+ // sort by amount_float
+ // sort temp array by amount.
+ $amounts = array_column($result, 'avg_float');
+ array_multisort($amounts, SORT_ASC, $result);
+
+ try {
+ $result = view('reports.category.partials.avg-expenses', compact('result'))->render();
+ // @codeCoverageIgnoreStart
+ } catch (Throwable $e) {
+ Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
+ $result = sprintf('Could not render view: %s', $e->getMessage());
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param Collection $accounts
+ * @param Collection $categories
+ * @param Carbon $start
+ * @param Carbon $end
+ *
+ * @return array|string
+ */
+ public function avgIncome(Collection $accounts, Collection $categories, Carbon $start, Carbon $end)
+ {
+ $spent = $this->opsRepository->listIncome($start, $end, $accounts, $categories);
+ $result = [];
+ foreach ($spent as $currency) {
+ $currencyId = $currency['currency_id'];
+ foreach ($currency['categories'] as $category) {
+ foreach ($category['transaction_journals'] as $journal) {
+ $sourceId = $journal['source_account_id'];
+ $key = sprintf('%d-%d', $sourceId, $currency['currency_id']);
+ $result[$key] = $result[$key] ?? [
+ 'transactions' => 0,
+ 'sum' => '0',
+ 'avg' => '0',
+ 'avg_float' => 0,
+ 'source_account_name' => $journal['source_account_name'],
+ 'source_account_id' => $journal['source_account_id'],
+ 'currency_id' => $currency['currency_id'],
+ 'currency_name' => $currency['currency_name'],
+ 'currency_symbol' => $currency['currency_symbol'],
+ 'currency_decimal_places' => $currency['currency_decimal_places'],
+ ];
+ $result[$key]['transactions']++;
+ $result[$key]['sum'] = bcadd($journal['amount'], $result[$key]['sum']);
+ $result[$key]['avg'] = bcdiv($result[$key]['sum'], (string)$result[$key]['transactions']);
+ $result[$key]['avg_float'] = (float)$result[$key]['avg'];
+ }
+ }
+ }
+ // sort by amount_float
+ // sort temp array by amount.
+ $amounts = array_column($result, 'avg_float');
+ array_multisort($amounts, SORT_DESC, $result);
+
+ try {
+ $result = view('reports.category.partials.avg-income', compact('result'))->render();
+ // @codeCoverageIgnoreStart
+ } catch (Throwable $e) {
+ Log::debug(sprintf('Could not render reports.partials.budget-period: %s', $e->getMessage()));
+ $result = sprintf('Could not render view: %s', $e->getMessage());
+ }
+
+ return $result;
+ }
+
/**
* @param array $array
*
diff --git a/app/Repositories/Category/OperationsRepository.php b/app/Repositories/Category/OperationsRepository.php
index a375fca03f..7742c44953 100644
--- a/app/Repositories/Category/OperationsRepository.php
+++ b/app/Repositories/Category/OperationsRepository.php
@@ -78,7 +78,7 @@ class OperationsRepository implements OperationsRepositoryInterface
if (null === $categories || (null !== $categories && 0 === $categories->count())) {
$collector->setCategories($this->getCategories());
}
- $collector->withCategoryInformation()->withAccountInformation();
+ $collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
$journals = $collector->getExtractedJournals();
$array = [];
@@ -115,9 +115,15 @@ class OperationsRepository implements OperationsRepositoryInterface
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
- 'amount' => app('steam')->negative($journal['amount']),
- 'date' => $journal['date'],
- 'source_account_id' => $journal['source_account_id'],
+ 'amount' => app('steam')->negative($journal['amount']),
+ 'date' => $journal['date'],
+ 'source_account_id' => $journal['source_account_id'],
+ 'budget_name' => $journal['budget_name'],
+ 'source_account_name' => $journal['source_account_name'],
+ 'destination_account_id' => $journal['destination_account_id'],
+ 'destination_account_name' => $journal['destination_account_name'],
+ 'description' => $journal['description'],
+ 'transaction_group_id' => $journal['transaction_group_id'],
];
}
@@ -188,10 +194,14 @@ class OperationsRepository implements OperationsRepositoryInterface
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
- 'amount' => app('steam')->positive($journal['amount']),
- 'date' => $journal['date'],
- 'source_account_id' => $journal['source_account_id'],
- 'destination_account_id' => $journal['destination_account_id'],
+ 'amount' => app('steam')->positive($journal['amount']),
+ 'date' => $journal['date'],
+ 'source_account_id' => $journal['source_account_id'],
+ 'destination_account_id' => $journal['destination_account_id'],
+ 'source_account_name' => $journal['source_account_name'],
+ 'destination_account_name' => $journal['destination_account_name'],
+ 'description' => $journal['description'],
+ 'transaction_group_id' => $journal['transaction_group_id'],
];
}
diff --git a/public/v1/js/ff/reports/category/month.js b/public/v1/js/ff/reports/category/month.js
index 413d93ff79..dd88a03c05 100644
--- a/public/v1/js/ff/reports/category/month.js
+++ b/public/v1/js/ff/reports/category/month.js
@@ -46,20 +46,23 @@ function drawChart() {
loadAjaxPartial('accountsHolder', accountsUri);
loadAjaxPartial('categoriesHolder', categoriesUri);
+ loadAjaxPartial('accountPerCategoryHolder', accountPerCategoryUri);
+ $.each($('.main_category_canvas'), function (i, v) {
+ var canvas = $(v);
+ columnChart(canvas.data('url'), canvas.attr('id'));
+ });
- // month view:
- //doubleYChart(mainUri, 'in-out-chart');
-
- // draw pie chart of income, depending on "show other transactions too":
- // redrawPieChart(categoryIncomeUri, 'categories-in-pie-chart');
- // redrawPieChart(categoryExpenseUri, 'categories-out-pie-chart');
- // redrawPieChart(accountIncomeUri, 'accounts-in-pie-chart');
- // redrawPieChart(accountExpenseUri, 'accounts-out-pie-chart');
+ multiCurrencyPieChart(categoryOutUri, 'category-out-pie-chart');
+ multiCurrencyPieChart(categoryInUri, 'category-in-pie-chart');
+ multiCurrencyPieChart(budgetsOutUri, 'budgets-out-pie-chart');
+ multiCurrencyPieChart(sourceOutUri, 'source-out-pie-chart');
+ multiCurrencyPieChart(sourceInUri, 'source-in-pie-chart');
+ multiCurrencyPieChart(destOutUri, 'dest-out-pie-chart');
+ multiCurrencyPieChart(destInUri, 'dest-in-pie-chart');
-}
-
-function redrawPieChart(uri, container) {
- "use strict";
- multiCurrencyPieChart(uri, container);
+ loadAjaxPartial('topExpensesHolder', topExpensesUri);
+ loadAjaxPartial('avgExpensesHolder', avgExpensesUri);
+ loadAjaxPartial('topIncomeHolder', topIncomeUri);
+ loadAjaxPartial('avgIncomeHolder', avgIncomeUri);
}
diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php
index f0038e4086..9098291d2d 100644
--- a/resources/lang/en_US/firefly.php
+++ b/resources/lang/en_US/firefly.php
@@ -888,11 +888,18 @@ return [
'cannot_change_amount_reconciled' => 'You can\'t change the amount of reconciled transactions.',
'no_budget' => '(no budget)',
'account_per_budget' => 'Account per budget',
+ 'account_per_category' => 'Account per category',
+ 'empty' => '(empty)',
'all_other_budgets' => '(all other budgets)',
'all_other_accounts' => '(all other accounts)',
'expense_per_source_account' => 'Expenses per source account',
'expense_per_destination_account' => 'Expenses per destination account',
+ 'income_per_destination_account' => 'Income per destination account',
+ 'spent_in_specific_category' => 'Spent in category ":category"',
+ 'earned_in_specific_category' => 'Earned in category ":category"',
+ 'income_per_source_account' => 'Income per source account',
'average_spending_per_destination' => 'Average expense per destination account',
+ 'average_earning_per_source' => 'Average earning per source account',
'no_budget_squared' => '(no budget)',
'perm-delete-many' => 'Deleting many items in one go can be very disruptive. Please be cautious. You can delete part of a split transaction from this page, so take care.',
'mass_deleted_transactions_success' => 'Deleted :amount transaction(s).',
diff --git a/resources/views/v1/reports/budget/partials/avg-expenses.twig b/resources/views/v1/reports/budget/partials/avg-expenses.twig
index 90c28ec1c7..b34a2ecc63 100644
--- a/resources/views/v1/reports/budget/partials/avg-expenses.twig
+++ b/resources/views/v1/reports/budget/partials/avg-expenses.twig
@@ -37,7 +37,7 @@
{% if result|length > listLength %}
-
+ |
{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}
|
diff --git a/resources/views/v1/reports/budget/partials/budgets.twig b/resources/views/v1/reports/budget/partials/budgets.twig
index 1b7c8fa880..0716387837 100644
--- a/resources/views/v1/reports/budget/partials/budgets.twig
+++ b/resources/views/v1/reports/budget/partials/budgets.twig
@@ -20,7 +20,7 @@
{{ budget.name }} ({{ currency.currency_name }})
|
-
+ |
{{ formatAmountBySymbol(currency.sum, currency.currency_symbol, currency.currency_decimal_places) }}
|
diff --git a/resources/views/v1/reports/budget/partials/top-expenses.twig b/resources/views/v1/reports/budget/partials/top-expenses.twig
index 96e825a8ad..8bcdee6243 100644
--- a/resources/views/v1/reports/budget/partials/top-expenses.twig
+++ b/resources/views/v1/reports/budget/partials/top-expenses.twig
@@ -45,7 +45,7 @@
{% if result|length > listLength %}
-
+ |
{{ trans('firefly.show_full_list',{number:incomeTopLength}) }}
|
diff --git a/resources/views/v1/reports/category/month.twig b/resources/views/v1/reports/category/month.twig
index 6a31812935..d054a4b6ea 100644
--- a/resources/views/v1/reports/category/month.twig
+++ b/resources/views/v1/reports/category/month.twig
@@ -95,7 +95,7 @@
@@ -107,7 +107,7 @@
@@ -121,7 +121,7 @@
@@ -133,7 +133,7 @@
@@ -148,7 +148,7 @@
{{ 'income_and_expenses'|_ }} ({{ category.name }})
- {##}
+
@@ -219,6 +219,7 @@
+
-
{% endblock %}
diff --git a/resources/views/v1/reports/category/partials/account-per-category.twig b/resources/views/v1/reports/category/partials/account-per-category.twig
new file mode 100644
index 0000000000..a45085672c
--- /dev/null
+++ b/resources/views/v1/reports/category/partials/account-per-category.twig
@@ -0,0 +1,31 @@
+
+
+
+ {{ 'name'|_ }} |
+ {% for category in categories %}
+ {{ category.name }} |
+ {% endfor %}
+
+
+
+ {% for account in report %}
+ {% for currency in account.currencies %}
+
+
+ {{ account.name }} ({{ currency.currency_name }})
+ |
+ {% for category in categories %}
+
+ {% if currency.categories[category.id] %}
+
+ {% endfor %}
+ |
+ {% endfor %}
+ {% endfor %}
+
+
\ No newline at end of file
diff --git a/resources/views/v1/reports/category/partials/avg-expenses.twig b/resources/views/v1/reports/category/partials/avg-expenses.twig
new file mode 100644
index 0000000000..b34a2ecc63
--- /dev/null
+++ b/resources/views/v1/reports/category/partials/avg-expenses.twig
@@ -0,0 +1,46 @@
+
+
+
+ {{ 'account'|_ }} |
+ {{ 'spent_average'|_ }} |
+ {{ 'total'|_ }} |
+ {{ 'transaction_count'|_ }} |
+
+
+
+ {% for row in result %}
+ {% if loop.index > listLength %}
+
+ {% else %}
+
+ {% endif %}
+
+
+ {{ row.destination_account_name }}
+
+ |
+
+ {{ formatAmountBySymbol(row.avg, row.currency_symbol, row.currency_decimal_places) }}
+ |
+
+
+ {{ formatAmountBySymbol(row.sum, row.currency_symbol, row.currency_decimal_places) }}
+ |
+
+
+ {{ row.transactions }}
+ |
+
+
+ {% endfor %}
+
+
+ {% if result|length > listLength %}
+
+
+ {{ trans('firefly.show_full_list',{number:incomeTopLength}) }}
+ |
+
+ {% endif %}
+
+
diff --git a/resources/views/v1/reports/category/partials/avg-income.twig b/resources/views/v1/reports/category/partials/avg-income.twig
new file mode 100644
index 0000000000..b859f13a09
--- /dev/null
+++ b/resources/views/v1/reports/category/partials/avg-income.twig
@@ -0,0 +1,46 @@
+
+
+
+ {{ 'account'|_ }} |
+ {{ 'spent_average'|_ }} |
+ {{ 'total'|_ }} |
+ {{ 'transaction_count'|_ }} |
+
+
+
+ {% for row in result %}
+ {% if loop.index > listLength %}
+
+ {% else %}
+
+ {% endif %}
+
+
+ {{ row.source_account_name }}
+
+ |
+
+ {{ formatAmountBySymbol(row.avg, row.currency_symbol, row.currency_decimal_places) }}
+ |
+
+
+ {{ formatAmountBySymbol(row.sum, row.currency_symbol, row.currency_decimal_places) }}
+ |
+
+
+ {{ row.transactions }}
+ |
+
+
+ {% endfor %}
+
+
+ {% if result|length > listLength %}
+
+
+ {{ trans('firefly.show_full_list',{number:incomeTopLength}) }}
+ |
+
+ {% endif %}
+
+
diff --git a/resources/views/v1/reports/category/partials/categories.twig b/resources/views/v1/reports/category/partials/categories.twig
new file mode 100644
index 0000000000..0846294049
--- /dev/null
+++ b/resources/views/v1/reports/category/partials/categories.twig
@@ -0,0 +1,56 @@
+
+
+
+ {{ 'name'|_ }} |
+ {{ 'spent'|_ }} |
+ {{ 'earned'|_ }} |
+ {{ 'sum'|_ }} |
+
+
+
+ {% for category in report %}
+ {% if category.currencies|length == 0 %}
+
+
+ {{ category.name }}
+ |
+ — |
+ — |
+ — |
+
+ {% endif %}
+ {% for currency in category.currencies %}
+
+
+ {{ category.name }} ({{ currency.currency_name }})
+ |
+
+ {{ formatAmountBySymbol(currency.spent, currency.currency_symbol, currency.currency_decimal_places) }}
+ |
+
+ {{ formatAmountBySymbol(currency.earned, currency.currency_symbol, currency.currency_decimal_places) }}
+ |
+
+ {{ formatAmountBySymbol(currency.sum, currency.currency_symbol, currency.currency_decimal_places) }}
+ |
+
+ {% endfor %}
+ {% endfor %}
+
+
+ {% for sum in sums %}
+
+ {{ 'sum'|_ }} ({{ sum.currency_name }}) |
+
+ {{ formatAmountBySymbol(sum.spent_sum, sum.currency_symbol, sum.currency_decimal_places) }}
+ |
+
+ {{ formatAmountBySymbol(sum.earned_sum, sum.currency_symbol, sum.currency_decimal_places) }}
+ |
+
+ {{ formatAmountBySymbol(sum.total_sum, sum.currency_symbol, sum.currency_decimal_places) }}
+ |
+
+ {% endfor %}
+
+
diff --git a/resources/views/v1/reports/category/partials/top-expenses.twig b/resources/views/v1/reports/category/partials/top-expenses.twig
new file mode 100644
index 0000000000..52e56fd701
--- /dev/null
+++ b/resources/views/v1/reports/category/partials/top-expenses.twig
@@ -0,0 +1,54 @@
+
diff --git a/resources/views/v1/reports/category/partials/top-income.twig b/resources/views/v1/reports/category/partials/top-income.twig
new file mode 100644
index 0000000000..8c256c4d9e
--- /dev/null
+++ b/resources/views/v1/reports/category/partials/top-income.twig
@@ -0,0 +1,54 @@
+
diff --git a/routes/web.php b/routes/web.php
index 97871db1e5..a565962ead 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -401,33 +401,18 @@ Route::group(
Route::get('period/{category}', ['uses' => 'CategoryController@currentPeriod', 'as' => 'current']);
Route::get('period/{category}/{date}', ['uses' => 'CategoryController@specificPeriod', 'as' => 'specific']);
Route::get('all/{category}', ['uses' => 'CategoryController@all', 'as' => 'all']);
- Route::get(
- 'report-period/0/{accountList}/{start_date}/{end_date}', ['uses' => 'CategoryController@reportPeriodNoCategory', 'as' => 'period.no-category']
- );
+ Route::get('report-period/0/{accountList}/{start_date}/{end_date}', ['uses' => 'CategoryController@reportPeriodNoCategory', 'as' => 'period.no-category']);
Route::get('report-period/{category}/{accountList}/{start_date}/{end_date}', ['uses' => 'CategoryController@reportPeriod', 'as' => 'period']);
- // these charts are used in reports (category reports):
- Route::get(
- 'category/income/{accountList}/{categoryList}/{start_date}/{end_date}',
- ['uses' => 'CategoryReportController@categoryIncome', 'as' => 'category-income']
- );
- Route::get(
- 'category/expense/{accountList}/{categoryList}/{start_date}/{end_date}',
- ['uses' => 'CategoryReportController@categoryExpense', 'as' => 'category-expense']
- );
- Route::get(
- 'account/income/{accountList}/{categoryList}/{start_date}/{end_date}',
- ['uses' => 'CategoryReportController@accountIncome', 'as' => 'account-income']
- );
- Route::get(
- 'account/expense/{accountList}/{categoryList}/{start_date}/{end_date}',
- ['uses' => 'CategoryReportController@accountExpense', 'as' => 'account-expense']
- );
- Route::get(
- 'operations/{accountList}/{categoryList}/{start_date}/{end_date}',
- ['uses' => 'CategoryReportController@mainChart', 'as' => 'main']
- );
+ Route::get('category/expense/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryReportController@categoryExpense', 'as' => 'category-expense']);
+ Route::get('category/income/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryReportController@categoryIncome', 'as' => 'category-income']);
+ Route::get('budget/expense/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryReportController@budgetExpense', 'as' => 'budget-expense']);
+ Route::get('source/expense/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryReportController@sourceExpense', 'as' => 'source-expense']);
+ Route::get('source/income/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryReportController@sourceIncome', 'as' => 'source-income']);
+ Route::get('dest/expense/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryReportController@destinationExpense', 'as' => 'dest-expense']);
+ Route::get('dest/income/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryReportController@destinationIncome', 'as' => 'dest-income']);
+ Route::get('operations/{accountList}/{category}/{start_date}/{end_date}', ['uses' => 'CategoryReportController@mainChart', 'as' => 'main']);
}
);
@@ -794,20 +779,23 @@ Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'FireflyIII\Http\Controllers\Report', 'prefix' => 'report-data/category',
'as' => 'report-data.category.'], static function () {
- // TODO still in use?
+ // TODO three routes still in use?
Route::get('operations/{accountList}/{start_date}/{end_date}', ['uses' => 'CategoryController@operations', 'as' => 'operations']);
Route::get('income/{accountList}/{start_date}/{end_date}', ['uses' => 'CategoryController@income', 'as' => 'income']);
Route::get('expenses/{accountList}/{start_date}/{end_date}', ['uses' => 'CategoryController@expenses', 'as' => 'expenses']);
Route::get('accounts/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@accounts', 'as' => 'accounts']);
Route::get('categories/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@categories', 'as' => 'categories']);
- // Route::get(
- // 'account-per-budget/{accountList}/{budgetList}/{start_date}/{end_date}',
- // ['uses' => 'BudgetController@accountPerBudget', 'as' => 'account-per-budget']
- // );
- // Route::get('top-expenses/{accountList}/{budgetList}/{start_date}/{end_date}', ['uses' => 'BudgetController@topExpenses', 'as' => 'top-expenses']);
- // Route::get('avg-expenses/{accountList}/{budgetList}/{start_date}/{end_date}', ['uses' => 'BudgetController@avgExpenses', 'as' => 'avg-expenses']);
+ Route::get(
+ 'account-per-category/{accountList}/{categoryList}/{start_date}/{end_date}',
+ ['uses' => 'CategoryController@accountPerCategory', 'as' => 'account-per-category']
+ );
+ Route::get('top-expenses/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@topExpenses', 'as' => 'top-expenses']);
+ Route::get('avg-expenses/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@avgExpenses', 'as' => 'avg-expenses']);
+
+ Route::get('top-income/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@topIncome', 'as' => 'top-income']);
+ Route::get('avg-income/{accountList}/{categoryList}/{start_date}/{end_date}', ['uses' => 'CategoryController@avgIncome', 'as' => 'avg-income']);
}
);
@@ -835,10 +823,7 @@ Route::group(
Route::get('accounts/{accountList}/{budgetList}/{start_date}/{end_date}', ['uses' => 'BudgetController@accounts', 'as' => 'accounts']);
Route::get('budgets/{accountList}/{budgetList}/{start_date}/{end_date}', ['uses' => 'BudgetController@budgets', 'as' => 'budgets']);
- Route::get(
- 'account-per-budget/{accountList}/{budgetList}/{start_date}/{end_date}',
- ['uses' => 'BudgetController@accountPerBudget', 'as' => 'account-per-budget']
- );
+ Route::get('account-per-budget/{accountList}/{budgetList}/{start_date}/{end_date}', ['uses' => 'BudgetController@accountPerBudget', 'as' => 'account-per-budget']);
Route::get('top-expenses/{accountList}/{budgetList}/{start_date}/{end_date}', ['uses' => 'BudgetController@topExpenses', 'as' => 'top-expenses']);
Route::get('avg-expenses/{accountList}/{budgetList}/{start_date}/{end_date}', ['uses' => 'BudgetController@avgExpenses', 'as' => 'avg-expenses']);