Optimize balance collection.

This commit is contained in:
James Cole
2025-08-10 12:12:30 +02:00
parent 9f394e92fe
commit c567474043
2 changed files with 96 additions and 92 deletions

View File

@@ -118,7 +118,7 @@ class AccountController extends Controller
Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary); $startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary); $endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
Log::debug('Done collecting balances');
// loop the accounts, then check for balance and currency info. // loop the accounts, then check for balance and currency info.
foreach ($accounts as $account) { foreach ($accounts as $account) {
// Log::debug(sprintf('[a] Now in account #%d ("%s")', $account->id, $account->name)); // Log::debug(sprintf('[a] Now in account #%d ("%s")', $account->id, $account->name));

View File

@@ -38,7 +38,6 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use ValueError; use ValueError;
use function Safe\parse_url; use function Safe\parse_url;
use function Safe\preg_replace; use function Safe\preg_replace;
@@ -66,10 +65,10 @@ class Steam
// Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); // Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision));
if (str_contains($number, '.')) { if (str_contains($number, '.')) {
if ('-' !== $number[0]) { if ('-' !== $number[0]) {
return bcadd($number, '0.'.str_repeat('0', $precision).'5', $precision); return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision);
} }
return bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision); return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
} }
return $number; return $number;
@@ -266,8 +265,7 @@ class Steam
'transactions.transaction_currency_id', 'transactions.transaction_currency_id',
DB::raw('SUM(transactions.amount) AS sum_of_day'), DB::raw('SUM(transactions.amount) AS sum_of_day'),
] ]
) );
;
$currentBalance = $startBalance; $currentBalance = $startBalance;
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
@@ -322,45 +320,52 @@ class Steam
public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array
{ {
Log::debug(sprintf('accountsBalancesOptimized: Called with date/time "%s"', $date->toIso8601String())); Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String()));
$result = []; $result = [];
$convertToPrimary ??= Amount::convertToPrimary(); $convertToPrimary ??= Amount::convertToPrimary();
$primary ??= Amount::getPrimaryCurrency(); $primary ??= Amount::getPrimaryCurrency();
$currencies = $this->getCurrencies($accounts); $currencies = $this->getCurrencies($accounts);
// balance(s) in all currencies for ALL accounts. // balance(s) in all currencies for ALL accounts.
$array = Transaction::whereIn('account_id', $accounts->pluck('id')->toArray()) $arrayOfSums = Transaction::whereIn('account_id', $accounts->pluck('id')->toArray())
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->get(['transactions.account_id', 'transaction_currencies.code', 'transactions.amount'])->toArray() ->groupBy(['transactions.account_id', 'transaction_currencies.code'])
; ->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->toArray();
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
// filter array back to this account:
$filtered = array_filter($array, function ($item) use ($account) {
return (int)$item['account_id'] === $account->id;
});
$currency = $currencies[$account->id];
// this array is PER account, so we wait a bit before we change code here. // this array is PER account, so we wait a bit before we change code here.
$return = [ $return = [
'pc_balance' => '0', 'pc_balance' => '0',
'balance' => '0', // this key is overwritten right away, but I must remember it is always created. 'balance' => '0', // this key is overwritten right away, but I must remember it is always created.
]; ];
$currency = $currencies[$account->id];
// second array
$accountSum = array_filter($arrayOfSums, function ($entry) use ($account) {
return $entry['account_id'] === $account->id;
});
if(0 === count($accountSum)) {
$result[$account->id] = $return;
continue;
}
$accountSum = array_values($accountSum)[0];
$sumsByCode = [
$accountSum['code'] => $accountSum['sum_of_amount'],
];
// balance(s) in all currencies.
$others = $this->groupAndSumTransactions($filtered, 'code', 'amount');
// Log::debug('All balances are (joined)', $others); // Log::debug('All balances are (joined)', $others);
// if there is no request to convert, take this as "balance" and "pc_balance". // if there is no request to convert, take this as "balance" and "pc_balance".
$return['balance'] = $others[$currency->code] ?? '0'; $return['balance'] = $sumsByCode[$currency->code] ?? '0';
if (!$convertToPrimary) { if (!$convertToPrimary) {
unset($return['pc_balance']); unset($return['pc_balance']);
// Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance'])); // Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance']));
} }
// if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency. // if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency.
if ($convertToPrimary) { if ($convertToPrimary) {
$return['pc_balance'] = $this->convertAllBalances($others, $primary, $date); // todo sum all and convert. $return['pc_balance'] = $this->convertAllBalances($sumsByCode, $primary, $date);
// Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance'])); // Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance']));
} }
@@ -379,7 +384,7 @@ class Steam
$return['balance'] = bcadd($return['balance'], $virtualBalance); $return['balance'] = bcadd($return['balance'], $virtualBalance);
// Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance'])); // Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance']));
} }
$final = array_merge($return, $others); $final = array_merge($return, $sumsByCode);
$result[$account->id] = $final; $result[$account->id] = $final;
// Log::debug('Final balance is', $final); // Log::debug('Final balance is', $final);
} }
@@ -438,8 +443,7 @@ class Steam
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->get(['transaction_currencies.code', 'transactions.amount'])->toArray() ->get(['transaction_currencies.code', 'transactions.amount'])->toArray();
;
$others = $this->groupAndSumTransactions($array, 'code', 'amount'); $others = $this->groupAndSumTransactions($array, 'code', 'amount');
// Log::debug('All balances are (joined)', $others); // Log::debug('All balances are (joined)', $others);
// if there is no request to convert, take this as "balance" and "pc_balance". // if there is no request to convert, take this as "balance" and "pc_balance".