Optimize available budgets.

This commit is contained in:
James Cole
2025-08-06 20:23:58 +02:00
parent 70071767ab
commit 0e8f608e00
4 changed files with 103 additions and 83 deletions

View File

@@ -123,8 +123,8 @@ class AccountEnrichment implements EnrichmentInterface
{ {
/** @var Account $account */ /** @var Account $account */
foreach ($this->collection as $account) { foreach ($this->collection as $account) {
$this->ids[] = (int) $account->id; $this->ids[] = (int)$account->id;
$this->accountTypeIds[] = (int) $account->account_type_id; $this->accountTypeIds[] = (int)$account->account_type_id;
} }
$this->ids = array_unique($this->ids); $this->ids = array_unique($this->ids);
$this->accountTypeIds = array_unique($this->accountTypeIds); $this->accountTypeIds = array_unique($this->accountTypeIds);
@@ -136,7 +136,7 @@ class AccountEnrichment implements EnrichmentInterface
/** @var AccountType $type */ /** @var AccountType $type */
foreach ($types as $type) { foreach ($types as $type) {
$this->accountTypes[(int) $type->id] = $type->type; $this->accountTypes[(int)$type->id] = $type->type;
} }
} }
@@ -144,19 +144,20 @@ class AccountEnrichment implements EnrichmentInterface
{ {
$set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt']) $set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt'])
->whereIn('account_id', $this->ids) ->whereIn('account_id', $this->ids)
->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray() ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray();
;
/** @var array $entry */ /** @var array $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
$this->meta[(int) $entry['account_id']][$entry['name']] = (string) $entry['data']; $this->meta[(int)$entry['account_id']][$entry['name']] = (string)$entry['data'];
if ('currency_id' === $entry['name']) { if ('currency_id' === $entry['name']) {
$this->currencies[(int) $entry['data']] = true; $this->currencies[(int)$entry['data']] = true;
} }
} }
if (count($this->currencies) > 0) {
$currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get(); $currencies = TransactionCurrency::whereIn('id', array_keys($this->currencies))->get();
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
$this->currencies[(int) $currency->id] = $currency; $this->currencies[(int)$currency->id] = $currency;
}
} }
$this->currencies[0] = $this->primaryCurrency; $this->currencies[0] = $this->primaryCurrency;
foreach ($this->currencies as $id => $currency) { foreach ($this->currencies as $id => $currency) {
@@ -171,10 +172,9 @@ class AccountEnrichment implements EnrichmentInterface
$notes = Note::query()->whereIn('noteable_id', $this->ids) $notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray() ->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray();
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int) $note['noteable_id']] = (string) $note['text']; $this->notes[(int)$note['noteable_id']] = (string)$note['text'];
} }
Log::debug(sprintf('Enrich with %d note(s)', count($this->notes))); Log::debug(sprintf('Enrich with %d note(s)', count($this->notes)));
} }
@@ -182,14 +182,13 @@ class AccountEnrichment implements EnrichmentInterface
private function collectLocations(): void private function collectLocations(): void
{ {
$locations = Location::query()->whereIn('locatable_id', $this->ids) $locations = Location::query()->whereIn('locatable_id', $this->ids)
->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray() ->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray();
;
foreach ($locations as $location) { foreach ($locations as $location) {
$this->locations[(int) $location['locatable_id']] $this->locations[(int)$location['locatable_id']]
= [ = [
'latitude' => (float) $location['latitude'], 'latitude' => (float)$location['latitude'],
'longitude' => (float) $location['longitude'], 'longitude' => (float)$location['longitude'],
'zoom_level' => (int) $location['zoom_level'], 'zoom_level' => (int)$location['zoom_level'],
]; ];
} }
Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations)));
@@ -205,16 +204,15 @@ class AccountEnrichment implements EnrichmentInterface
->setUserGroup($this->userGroup) ->setUserGroup($this->userGroup)
->setAccounts($this->collection) ->setAccounts($this->collection)
->withAccountInformation() ->withAccountInformation()
->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]) ->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]);
;
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
foreach ($journals as $journal) { foreach ($journals as $journal) {
$this->openingBalances[(int) $journal['source_account_id']] $this->openingBalances[(int)$journal['source_account_id']]
= [ = [
'amount' => Steam::negative($journal['amount']), 'amount' => Steam::negative($journal['amount']),
'date' => $journal['date'], 'date' => $journal['date'],
]; ];
$this->openingBalances[(int) $journal['destination_account_id']] $this->openingBalances[(int)$journal['destination_account_id']]
= [ = [
'amount' => Steam::positive($journal['amount']), 'amount' => Steam::positive($journal['amount']),
'date' => $journal['date'], 'date' => $journal['date'],
@@ -236,8 +234,8 @@ class AccountEnrichment implements EnrichmentInterface
private function appendCollectedData(): void private function appendCollectedData(): void
{ {
$this->collection = $this->collection->map(function (Account $item) { $this->collection = $this->collection->map(function (Account $item) {
$id = (int) $item->id; $id = (int)$item->id;
$item->full_account_type = $this->accountTypes[(int) $item->account_type_id] ?? null; $item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null;
$meta = [ $meta = [
'currency' => null, 'currency' => null,
'location' => [ 'location' => [
@@ -247,7 +245,7 @@ class AccountEnrichment implements EnrichmentInterface
], ],
'opening_balance_date' => null, 'opening_balance_date' => null,
'opening_balance_amount' => null, 'opening_balance_amount' => null,
'account_number'=> null, 'account_number' => null,
'notes' => $notes[$id] ?? null, 'notes' => $notes[$id] ?? null,
'last_activity' => $this->lastActivities[$id] ?? null, 'last_activity' => $this->lastActivities[$id] ?? null,
]; ];
@@ -263,7 +261,7 @@ class AccountEnrichment implements EnrichmentInterface
} }
// also add currency, if present. // also add currency, if present.
if (array_key_exists('currency_id', $meta)) { if (array_key_exists('currency_id', $meta)) {
$currencyId = (int) $meta['currency_id']; $currencyId = (int)$meta['currency_id'];
$meta['currency'] = $this->currencies[$currencyId]; $meta['currency'] = $this->currencies[$currencyId];
} }
@@ -340,7 +338,8 @@ class AccountEnrichment implements EnrichmentInterface
$this->lastActivities = Steam::getLastActivities($this->ids); $this->lastActivities = Steam::getLastActivities($this->ids);
} }
private function collectBalances(): void { private function collectBalances(): void
{
$this->balances = Steam::finalAccountsBalanceOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary); $this->balances = Steam::finalAccountsBalanceOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary);
} }

View File

@@ -45,6 +45,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
private TransactionCurrency $primaryCurrency; private TransactionCurrency $primaryCurrency;
private bool $convertToPrimary = false; private bool $convertToPrimary = false;
private array $ids = []; private array $ids = [];
private array $currencyIds = [];
private array $currencies = [];
private Collection $collection; private Collection $collection;
private array $spentInBudgets = []; private array $spentInBudgets = [];
private array $spentOutsideBudgets = []; private array $spentOutsideBudgets = [];
@@ -72,6 +74,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
{ {
$this->collection = $collection; $this->collection = $collection;
$this->collectIds(); $this->collectIds();
$this->collectCurrencies();
$this->collectSpentInfo(); $this->collectSpentInfo();
$this->appendCollectedData(); $this->appendCollectedData();
@@ -108,7 +111,8 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
{ {
/** @var AvailableBudget $availableBudget */ /** @var AvailableBudget $availableBudget */
foreach ($this->collection as $availableBudget) { foreach ($this->collection as $availableBudget) {
$this->ids[] = (int) $availableBudget->id; $this->ids[] = (int)$availableBudget->id;
$this->currencyIds[(int)$availableBudget->id] = (int)$availableBudget->transaction_currency_id;
} }
$this->ids = array_unique($this->ids); $this->ids = array_unique($this->ids);
} }
@@ -121,15 +125,17 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
$spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null); $spentInBudgets = $this->opsRepository->collectExpenses($start, $end, null, $allActive, null);
$spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end, null, null, null); $spentOutsideBudgets = $this->noBudgetRepository->collectExpenses($start, $end, null, null, null);
foreach ($this->collection as $availableBudget) { foreach ($this->collection as $availableBudget) {
$id = (int) $availableBudget->id; $id = (int)$availableBudget->id;
$filteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false); $currencyId = $this->currencyIds[$id];
$filteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, false); $currency = $this->currencies[$currencyId];
$filteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $currency, false);
$filteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $currency, false);
$this->spentInBudgets[$id] = array_values($filteredSpentInBudgets); $this->spentInBudgets[$id] = array_values($filteredSpentInBudgets);
$this->spentOutsideBudgets[$id] = array_values($filteredSpentOutsideBudgets); $this->spentOutsideBudgets[$id] = array_values($filteredSpentOutsideBudgets);
if (true === $this->convertToPrimary) { if (true === $this->convertToPrimary) {
$pcFilteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, true); $pcFilteredSpentInBudgets = $this->opsRepository->sumCollectedExpenses($spentInBudgets, $availableBudget->start_date, $availableBudget->end_date, $currency, true);
$pcFilteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $availableBudget->transactionCurrency, true); $pcFilteredSpentOutsideBudgets = $this->opsRepository->sumCollectedExpenses($spentOutsideBudgets, $availableBudget->start_date, $availableBudget->end_date, $currency, true);
$this->pcSpentInBudgets[$id] = array_values($pcFilteredSpentInBudgets); $this->pcSpentInBudgets[$id] = array_values($pcFilteredSpentInBudgets);
$this->pcSpentOutsideBudgets[$id] = array_values($pcFilteredSpentOutsideBudgets); $this->pcSpentOutsideBudgets[$id] = array_values($pcFilteredSpentOutsideBudgets);
} }
@@ -145,22 +151,29 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
private function appendCollectedData(): void private function appendCollectedData(): void
{ {
$spentInsideBudgets = $this->spentInBudgets; $this->collection = $this->collection->map(function (AvailableBudget $item) {
$spentOutsideBudgets = $this->spentOutsideBudgets; $id = (int)$item->id;
$pcSpentInBudgets = $this->pcSpentInBudgets; $currencyId = $this->currencyIds[$id];
$pcSpentOutsideBudgets = $this->pcSpentOutsideBudgets; $currency = $this->currencies[$currencyId];
$this->collection = $this->collection->map(function (AvailableBudget $item) use ($spentInsideBudgets, $spentOutsideBudgets, $pcSpentInBudgets, $pcSpentOutsideBudgets) {
$id = (int) $item->id;
$meta = [ $meta = [
'spent_in_budgets' => $spentInsideBudgets[$id] ?? [], 'currency' => $currency,
'pc_spent_in_budgets' => $pcSpentInBudgets[$id] ?? [], 'spent_in_budgets' => $this->spentInsideBudgets[$id] ?? [],
'pc_spent_in_budgets' => $this->pcSpentInBudgets[$id] ?? [],
'spent_outside_budgets' => $spentOutsideBudgets[$id] ?? [], 'spent_outside_budgets' => $this->spentOutsideBudgets[$id] ?? [],
'pc_spent_outside_budgets' => $pcSpentOutsideBudgets[$id] ?? [], 'pc_spent_outside_budgets' => $this->pcSpentOutsideBudgets[$id] ?? [],
]; ];
$item->meta = $meta; $item->meta = $meta;
return $item; return $item;
}); });
} }
private function collectCurrencies(): void
{
$ids = array_unique(array_values($this->currencyIds));
$set = TransactionCurrency::whereIn('id', $ids)->get();
foreach ($set as $currency) {
$this->currencies[(int)$currency->id] = $currency;
}
}
} }

View File

@@ -51,7 +51,7 @@ class AvailableBudgetTransformer extends AbstractTransformer
*/ */
public function transform(AvailableBudget $availableBudget): array public function transform(AvailableBudget $availableBudget): array
{ {
$currency = $availableBudget->transactionCurrency; $currency = $availableBudget->meta['currency'];
$amount = Steam::bcround($availableBudget->amount, $currency->decimal_places); $amount = Steam::bcround($availableBudget->amount, $currency->decimal_places);
$pcAmount = null; $pcAmount = null;

View File

@@ -26,6 +26,14 @@
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'), 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
}, },
}); });
$.ajax({
url: 'api/v1/available-budgets?size=50&date=2025-08-06',
type: 'GET',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
},
});
}); });
</script> </script>