Clean up balance methods.

This commit is contained in:
James Cole
2024-12-22 20:32:58 +01:00
parent a0e92b6969
commit d90ac519f7
93 changed files with 1233 additions and 1853 deletions

View File

@@ -64,7 +64,7 @@ class AccountBalanceGrouped
/** @var array $currency */
foreach ($this->data as $currency) {
// income and expense array prepped:
$income = [
$income = [
'label' => 'earned',
'currency_id' => (string) $currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
@@ -81,7 +81,7 @@ class AccountBalanceGrouped
'entries' => [],
'native_entries' => [],
];
$expense = [
$expense = [
'label' => 'spent',
'currency_id' => (string) $currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
@@ -101,22 +101,22 @@ class AccountBalanceGrouped
// loop all possible periods between $start and $end, and add them to the correct dataset.
$currentStart = clone $this->start;
while ($currentStart <= $this->end) {
$key = $currentStart->format($this->carbonFormat);
$label = $currentStart->toAtomString();
$key = $currentStart->format($this->carbonFormat);
$label = $currentStart->toAtomString();
// normal entries
$income['entries'][$label] = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']);
$expense['entries'][$label] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']);
$income['entries'][$label] = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']);
$expense['entries'][$label] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']);
// converted entries
$income['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_earned'] ?? '0', $currency['native_currency_decimal_places']);
$expense['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_spent'] ?? '0', $currency['native_currency_decimal_places']);
// next loop
$currentStart = app('navigation')->addPeriod($currentStart, $this->preferredRange, 0);
$currentStart = app('navigation')->addPeriod($currentStart, $this->preferredRange, 0);
}
$chartData[] = $income;
$chartData[] = $expense;
$chartData[] = $income;
$chartData[] = $expense;
}
return $chartData;
@@ -142,9 +142,9 @@ class AccountBalanceGrouped
private function processJournal(array $journal): void
{
// format the date according to the period
$period = $journal['date']->format($this->carbonFormat);
$currencyId = (int) $journal['currency_id'];
$currency = $this->findCurrency($currencyId);
$period = $journal['date']->format($this->carbonFormat);
$currencyId = (int) $journal['currency_id'];
$currency = $this->findCurrency($currencyId);
// set the array with monetary info, if it does not exist.
$this->createDefaultDataEntry($journal);
@@ -152,12 +152,12 @@ class AccountBalanceGrouped
$this->createDefaultPeriodEntry($journal);
// is this journal's amount in- our outgoing?
$key = $this->getDataKey($journal);
$amount = 'spent' === $key ? app('steam')->negative($journal['amount']) : app('steam')->positive($journal['amount']);
$key = $this->getDataKey($journal);
$amount = 'spent' === $key ? app('steam')->negative($journal['amount']) : app('steam')->positive($journal['amount']);
// get conversion rate
$rate = $this->getRate($currency, $journal['date']);
$amountConverted = bcmul($amount, $rate);
$rate = $this->getRate($currency, $journal['date']);
$amountConverted = bcmul($amount, $rate);
// perhaps transaction already has the foreign amount in the native currency.
if ((int) $journal['foreign_currency_id'] === $this->default->id) {
@@ -166,7 +166,7 @@ class AccountBalanceGrouped
}
// add normal entry
$this->data[$currencyId][$period][$key] = bcadd($this->data[$currencyId][$period][$key], $amount);
$this->data[$currencyId][$period][$key] = bcadd($this->data[$currencyId][$period][$key], $amount);
// add converted entry
$convertedKey = sprintf('native_%s', $key);
@@ -185,7 +185,7 @@ class AccountBalanceGrouped
private function createDefaultDataEntry(array $journal): void
{
$currencyId = (int) $journal['currency_id'];
$currencyId = (int) $journal['currency_id'];
$this->data[$currencyId] ??= [
'currency_id' => (string) $currencyId,
'currency_symbol' => $journal['currency_symbol'],
@@ -202,8 +202,8 @@ class AccountBalanceGrouped
private function createDefaultPeriodEntry(array $journal): void
{
$currencyId = (int) $journal['currency_id'];
$period = $journal['date']->format($this->carbonFormat);
$currencyId = (int) $journal['currency_id'];
$period = $journal['date']->format($this->carbonFormat);
$this->data[$currencyId][$period] ??= [
'period' => $period,
'spent' => '0',

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Support\Http\Api;
use Carbon\Carbon;
use DateTimeInterface;
use FireflyIII\Models\TransactionCurrency;
/**
@@ -45,7 +46,7 @@ trait ConvertsExchangeRates
// if not enabled, return the same array but without conversion:
return $set;
$this->enabled = false;
$this->enabled = false;
if (false === $this->enabled) {
$set['converted'] = false;
@@ -55,8 +56,8 @@ trait ConvertsExchangeRates
$set['converted'] = true;
/** @var TransactionCurrency $native */
$native = app('amount')->getDefaultCurrency();
$currency = $this->getCurrency((int) $set['currency_id']);
$native = app('amount')->getDefaultCurrency();
$currency = $this->getCurrency((int) $set['currency_id']);
if ($native->id === $currency->id) {
$set['native_currency_id'] = (string) $currency->id;
$set['native_currency_code'] = $currency->code;
@@ -66,9 +67,9 @@ trait ConvertsExchangeRates
return $set;
}
foreach ($set['entries'] as $date => $entry) {
$carbon = Carbon::createFromFormat(\DateTimeInterface::ATOM, $date);
$rate = $this->getRate($currency, $native, $carbon);
$rate = '0' === $rate ? '1' : $rate;
$carbon = Carbon::createFromFormat(DateTimeInterface::ATOM, $date);
$rate = $this->getRate($currency, $native, $carbon);
$rate = '0' === $rate ? '1' : $rate;
app('log')->debug(sprintf('bcmul("%s", "%s")', (string) $entry, $rate));
$set['entries'][$date] = (float) bcmul((string) $entry, $rate);
}

View File

@@ -85,8 +85,8 @@ class ExchangeRateConverter
*/
private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string
{
$key = $this->getCacheKey($from, $to, $date);
$res = Cache::get($key, null);
$key = $this->getCacheKey($from, $to, $date);
$res = Cache::get($key, null);
// find in cache
if (null !== $res) {
@@ -96,7 +96,7 @@ class ExchangeRateConverter
}
// find in database
$rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d'));
$rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d'));
if (null !== $rate) {
Cache::forever($key, $rate);
Log::debug(sprintf('ExchangeRateConverter: Return DB rate from #%d to #%d on %s.', $from->id, $to->id, $date->format('Y-m-d')));
@@ -105,7 +105,7 @@ class ExchangeRateConverter
}
// find reverse in database
$rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d'));
$rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d'));
if (null !== $rate) {
$rate = bcdiv('1', $rate);
Cache::forever($key, $rate);
@@ -143,7 +143,7 @@ class ExchangeRateConverter
if ($from === $to) {
return '1';
}
$key = sprintf('cer-%d-%d-%s', $from, $to, $date);
$key = sprintf('cer-%d-%d-%s', $from, $to, $date);
// perhaps the rate has been cached during this particular run
$preparedRate = $this->prepared[$date][$from][$to] ?? null;
@@ -153,7 +153,7 @@ class ExchangeRateConverter
return $preparedRate;
}
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($key);
if ($cache->has()) {
$rate = $cache->get();
@@ -166,16 +166,15 @@ class ExchangeRateConverter
}
/** @var null|CurrencyExchangeRate $result */
$result = auth()->user()
?->currencyExchangeRates()
->where('from_currency_id', $from)
->where('to_currency_id', $to)
->where('date', '<=', $date)
->orderBy('date', 'DESC')
->first()
;
$result = auth()->user()
?->currencyExchangeRates()
->where('from_currency_id', $from)
->where('to_currency_id', $to)
->where('date', '<=', $date)
->orderBy('date', 'DESC')
->first();
++$this->queryCount;
$rate = (string) $result?->rate;
$rate = (string) $result?->rate;
if ('' === $rate) {
app('log')->debug(sprintf('ExchangeRateConverter: Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date));
@@ -215,13 +214,13 @@ class ExchangeRateConverter
if ($euroId === $currency->id) {
return '1';
}
$rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d'));
$rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d'));
if (null !== $rate) {
// app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate));
return $rate;
}
$rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d'));
$rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d'));
if (null !== $rate) {
return bcdiv('1', $rate);
// app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate));
@@ -250,7 +249,7 @@ class ExchangeRateConverter
if ($cache->has()) {
return (int) $cache->get();
}
$euro = TransactionCurrency::whereCode('EUR')->first();
$euro = TransactionCurrency::whereCode('EUR')->first();
++$this->queryCount;
if (null === $euro) {
throw new FireflyException('Cannot find EUR in system, cannot do currency conversion.');
@@ -272,14 +271,13 @@ class ExchangeRateConverter
$start->startOfDay();
$end->endOfDay();
Log::debug(sprintf('Preparing for %s to %s between %s and %s', $from->code, $to->code, $start->format('Y-m-d'), $end->format('Y-m-d')));
$set = auth()->user()
->currencyExchangeRates()
->where('from_currency_id', $from->id)
->where('to_currency_id', $to->id)
->where('date', '<=', $end->format('Y-m-d'))
->where('date', '>=', $start->format('Y-m-d'))
->orderBy('date', 'DESC')->get()
;
$set = auth()->user()
->currencyExchangeRates()
->where('from_currency_id', $from->id)
->where('to_currency_id', $to->id)
->where('date', '<=', $end->format('Y-m-d'))
->where('date', '>=', $start->format('Y-m-d'))
->orderBy('date', 'DESC')->get();
++$this->queryCount;
if (0 === $set->count()) {
Log::debug('No prepared rates found in this period, use the fallback');
@@ -293,10 +291,10 @@ class ExchangeRateConverter
$this->isPrepared = true;
// so there is a fallback just in case. Now loop the set of rates we DO have.
$temp = [];
$count = 0;
$temp = [];
$count = 0;
foreach ($set as $rate) {
$date = $rate->date->format('Y-m-d');
$date = $rate->date->format('Y-m-d');
$temp[$date] ??= [
$from->id => [
$to->id => $rate->rate,
@@ -305,11 +303,11 @@ class ExchangeRateConverter
++$count;
}
Log::debug(sprintf('Found %d rates in this period.', $count));
$currentStart = clone $start;
$currentStart = clone $start;
while ($currentStart->lte($end)) {
$currentDate = $currentStart->format('Y-m-d');
$currentDate = $currentStart->format('Y-m-d');
$this->prepared[$currentDate] ??= [];
$fallback = $temp[$currentDate][$from->id][$to->id] ?? $this->fallback[$from->id][$to->id] ?? '0';
$fallback = $temp[$currentDate][$from->id][$to->id] ?? $this->fallback[$from->id][$to->id] ?? '0';
if (0 === count($this->prepared[$currentDate]) && 0 !== bccomp('0', $fallback)) {
// fill from temp or fallback or from temp (see before)
$this->prepared[$currentDate][$from->id][$to->id] = $fallback;

View File

@@ -40,7 +40,7 @@ trait ParsesQueryFilters
private function dateOrToday(QueryParameters $parameters, string $field): Carbon
{
$date = today();
$date = today();
$value = $parameters->filter()?->value($field, date('Y-m-d'));

View File

@@ -30,7 +30,7 @@ use Illuminate\Support\Facades\Log;
class SummaryBalanceGrouped
{
private const string SUM = 'sum';
private const string SUM = 'sum';
private array $amounts = [];
private array $currencies;
private CurrencyRepositoryInterface $currencyRepository;
@@ -47,9 +47,9 @@ class SummaryBalanceGrouped
public function groupData(): array
{
Log::debug('Now going to group data.');
$return = [];
$return = [];
foreach ($this->keys as $key) {
$title = match ($key) {
$title = match ($key) {
'sum' => 'balance',
'expense' => 'spent',
'income' => 'earned',
@@ -108,11 +108,11 @@ class SummaryBalanceGrouped
/** @var array $journal */
foreach ($journals as $journal) {
// transaction info:
$currencyId = (int) $journal['currency_id'];
$amount = bcmul($journal['amount'], $multiplier);
$currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$this->currencies[$currencyId] = $currency;
$nativeAmount = $converter->convert($currency, $this->default, $journal['date'], $amount);
$currencyId = (int) $journal['currency_id'];
$amount = bcmul($journal['amount'], $multiplier);
$currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$this->currencies[$currencyId] = $currency;
$nativeAmount = $converter->convert($currency, $this->default, $journal['date'], $amount);
if ((int) $journal['foreign_currency_id'] === $this->default->id) {
// use foreign amount instead
$nativeAmount = $journal['foreign_amount'];

View File

@@ -56,8 +56,8 @@ trait ValidatesUserGroupTrait
}
/** @var User $user */
$user = auth()->user();
$groupId = 0;
$user = auth()->user();
$groupId = 0;
if (!$request->has('user_group_id')) {
$groupId = $user->user_group_id;
Log::debug(sprintf('validateUserGroup: no user group submitted, use default group #%d.', $groupId));
@@ -68,7 +68,7 @@ trait ValidatesUserGroupTrait
}
/** @var UserGroupRepositoryInterface $repository */
$repository = app(UserGroupRepositoryInterface::class);
$repository = app(UserGroupRepositoryInterface::class);
$repository->setUser($user);
$memberships = $repository->getMembershipsFromGroupId($groupId);
@@ -79,14 +79,14 @@ trait ValidatesUserGroupTrait
}
// need to get the group from the membership:
$group = $repository->getById($groupId);
$group = $repository->getById($groupId);
if (null === $group) {
Log::debug(sprintf('validateUserGroup: group #%d does not exist.', $groupId));
throw new AuthorizationException((string) trans('validation.belongs_user_or_user_group'));
}
Log::debug(sprintf('validateUserGroup: validate access of user to group #%d ("%s").', $groupId, $group->title));
$roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : []; // @phpstan-ignore-line
$roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : []; // @phpstan-ignore-line
if (0 === count($roles)) {
Log::debug('validateUserGroup: no roles defined, so no access.');