Optimize available budgets.

This commit is contained in:
James Cole
2025-08-03 07:53:36 +02:00
parent 3233ca4a4c
commit 716d72d8af
7 changed files with 65 additions and 73 deletions

View File

@@ -89,8 +89,6 @@ class ShowController extends Controller
$admin = auth()->user(); $admin = auth()->user();
$enrichment = new AvailableBudgetEnrichment(); $enrichment = new AvailableBudgetEnrichment();
$enrichment->setUser($admin); $enrichment->setUser($admin);
$enrichment->setStart($start);
$enrichment->setEnd($end);
$availableBudgets = $enrichment->enrich($availableBudgets); $availableBudgets = $enrichment->enrich($availableBudgets);
// make paginator: // make paginator:

View File

@@ -32,6 +32,7 @@ use FireflyIII\Support\Report\Summarizer\TransactionSummarizer;
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface; use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait; use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/** /**
* Class NoBudgetRepository * Class NoBudgetRepository
@@ -98,4 +99,22 @@ class NoBudgetRepository implements NoBudgetRepositoryInterface, UserGroupInterf
return $summarizer->groupByCurrencyId($journals); return $summarizer->groupByCurrencyId($journals);
} }
#[\Override] public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array
{
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
if ($accounts instanceof Collection && $accounts->count() > 0) {
$collector->setAccounts($accounts);
}
if ($currency instanceof TransactionCurrency) {
$collector->setCurrency($currency);
}
$collector->withoutBudget();
$collector->withBudgetInformation();
return $collector->getExtractedJournals();
}
} }

View File

@@ -49,4 +49,6 @@ interface NoBudgetRepositoryInterface
public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array; public function getNoBudgetPeriodReport(Collection $accounts, Carbon $start, Carbon $end): array;
public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array; public function sumExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array;
public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array;
} }

View File

@@ -283,16 +283,15 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
return $summarizer->groupByCurrencyId($journals, 'negative', false); return $summarizer->groupByCurrencyId($journals, 'negative', false);
} }
public function sumCollectedExpenses(array $expenses, Carbon $start, Carbon $end, bool $convertToPrimary = false): array public function sumCollectedExpenses(array $expenses, Carbon $start, Carbon $end, TransactionCurrency $transactionCurrency, bool $convertToPrimary = false): array
{ {
Log::debug(sprintf('Start of %s.', __METHOD__)); Log::debug(sprintf('Start of %s.', __METHOD__));
$summarizer = new TransactionSummarizer($this->user); $summarizer = new TransactionSummarizer($this->user);
// 2025-04-21 overrule "convertToPrimary" because in this particular view, we never want to do this.
$summarizer->setConvertToPrimary($convertToPrimary); $summarizer->setConvertToPrimary($convertToPrimary);
// filter $journals by range. // filter $journals by range.
$expenses = array_filter($expenses, static function (array $expense) use ($start, $end): bool { $expenses = array_filter($expenses, static function (array $expense) use ($start, $end, $transactionCurrency): bool {
return $expense['date']->between($start, $end); return $expense['date']->between($start, $end) && $expense['currency_id'] === $transactionCurrency->id;
}); });
return $summarizer->groupByCurrencyId($expenses, 'negative', false); return $summarizer->groupByCurrencyId($expenses, 'negative', false);

View File

@@ -74,7 +74,7 @@ interface OperationsRepositoryInterface
bool $convertToPrimary = false bool $convertToPrimary = false
): array; ): array;
public function sumCollectedExpenses(array $expenses, Carbon $start, Carbon $end, bool $convertToPrimary = false): array; public function sumCollectedExpenses(array $expenses, Carbon $start, Carbon $end, TransactionCurrency $transactionCurrency, bool $convertToPrimary = false): array;
public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null): array; public function collectExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $budgets = null, ?TransactionCurrency $currency = null): array;
} }

View File

@@ -38,16 +38,16 @@ use Illuminate\Support\Facades\Log;
class AvailableBudgetEnrichment implements EnrichmentInterface class AvailableBudgetEnrichment implements EnrichmentInterface
{ {
private User $user; private User $user;
private UserGroup $userGroup; private UserGroup $userGroup;
private TransactionCurrency $primaryCurrency; private TransactionCurrency $primaryCurrency;
private bool $convertToPrimary = false; private bool $convertToPrimary = false;
private array $ids = []; private array $ids = [];
private Collection $collection; private Collection $collection;
private array $spentInBudgets = []; private array $spentInBudgets = [];
private array $spentOutsideBudgets = []; private array $spentOutsideBudgets = [];
private array $pcSpentInBudgets = []; private array $pcSpentInBudgets = [];
private array $pcSpentOutsideBudgets = []; private array $pcSpentOutsideBudgets = [];
private readonly NoBudgetRepositoryInterface $noBudgetRepository; private readonly NoBudgetRepositoryInterface $noBudgetRepository;
private readonly OperationsRepositoryInterface $opsRepository; private readonly OperationsRepositoryInterface $opsRepository;
private readonly BudgetRepositoryInterface $repository; private readonly BudgetRepositoryInterface $repository;
@@ -58,8 +58,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
public function __construct() public function __construct()
{ {
$this->primaryCurrency = Amount::getPrimaryCurrency(); $this->primaryCurrency = Amount::getPrimaryCurrency();
$this->convertToPrimary = Amount::convertToPrimary(); $this->convertToPrimary = Amount::convertToPrimary();
$this->noBudgetRepository = app(NoBudgetRepositoryInterface::class); $this->noBudgetRepository = app(NoBudgetRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class); $this->opsRepository = app(OperationsRepositoryInterface::class);
$this->repository = app(BudgetRepositoryInterface::class); $this->repository = app(BudgetRepositoryInterface::class);
@@ -86,7 +86,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
#[\Override] public function setUser(User $user): void #[\Override] public function setUser(User $user): void
{ {
$this->user = $user; $this->user = $user;
$this->setUserGroup($user->userGroup); $this->setUserGroup($user->userGroup);
} }
@@ -107,25 +107,28 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
$this->ids = array_unique($this->ids); $this->ids = array_unique($this->ids);
} }
public function setStart(?Carbon $start): void private function collectSpentInfo(): void
{ {
$this->start = $start; $start = $this->collection->min('start_date');
} $end = $this->collection->max('end_date');
$allActive = $this->repository->getActiveBudgets();
$spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null);
$spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end, null, null, null);
foreach ($this->collection as $availableBudget) {
$id = (int) $availableBudget->id;
$filteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false);
$filteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false);
$this->spentInBudgets[$id] = array_values($filteredSpentInBudgets);
$this->spentOutsideBudgets[$id] = array_values($filteredSpentOutsideBudgets);
if (true === $this->convertToPrimary) {
$pcFilteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, true);
$pcFilteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, true);
$this->pcSpentInBudgets[$id] = array_values($pcFilteredSpentInBudgets);
$this->pcSpentOutsideBudgets[$id] = array_values($pcFilteredSpentOutsideBudgets);
}
public function setEnd(?Carbon $end): void
{
$this->end = $end;
}
private function collectSpentInfo(): void {
$start = $this->collection->min('start_date');
$end = $this->collection->max('end_date');
$allActive = $this->repository->getActiveBudgets();
$spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null);
foreach($this->collection as $availableBudget) {
$filteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $this->convertToPrimary);
$id = (int) $availableBudget->id;
$this->spentInBudgets[$id] = array_values($filteredSpentInBudgets);
// filter arrays on date. // filter arrays on date.
// send them to sumCollection thing. // send them to sumCollection thing.
// save. // save.
@@ -144,9 +147,10 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
$id = (int) $item->id; $id = (int) $item->id;
$meta = [ $meta = [
'spent_in_budgets' => $spentInsideBudgets[$id] ?? [], 'spent_in_budgets' => $spentInsideBudgets[$id] ?? [],
'spent_outside_budgets' => $spentOutsideBudgets[$id] ?? [],
'pc_spent_in_budgets' => $pcSpentInBudgets[$id] ?? [], 'pc_spent_in_budgets' => $pcSpentInBudgets[$id] ?? [],
'pc_spent_outside_budgets' => $pcSpentOutsideBudgets ?? [],
'spent_outside_budgets' => $spentOutsideBudgets[$id] ?? [],
'pc_spent_outside_budgets' => $pcSpentOutsideBudgets[$id] ?? [],
]; ];
$item->meta = $meta; $item->meta = $meta;
return $item; return $item;

View File

@@ -26,9 +26,6 @@ namespace FireflyIII\Transformers;
use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\NoBudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\Amount;
/** /**
@@ -36,22 +33,16 @@ use FireflyIII\Support\Facades\Amount;
*/ */
class AvailableBudgetTransformer extends AbstractTransformer class AvailableBudgetTransformer extends AbstractTransformer
{ {
private readonly bool $convertToPrimary; private readonly bool $convertToPrimary;
private readonly TransactionCurrency $primary; private readonly TransactionCurrency $primary;
private readonly NoBudgetRepositoryInterface $noBudgetRepository;
private readonly OperationsRepositoryInterface $opsRepository;
private readonly BudgetRepositoryInterface $repository;
/** /**
* CurrencyTransformer constructor. * CurrencyTransformer constructor.
*/ */
public function __construct() public function __construct()
{ {
$this->repository = app(BudgetRepositoryInterface::class); $this->primary = Amount::getPrimaryCurrency();
$this->opsRepository = app(OperationsRepositoryInterface::class); $this->convertToPrimary = Amount::convertToPrimary();
$this->noBudgetRepository = app(NoBudgetRepositoryInterface::class);
$this->primary = Amount::getPrimaryCurrency();
$this->convertToPrimary = Amount::convertToPrimary();
} }
/** /**
@@ -59,7 +50,6 @@ class AvailableBudgetTransformer extends AbstractTransformer
*/ */
public function transform(AvailableBudget $availableBudget): array public function transform(AvailableBudget $availableBudget): array
{ {
$this->repository->setUser($availableBudget->user);
$currency = $availableBudget->transactionCurrency; $currency = $availableBudget->transactionCurrency;
$amount = app('steam')->bcround($availableBudget->amount, $currency->decimal_places); $amount = app('steam')->bcround($availableBudget->amount, $currency->decimal_places);
$pcAmount = null; $pcAmount = null;
@@ -68,7 +58,7 @@ class AvailableBudgetTransformer extends AbstractTransformer
$pcAmount = app('steam')->bcround($availableBudget->native_amount, $this->primary->decimal_places); $pcAmount = app('steam')->bcround($availableBudget->native_amount, $this->primary->decimal_places);
} }
$data = [ $data = [
'id' => (string) $availableBudget->id, 'id' => (string) $availableBudget->id,
'created_at' => $availableBudget->created_at->toAtomString(), 'created_at' => $availableBudget->created_at->toAtomString(),
'updated_at' => $availableBudget->updated_at->toAtomString(), 'updated_at' => $availableBudget->updated_at->toAtomString(),
@@ -100,28 +90,8 @@ class AvailableBudgetTransformer extends AbstractTransformer
], ],
], ],
]; ];
$start = $this->parameters->get('start');
$end = $this->parameters->get('end');
if (null !== $start && null !== $end) {
$data['old_spent_in_budgets'] = $this->getSpentInBudgets();
$data['old_spent_no_budget'] = $this->spentOutsideBudgets();
}
return $data; return $data;
} }
private function getSpentInBudgets(): array
{
$allActive = $this->repository->getActiveBudgets();
$sums = $this->opsRepository->sumExpenses($this->parameters->get('start'), $this->parameters->get('end'), null, $allActive);
return array_values($sums);
}
private function spentOutsideBudgets(): array
{
$sums = $this->noBudgetRepository->sumExpenses($this->parameters->get('start'), $this->parameters->get('end'));
return array_values($sums);
}
} }