. */ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Generator\Chart\Basic\GeneratorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Category; use FireflyIII\Repositories\Category\OperationsRepositoryInterface; use FireflyIII\Support\Facades\Navigation; use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\ResolvesJournalAmountAndCurrency; use FireflyIII\Support\Http\Controllers\TransactionCalculation; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; /** * Separate controller because many helper functions are shared. * * Class CategoryReportController */ final class CategoryReportController extends Controller { use AugumentData; use ResolvesJournalAmountAndCurrency; use TransactionCalculation; private GeneratorInterface $generator; private OperationsRepositoryInterface $opsRepository; /** * CategoryReportController constructor. */ public function __construct() { parent::__construct(); $this->middleware(function ($request, $next) { $this->generator = app(GeneratorInterface::class); $this->opsRepository = app(OperationsRepositoryInterface::class); return $next($request); }); } public function budgetExpense(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) { $journalData = $this->resolveJournalAmountAndCurrency($journal, $currency); $objectName = $journal['budget_name'] ?? trans('firefly.no_budget'); $title = sprintf('%s (%s)', $objectName, $journalData['currency_name']); $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journalData['currency_symbol'], 'currency_code' => $journalData['currency_code'], ]; $result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']); } } } $data = $this->generator->multiCurrencyPieChart($result); return response()->json($data); } 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) { foreach ($category['transaction_journals'] as $journal) { $journalData = $this->resolveJournalAmountAndCurrency($journal, $currency); $title = sprintf('%s (%s)', $category['name'], $journalData['currency_name']); $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journalData['currency_symbol'], 'currency_code' => $journalData['currency_code'], ]; $result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']); } } } $data = $this->generator->multiCurrencyPieChart($result); return response()->json($data); } 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) { foreach ($category['transaction_journals'] as $journal) { $journalData = $this->resolveJournalAmountAndCurrency($journal, $currency); $title = sprintf('%s (%s)', $category['name'], $journalData['currency_name']); $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journalData['currency_symbol'], 'currency_code' => $journalData['currency_code'], ]; $result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']); } } } $data = $this->generator->multiCurrencyPieChart($result); return response()->json($data); } 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) { $journalData = $this->resolveJournalAmountAndCurrency($journal, $currency); $objectName = $journal['destination_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $journalData['currency_name']); $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journalData['currency_symbol'], 'currency_code' => $journalData['currency_code'], ]; $result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']); } } } $data = $this->generator->multiCurrencyPieChart($result); return response()->json($data); } 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) { $journalData = $this->resolveJournalAmountAndCurrency($journal, $currency); $objectName = $journal['destination_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $journalData['currency_name']); $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journalData['currency_symbol'], 'currency_code' => $journalData['currency_code'], ]; $result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']); } } } $data = $this->generator->multiCurrencyPieChart($result); return response()->json($data); } public function mainChart(Collection $accounts, Category $category, Carbon $start, Carbon $end): JsonResponse { $chartData = []; $spent = $this->opsRepository->listExpenses($start, $end, $accounts, new Collection()->push($category)); $earned = $this->opsRepository->listIncome($start, $end, $accounts, new Collection()->push($category)); $format = Navigation::preferredCarbonLocalizedFormat($start, $end); // loop expenses. foreach ($spent as $currency) { // add things to chart Data for each currency: foreach ($currency['categories'] as $currentCategory) { foreach ($currentCategory['transaction_journals'] as $journal) { $journalData = $this->resolveJournalAmountAndCurrency($journal, $currency); $spentKey = sprintf('%d-spent', $journalData['currency_id']); $chartData[$spentKey] ??= [ 'label' => sprintf( '%s (%s)', (string) trans('firefly.spent_in_specific_category', ['category' => $category->name]), $journalData['currency_name'] ), 'type' => 'bar', 'currency_symbol' => $journalData['currency_symbol'], 'currency_code' => $journalData['currency_code'], 'currency_id' => $journalData['currency_id'], 'entries' => $this->makeEntries($start, $end), ]; $key = $journal['date']->isoFormat($format); $chartData[$spentKey]['entries'][$key] ??= '0'; $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $journalData['amount']); } } } // loop income. foreach ($earned as $currency) { // add things to chart Data for each currency: foreach ($currency['categories'] as $currentCategory) { foreach ($currentCategory['transaction_journals'] as $journal) { $journalData = $this->resolveJournalAmountAndCurrency($journal, $currency); $spentKey = sprintf('%d-earned', $journalData['currency_id']); $chartData[$spentKey] ??= [ 'label' => sprintf( '%s (%s)', (string) trans('firefly.earned_in_specific_category', ['category' => $category->name]), $journalData['currency_name'] ), 'type' => 'bar', 'currency_symbol' => $journalData['currency_symbol'], 'currency_code' => $journalData['currency_code'], 'currency_id' => $journalData['currency_id'], 'entries' => $this->makeEntries($start, $end), ]; $key = $journal['date']->isoFormat($format); $chartData[$spentKey]['entries'][$key] ??= '0'; $chartData[$spentKey]['entries'][$key] = bcadd($chartData[$spentKey]['entries'][$key], $journalData['amount']); } } } $data = $this->generator->multiSet($chartData); return response()->json($data); } 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) { $journalData = $this->resolveJournalAmountAndCurrency($journal, $currency); $objectName = $journal['source_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $journalData['currency_name']); $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journalData['currency_symbol'], 'currency_code' => $journalData['currency_code'], ]; $result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']); } } } $data = $this->generator->multiCurrencyPieChart($result); return response()->json($data); } 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) { $journalData = $this->resolveJournalAmountAndCurrency($journal, $currency); $objectName = $journal['source_account_name'] ?? trans('firefly.empty'); $title = sprintf('%s (%s)', $objectName, $journalData['currency_name']); $result[$title] ??= [ 'amount' => '0', 'currency_symbol' => $journalData['currency_symbol'], 'currency_code' => $journalData['currency_code'], ]; $result[$title]['amount'] = bcadd($result[$title]['amount'], $journalData['amount']); } } } $data = $this->generator->multiCurrencyPieChart($result); return response()->json($data); } /** * TODO duplicate function */ private function makeEntries(Carbon $start, Carbon $end): array { $return = []; $format = Navigation::preferredCarbonLocalizedFormat($start, $end); $preferredRange = Navigation::preferredRangeFormat($start, $end); $currentStart = clone $start; while ($currentStart <= $end) { $currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange); $key = $currentStart->isoFormat($format); $return[$key] = '0'; $currentStart = clone $currentEnd; $currentStart->addDay()->startOfDay(); } return $return; } }