. */ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers\Chart; use Carbon\Carbon; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Data\DateRequest; use FireflyIII\Api\V1\Requests\Data\SameDateRequest; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\UserRoleEnum; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Http\Api\CleansChartData; use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Http\Api\ValidatesUserGroupTrait; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; /** * Class BudgetController */ class CategoryController extends Controller { use CleansChartData; use ValidatesUserGroupTrait; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; private AccountRepositoryInterface $accountRepos; private CurrencyRepositoryInterface $currencyRepos; public function __construct() { parent::__construct(); $this->middleware( function ($request, $next) { $this->validateUserGroup($request); $this->accountRepos = app(AccountRepositoryInterface::class); $this->currencyRepos = app(CurrencyRepositoryInterface::class); $this->accountRepos->setUserGroup($this->userGroup); $this->currencyRepos->setUserGroup($this->userGroup); $this->accountRepos->setUser($this->user); $this->currencyRepos->setUser($this->user); return $next($request); } ); } /** * TODO may be worth to move to a handler but the data is simple enough. * TODO see autoComplete/account controller * * @throws FireflyException * * @SuppressWarnings("PHPMD.UnusedFormalParameter") */ public function overview(SameDateRequest $request): JsonResponse { /** @var Carbon $start */ $start = $this->parameters->get('start'); /** @var Carbon $end */ $end = $this->parameters->get('end'); $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value]); $currencies = []; $return = []; $converter = new ExchangeRateConverter(); // get journals for entire period: /** @var GroupCollectorInterface $collector */ $collector = app(GroupCollectorInterface::class); $collector->setRange($start, $end)->withAccountInformation(); $collector->setXorAccounts($accounts)->withCategoryInformation(); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::RECONCILIATION->value]); $journals = $collector->getExtractedJournals(); /** @var array $journal */ foreach ($journals as $journal) { // find journal: $journalCurrencyId = (int)$journal['currency_id']; $currency = $currencies[$journalCurrencyId] ?? $this->currencyRepos->find($journalCurrencyId); $currencies[$journalCurrencyId] = $currency; $currencyId = (int)$currency->id; $currencyName = (string)$currency->name; $currencyCode = (string)$currency->code; $currencySymbol = (string)$currency->symbol; $currencyDecimalPlaces = (int)$currency->decimal_places; $amount = Steam::positive($journal['amount']); $pcAmount = null; // overrule if necessary: if ($this->convertToPrimary && $journalCurrencyId === $this->primaryCurrency->id) { $pcAmount = $amount; } if ($this->convertToPrimary && $journalCurrencyId !== $this->primaryCurrency->id) { $currencyId = (int)$this->primaryCurrency->id; $currencyName = (string)$this->primaryCurrency->name; $currencyCode = (string)$this->primaryCurrency->code; $currencySymbol = (string)$this->primaryCurrency->symbol; $currencyDecimalPlaces = (int)$this->primaryCurrency->decimal_places; $pcAmount = $converter->convert($currency, $this->primaryCurrency, $journal['date'], $amount); Log::debug(sprintf('Converted %s %s to %s %s', $journal['currency_code'], $amount, $this->primaryCurrency->code, $pcAmount)); } $categoryName = $journal['category_name'] ?? (string)trans('firefly.no_category'); $key = sprintf('%s-%s', $categoryName, $currencyCode); // create arrays $return[$key] ??= [ 'label' => $categoryName, 'currency_id' => (string)$currencyId, 'currency_name' => $currencyName, 'currency_code' => $currencyCode, 'currency_symbol' => $currencySymbol, 'currency_decimal_places' => $currencyDecimalPlaces, 'primary_currency_id' => (string)$this->primaryCurrency->id, 'primary_currency_name' => (string)$this->primaryCurrency->name, 'primary_currency_code' => (string)$this->primaryCurrency->code, 'primary_currency_symbol' => (string)$this->primaryCurrency->symbol, 'primary_currency_decimal_places' => (int)$this->primaryCurrency->decimal_places, 'period' => null, 'start_date' => $start->toAtomString(), 'end_date' => $end->toAtomString(), 'yAxisID' => 0, 'type' => 'bar', 'entries' => [ 'spent' => '0', ], 'pc_entries' => [ 'spent' => '0', ], ]; // add monies $return[$key]['entries']['spent'] = bcadd($return[$key]['entries']['spent'], (string)$amount); if (null !== $pcAmount) { $return[$key]['pc_entries']['spent'] = bcadd($return[$key]['pc_entries']['spent'], (string)$pcAmount); } } $return = array_values($return); // order by amount usort($return, static fn (array $a, array $b) => (float)$a['entries']['spent'] < (float)$b['entries']['spent'] ? 1 : -1); return response()->json($this->clean($return)); } }