Clean up variety of account balance methods.

This commit is contained in:
James Cole 2024-12-22 19:42:06 +01:00
parent df49dd23e2
commit a0e92b6969
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
12 changed files with 448 additions and 426 deletions

View File

@ -31,6 +31,7 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Api\AccountFilter; use FireflyIII\Support\Http\Api\AccountFilter;
use FireflyIII\Support\Steam;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
@ -89,11 +90,11 @@ class AccountController extends Controller
$currency = $this->repository->getAccountCurrency($account) ?? $defaultCurrency; $currency = $this->repository->getAccountCurrency($account) ?? $defaultCurrency;
if (in_array($account->accountType->type, $this->balanceTypes, true)) { if (in_array($account->accountType->type, $this->balanceTypes, true)) {
$balance = app('steam')->balance($account, $date); $balance = Steam::finalAccountBalance($account, $date);
$nameWithBalance = sprintf( $nameWithBalance = sprintf(
'%s (%s)', '%s (%s)',
$account->name, $account->name,
app('amount')->formatAnything($currency, $balance, false) app('amount')->formatAnything($currency, $balance['balance'], false)
); );
} }

View File

@ -31,6 +31,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@ -129,8 +130,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
; ;
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$journals = array_reverse($journals, true); $journals = array_reverse($journals, true);
$dayBeforeBalance = app('steam')->balance($account, $date); $dayBeforeBalance = Steam::finalAccountBalance($account, $date);
$startBalance = $dayBeforeBalance; $startBalance = $dayBeforeBalance['balance'];
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
$currency = $accountRepository->getAccountCurrency($account) ?? $defaultCurrency; $currency = $accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
@ -169,7 +170,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
'currency' => $currency, 'currency' => $currency,
'exists' => 0 !== count($journals), 'exists' => 0 !== count($journals),
'end' => $this->end->isoFormat((string) trans('config.month_and_day_moment_js', [], $locale)), 'end' => $this->end->isoFormat((string) trans('config.month_and_day_moment_js', [], $locale)),
'endBalance' => app('steam')->balance($account, $this->end), 'endBalance' => Steam::finalAccountBalance($account, $this->end)['balance'],
'dayBefore' => $date->isoFormat((string) trans('config.month_and_day_moment_js', [], $locale)), 'dayBefore' => $date->isoFormat((string) trans('config.month_and_day_moment_js', [], $locale)),
'dayBeforeBalance' => $dayBeforeBalance, 'dayBeforeBalance' => $dayBeforeBalance,
]; ];

View File

@ -34,6 +34,7 @@ use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
@ -110,8 +111,8 @@ class ReconcileController extends Controller
$startDate = clone $start; $startDate = clone $start;
$startDate->subDay(); $startDate->subDay();
$startBalance = app('steam')->bcround(app('steam')->balance($account, $startDate), $currency->decimal_places); $startBalance = Steam::finalAccountBalance($account, $startDate)['balance'];
$endBalance = app('steam')->bcround(app('steam')->balance($account, $end), $currency->decimal_places); $endBalance = Steam::finalAccountBalance($account, $end)['balance'];
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type)); $subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
$subTitle = (string) trans('firefly.reconcile_account', ['account' => $account->name]); $subTitle = (string) trans('firefly.reconcile_account', ['account' => $account->name]);

View File

@ -30,6 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Controllers\PeriodOverview; use FireflyIII\Support\Http\Controllers\PeriodOverview;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
@ -128,7 +129,7 @@ class ShowController extends Controller
$groups->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')])); $groups->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]));
$showAll = false; $showAll = false;
$balance = app('steam')->balance($account, $end); $balance = Steam::finalAccountBalance($account, $end)['balance'];
return view( return view(
'accounts.show', 'accounts.show',
@ -190,7 +191,7 @@ class ShowController extends Controller
$groups->setPath(route('accounts.show.all', [$account->id])); $groups->setPath(route('accounts.show.all', [$account->id]));
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]); $chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$showAll = true; $showAll = true;
$balance = app('steam')->balance($account, $end); $balance = Steam::finalAccountBalance($account, $end)['balance'];
return view( return view(
'accounts.show', 'accounts.show',

View File

@ -36,6 +36,7 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Controllers\AugumentData; use FireflyIII\Support\Http\Controllers\AugumentData;
use FireflyIII\Support\Http\Controllers\ChartGeneration; use FireflyIII\Support\Http\Controllers\ChartGeneration;
use FireflyIII\Support\Http\Controllers\DateCalculation; use FireflyIII\Support\Http\Controllers\DateCalculation;
@ -450,7 +451,7 @@ class AccountController extends Controller
if ('1W' === $step || '1M' === $step || '1Y' === $step) { if ('1W' === $step || '1M' === $step || '1Y' === $step) {
while ($end >= $current) { while ($end >= $current) {
Log::debug(sprintf('Current is: %s', $current->format('Y-m-d'))); Log::debug(sprintf('Current is: %s', $current->format('Y-m-d')));
$balance = (float) app('steam')->balance($account, $current, $currency); $balance = Steam::finalAccountBalance($account, $current)[$currency->code] ?? '0';
$label = app('navigation')->periodShow($current, $step); $label = app('navigation')->periodShow($current, $step);
$entries[$label] = $balance; $entries[$label] = $balance;
$current = app('navigation')->addPeriod($current, $step, 0); $current = app('navigation')->addPeriod($current, $step, 0);

View File

@ -29,9 +29,11 @@ use FireflyIII\Events\RequestedVersionCheckStatus;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Middleware\Installer; use FireflyIII\Http\Middleware\Installer;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@ -120,7 +122,6 @@ class HomeController extends Controller
*/ */
public function index(AccountRepositoryInterface $repository): mixed public function index(AccountRepositoryInterface $repository): mixed
{ {
$types = config('firefly.accountTypesByIdentifier.asset'); $types = config('firefly.accountTypesByIdentifier.asset');
$count = $repository->count($types); $count = $repository->count($types);
Log::channel('audit')->info('User visits homepage.'); Log::channel('audit')->info('User visits homepage.');

View File

@ -32,6 +32,7 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -193,8 +194,8 @@ class ReconcileController extends Controller
$end->endOfDay(); $end->endOfDay();
$currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency(); $currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
$startBalance = app('steam')->bcround(app('steam')->balance($account, $startDate), $currency->decimal_places); $startBalance = Steam::finalAccountBalance($account, $startDate)['balance'];
$endBalance = app('steam')->bcround(app('steam')->balance($account, $end), $currency->decimal_places); $endBalance = Steam::finalAccountBalance($account, $end)['balance'];
// get the transactions // get the transactions
$selectionStart = clone $start; $selectionStart = clone $start;

View File

@ -34,6 +34,7 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Services\Internal\Update\JournalUpdateService; use FireflyIII\Services\Internal\Update\JournalUpdateService;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Controllers\ModelInformation; use FireflyIII\Support\Http\Controllers\ModelInformation;
use FireflyIII\Transformers\TransactionGroupTransformer; use FireflyIII\Transformers\TransactionGroupTransformer;
use FireflyIII\Validation\AccountValidator; use FireflyIII\Validation\AccountValidator;
@ -222,7 +223,7 @@ class ConvertController extends Controller
// group accounts: // group accounts:
/** @var Account $account */ /** @var Account $account */
foreach ($accountList as $account) { foreach ($accountList as $account) {
$balance = app('steam')->balance($account, today()); $balance = Steam::finalAccountBalance($account, today()->endOfDay())['balance'];
$currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency; $currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
$role = 'l_'.$account->accountType->type; $role = 'l_'.$account->accountType->type;
$key = (string) trans('firefly.opt_group_'.$role); $key = (string) trans('firefly.opt_group_'.$role);
@ -245,7 +246,7 @@ class ConvertController extends Controller
// group accounts: // group accounts:
/** @var Account $account */ /** @var Account $account */
foreach ($accountList as $account) { foreach ($accountList as $account) {
$balance = app('steam')->balance($account, today()); $balance = Steam::finalAccountBalance($account, today()->endOfDay())['balance'];
$currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency; $currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role'); $role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
if ('' === $role) { if ('' === $role) {

View File

@ -35,6 +35,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
@ -334,7 +335,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
public function leftOnAccount(PiggyBank $piggyBank, Account $account, Carbon $date): string public function leftOnAccount(PiggyBank $piggyBank, Account $account, Carbon $date): string
{ {
Log::debug(sprintf('leftOnAccount("%s","%s","%s")', $piggyBank->name, $account->name, $date->format('Y-m-d H:i:s'))); Log::debug(sprintf('leftOnAccount("%s","%s","%s")', $piggyBank->name, $account->name, $date->format('Y-m-d H:i:s')));
$balance = app('steam')->balanceConvertedIgnoreVirtual($account, $date, $piggyBank->transactionCurrency); $balance = Steam::finalAccountBalance($account, $date)['balance'];
Log::debug(sprintf('Balance is: %s', $balance)); Log::debug(sprintf('Balance is: %s', $balance));
/** @var Collection $piggies */ /** @var Collection $piggies */

View File

@ -25,6 +25,8 @@ namespace FireflyIII\Support;
use Carbon\Carbon; use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException; use Carbon\Exceptions\InvalidFormatException;
use DB;
use Exception;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
@ -33,76 +35,126 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Api\ExchangeRateConverter; use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use stdClass;
use Str;
use ValueError;
/** /**
* Class Steam. * Class Steam.
*/ */
class Steam class Steam
{ {
public function balanceByTransactions(Account $account, Carbon $date, ?TransactionCurrency $currency): array
{
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-by-transactions');
$cache->addProperty($date);
$cache->addProperty(null !== $currency ? $currency->id : 0);
if ($cache->has()) {
return $cache->get();
}
$query = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->orderBy('transaction_journals.date', 'desc')
->orderBy('transaction_journals.order', 'asc')
->orderBy('transaction_journals.description', 'desc')
->orderBy('transactions.amount', 'desc');
if (null !== $currency) {
$query->where('transactions.transaction_currency_id', $currency->id);
$query->limit(1);
$result = $query->get(['transactions.transaction_currency_id', 'transactions.balance_after'])->first();
$key = (int) $result->transaction_currency_id;
$return = [$key => $result->balance_after];
$cache->store($return);
return $return;
}
$return = [];
$result = $query->get(['transactions.transaction_currency_id', 'transactions.balance_after']);
foreach ($result as $entry) {
$key = (int) $entry->transaction_currency_id;
if (array_key_exists($key, $return)) {
continue;
}
$return[$key] = $entry->balance_after;
}
return $return;
}
/** /**
* @throws FireflyException
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @deprecated * @deprecated
*/ */
public function balanceConvertedIgnoreVirtual(Account $account, Carbon $date, TransactionCurrency $currency): string public function balanceInRangeConverted(Account $account, Carbon $start, Carbon $end, TransactionCurrency $native): array
{ {
$balance = $this->balanceConverted($account, $date, $currency); // Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance; $cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-in-range-converted');
$cache->addProperty($native->id);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
//return $cache->get();
}
Log::debug(sprintf('balanceInRangeConverted for account #%d to %s', $account->id, $native->code));
$start->subDay();
$end->addDay();
$balances = [];
$formatted = $start->format('Y-m-d');
$currencies = [];
$startBalance = $this->balanceConverted($account, $start, $native); // already converted to native amount
$balances[$formatted] = $startBalance;
// currency of account Log::debug(sprintf('Start balance on %s is %s', $formatted, $startBalance));
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($account->user);
$accountCurrency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
if ($accountCurrency->id !== $currency->id && 0 !== bccomp($virtual, '0')) {
// convert amount to given currency.
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__)); Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
$virtual = $converter->convert($accountCurrency, $currency, $date, $virtual);
// not sure why this is happening:
$start->addDay();
// grab all transactions between start and end:
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[
'transaction_journals.date',
'transactions.transaction_currency_id',
'transactions.amount',
'transactions.foreign_currency_id',
'transactions.foreign_amount',
]
)->toArray();
// loop the set and convert if necessary:
$currentBalance = $startBalance;
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$day = false;
try {
$day = Carbon::parse($transaction['date'], config('app.timezone'));
} catch (InvalidFormatException $e) {
Log::error(sprintf('Could not parse date "%s" in %s: %s', $transaction['date'], __METHOD__, $e->getMessage()));
}
if (false === $day) {
$day = today(config('app.timezone'));
}
$format = $day->format('Y-m-d');
// if the transaction is in the expected currency, change nothing.
if ((int) $transaction['transaction_currency_id'] === $native->id) {
// change the current balance, set it to today, continue the loop.
$currentBalance = bcadd($currentBalance, $transaction['amount']);
$balances[$format] = $currentBalance;
Log::debug(sprintf('%s: transaction in %s, new balance is %s.', $format, $native->code, $currentBalance));
continue;
}
// if foreign currency is in the expected currency, do nothing:
if ((int) $transaction['foreign_currency_id'] === $native->id) {
$currentBalance = bcadd($currentBalance, $transaction['foreign_amount']);
$balances[$format] = $currentBalance;
Log::debug(sprintf('%s: transaction in %s (foreign), new balance is %s.', $format, $native->code, $currentBalance));
continue;
}
// otherwise, convert 'amount' to the necessary currency:
$currencyId = (int) $transaction['transaction_currency_id'];
$currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$currencies[$currencyId] = $currency;
$rate = $converter->getCurrencyRate($currency, $native, $day);
$convertedAmount = bcmul($transaction['amount'], $rate);
$currentBalance = bcadd($currentBalance, $convertedAmount);
$balances[$format] = $currentBalance;
Log::debug(sprintf(
'%s: transaction in %s(!). Conversion rate is %s. %s %s = %s %s',
$format,
$currency->code,
$rate,
$currency->code,
$transaction['amount'],
$native->code,
$convertedAmount
));
} }
return bcsub($balance, $virtual); $cache->store($balances);
} $converter->summarize();
// return $balances;
}
/** /**
* @throws FireflyException * @throws FireflyException
@ -124,7 +176,7 @@ class Steam
* to the indicated currency ($native). * to the indicated currency ($native).
* *
*/ */
public function balanceConverted(Account $account, Carbon $date, TransactionCurrency $native): string private function balanceConverted(Account $account, Carbon $date, TransactionCurrency $native): string
{ {
Log::debug(sprintf('Now in balanceConverted (%s) for account #%d, converting to %s', $date->format('Y-m-d'), $account->id, $native->code)); Log::debug(sprintf('Now in balanceConverted (%s) for account #%d, converting to %s', $date->format('Y-m-d'), $account->id, $native->code));
$cache = new CacheProperties(); $cache = new CacheProperties();
@ -257,84 +309,15 @@ class Steam
return $balance; return $balance;
} }
/** //
* Balance of an (asset) account in the user's native currency.
* Is calculated by summing up three numbers.
*
* - Transactions in foreign amount that happen to be in the native currency.
* - The rest of the transactions in the native currency.
* - Where both are zero or NULL, the normal amount converted (and stored!)
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function balanceNative(Account $account, Carbon $date): string
{
$native = app('amount')->getDefaultCurrency();
Log::debug(sprintf('Now in balanceNative (%s) for account #%d, converting to %s', $date->format('Y-m-d'), $account->id, $native->code));
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-native');
$cache->addProperty($date);
$cache->addProperty($native->id);
if ($cache->has()) {
$value = $cache->get();
Log::debug(sprintf('Return cached value %s', $value));
return $value;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$currency = $repository->getAccountCurrency($account);
$currency = null === $currency ? $native : $currency;
if ($native->id === $currency->id) {
Log::debug('No conversion necessary!');
return $this->balance($account, $date);
}
$balance = '0';
// transactions in foreign amount that happen to be in the native currency:
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $native->id)
->get(['transactions.foreign_amount'])->toArray();
$balance = bcadd($this->sumTransactions($set, 'foreign_amount'), $balance);
Log::debug(sprintf('The balance is now %s', $balance));
// transactions in the native amount.
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->whereNull('transactions.foreign_currency_id')
->whereNotNull('transactions.native_amount')
->get(['transactions.native_amount'])->toArray();
$balance = bcadd($this->sumTransactions($set, 'native_amount'), $balance);
Log::debug(sprintf('The balance is now %s', $balance));
// transactions in the normal amount with no native amount set.
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->whereNull('transactions.foreign_currency_id')
->whereNull('transactions.native_amount')
->get(['transactions.amount'])->toArray();
$balance = bcadd($this->sumTransactions($set, 'amount'), $balance);
Log::debug(sprintf('The balance is now %s', $balance));
// add virtual balance (also needs conversion)
$virtualNative = null === $account->native_virtual_balance ? '0' : $account->native_virtual_balance;
$final = bcadd($virtualNative, $balance);
Log::debug(sprintf('Final balance is %s', $final));
$cache->store($final);
return $final;
}
/** /**
* Gets balance at the end of current month by default * Gets balance at the end of current month by default
* *
* @throws FireflyException * @throws FireflyException
* @deprecated
*/ */
public function balance(Account $account, Carbon $date, ?TransactionCurrency $currency = null): string private function balance(Account $account, Carbon $date, ?TransactionCurrency $currency = null): string
{ {
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__)); // Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
// abuse chart properties: // abuse chart properties:
@ -377,250 +360,8 @@ class Steam
} }
/** /**
* @throws FireflyException
* @deprecated * @deprecated
*/
public function balanceIgnoreVirtual(Account $account, Carbon $date): string
{
throw new FireflyException('Deprecated method balanceIgnoreVirtual.');
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($account->user);
$currencyId = (int) $repository->getMetaValue($account, 'currency_id');
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currencyId)
->get(['transactions.amount'])->toArray();
$nativeBalance = $this->sumTransactions($transactions, 'amount');
// get all balances in foreign currency:
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $currencyId)
->where('transactions.transaction_currency_id', '!=', $currencyId)
->get(['transactions.foreign_amount'])->toArray();
$foreignBalance = $this->sumTransactions($transactions, 'foreign_amount');
return bcadd($nativeBalance, $foreignBalance);
}
public function sumTransactions(array $transactions, string $key): string
{
$sum = '0';
/** @var array $transaction */
foreach ($transactions as $transaction) {
$value = (string) ($transaction[$key] ?? '0');
$value = '' === $value ? '0' : $value;
$sum = bcadd($sum, $value);
}
return $sum;
}
/**
* Gets the balance for the given account during the whole range, using this format:.
*
* [yyyy-mm-dd] => 123,2
*
* @throws FireflyException
*/
public function balanceInRange(Account $account, Carbon $start, Carbon $end, ?TransactionCurrency $currency = null): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-in-range');
$cache->addProperty(null !== $currency ? $currency->id : 0);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return $cache->get();
}
$start->subDay();
$end->addDay();
$balances = [];
$formatted = $start->format('Y-m-d');
$startBalance = $this->balance($account, $start, $currency);
$balances[$formatted] = $startBalance;
if (null === $currency) {
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($account->user);
$currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
}
$currencyId = $currency->id;
$start->addDay();
// query!
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->groupBy('transaction_journals.date')
->groupBy('transactions.transaction_currency_id')
->groupBy('transactions.foreign_currency_id')
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[ // @phpstan-ignore-line
'transaction_journals.date',
'transactions.transaction_currency_id',
\DB::raw('SUM(transactions.amount) AS modified'),
'transactions.foreign_currency_id',
\DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'),
]
);
$currentBalance = $startBalance;
/** @var Transaction $entry */
foreach ($set as $entry) {
// normal amount and foreign amount
$modified = (string) (null === $entry->modified ? '0' : $entry->modified);
$foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign);
$amount = '0';
if ($currencyId === (int) $entry->transaction_currency_id || 0 === $currencyId) {
// use normal amount:
$amount = $modified;
}
if ($currencyId === (int) $entry->foreign_currency_id) {
// use foreign amount:
$amount = $foreignModified;
}
// Log::debug(sprintf('Trying to add %s and %s.', var_export($currentBalance, true), var_export($amount, true)));
$currentBalance = bcadd($currentBalance, $amount);
$carbon = new Carbon($entry->date, config('app.timezone'));
$date = $carbon->format('Y-m-d');
$balances[$date] = $currentBalance;
}
$cache->store($balances);
return $balances;
}
/**
* @throws FireflyException
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function balanceInRangeConverted(Account $account, Carbon $start, Carbon $end, TransactionCurrency $native): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-in-range-converted');
$cache->addProperty($native->id);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
//return $cache->get();
}
Log::debug(sprintf('balanceInRangeConverted for account #%d to %s', $account->id, $native->code));
$start->subDay();
$end->addDay();
$balances = [];
$formatted = $start->format('Y-m-d');
$currencies = [];
$startBalance = $this->balanceConverted($account, $start, $native); // already converted to native amount
$balances[$formatted] = $startBalance;
Log::debug(sprintf('Start balance on %s is %s', $formatted, $startBalance));
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$converter = new ExchangeRateConverter();
// not sure why this is happening:
$start->addDay();
// grab all transactions between start and end:
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[
'transaction_journals.date',
'transactions.transaction_currency_id',
'transactions.amount',
'transactions.foreign_currency_id',
'transactions.foreign_amount',
]
)->toArray();
// loop the set and convert if necessary:
$currentBalance = $startBalance;
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$day = false;
try {
$day = Carbon::parse($transaction['date'], config('app.timezone'));
} catch (InvalidFormatException $e) {
Log::error(sprintf('Could not parse date "%s" in %s: %s', $transaction['date'], __METHOD__, $e->getMessage()));
}
if (false === $day) {
$day = today(config('app.timezone'));
}
$format = $day->format('Y-m-d');
// if the transaction is in the expected currency, change nothing.
if ((int) $transaction['transaction_currency_id'] === $native->id) {
// change the current balance, set it to today, continue the loop.
$currentBalance = bcadd($currentBalance, $transaction['amount']);
$balances[$format] = $currentBalance;
Log::debug(sprintf('%s: transaction in %s, new balance is %s.', $format, $native->code, $currentBalance));
continue;
}
// if foreign currency is in the expected currency, do nothing:
if ((int) $transaction['foreign_currency_id'] === $native->id) {
$currentBalance = bcadd($currentBalance, $transaction['foreign_amount']);
$balances[$format] = $currentBalance;
Log::debug(sprintf('%s: transaction in %s (foreign), new balance is %s.', $format, $native->code, $currentBalance));
continue;
}
// otherwise, convert 'amount' to the necessary currency:
$currencyId = (int) $transaction['transaction_currency_id'];
$currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$currencies[$currencyId] = $currency;
$rate = $converter->getCurrencyRate($currency, $native, $day);
$convertedAmount = bcmul($transaction['amount'], $rate);
$currentBalance = bcadd($currentBalance, $convertedAmount);
$balances[$format] = $currentBalance;
Log::debug(sprintf(
'%s: transaction in %s(!). Conversion rate is %s. %s %s = %s %s',
$format,
$currency->code,
$rate,
$currency->code,
$transaction['amount'],
$native->code,
$convertedAmount
));
}
$cache->store($balances);
$converter->summarize();
return $balances;
}
/**
* @throws FireflyException
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/ */
public function balanceInRangeNative(Account $account, Carbon $start, Carbon $end): array public function balanceInRangeNative(Account $account, Carbon $start, Carbon $end): array
@ -723,10 +464,169 @@ class Steam
return $balances; return $balances;
} }
/**
* Gets the balance for the given account during the whole range, using this format:.
*
* [yyyy-mm-dd] => 123,2
*
* @throws FireflyException
* @deprecated
*/
public function balanceInRange(Account $account, Carbon $start, Carbon $end, ?TransactionCurrency $currency = null): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-in-range');
$cache->addProperty(null !== $currency ? $currency->id : 0);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return $cache->get();
}
$start->subDay();
$end->addDay();
$balances = [];
$formatted = $start->format('Y-m-d');
$startBalance = $this->balance($account, $start, $currency);
$balances[$formatted] = $startBalance;
if (null === $currency) {
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($account->user);
$currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
}
$currencyId = $currency->id;
$start->addDay();
// query!
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->groupBy('transaction_journals.date')
->groupBy('transactions.transaction_currency_id')
->groupBy('transactions.foreign_currency_id')
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[ // @phpstan-ignore-line
'transaction_journals.date',
'transactions.transaction_currency_id',
DB::raw('SUM(transactions.amount) AS modified'),
'transactions.foreign_currency_id',
DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'),
]
);
$currentBalance = $startBalance;
/** @var Transaction $entry */
foreach ($set as $entry) {
// normal amount and foreign amount
$modified = (string) (null === $entry->modified ? '0' : $entry->modified);
$foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign);
$amount = '0';
if ($currencyId === (int) $entry->transaction_currency_id || 0 === $currencyId) {
// use normal amount:
$amount = $modified;
}
if ($currencyId === (int) $entry->foreign_currency_id) {
// use foreign amount:
$amount = $foreignModified;
}
// Log::debug(sprintf('Trying to add %s and %s.', var_export($currentBalance, true), var_export($amount, true)));
$currentBalance = bcadd($currentBalance, $amount);
$carbon = new Carbon($entry->date, config('app.timezone'));
$date = $carbon->format('Y-m-d');
$balances[$date] = $currentBalance;
}
$cache->store($balances);
return $balances;
}
/**
* Balance of an (asset) account in the user's native currency.
* Is calculated by summing up three numbers.
*
* - Transactions in foreign amount that happen to be in the native currency.
* - The rest of the transactions in the native currency.
* - Where both are zero or NULL, the normal amount converted (and stored!)
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @deprecated
*/
private function balanceNative(Account $account, Carbon $date): string
{
$native = app('amount')->getDefaultCurrency();
Log::debug(sprintf('Now in balanceNative (%s) for account #%d, converting to %s', $date->format('Y-m-d'), $account->id, $native->code));
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-native');
$cache->addProperty($date);
$cache->addProperty($native->id);
if ($cache->has()) {
$value = $cache->get();
Log::debug(sprintf('Return cached value %s', $value));
return $value;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$currency = $repository->getAccountCurrency($account);
$currency = null === $currency ? $native : $currency;
if ($native->id === $currency->id) {
Log::debug('No conversion necessary!');
return $this->balance($account, $date);
}
$balance = '0';
// transactions in foreign amount that happen to be in the native currency:
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $native->id)
->get(['transactions.foreign_amount'])->toArray();
$balance = bcadd($this->sumTransactions($set, 'foreign_amount'), $balance);
Log::debug(sprintf('The balance is now %s', $balance));
// transactions in the native amount.
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->whereNull('transactions.foreign_currency_id')
->whereNotNull('transactions.native_amount')
->get(['transactions.native_amount'])->toArray();
$balance = bcadd($this->sumTransactions($set, 'native_amount'), $balance);
Log::debug(sprintf('The balance is now %s', $balance));
// transactions in the normal amount with no native amount set.
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->whereNull('transactions.foreign_currency_id')
->whereNull('transactions.native_amount')
->get(['transactions.amount'])->toArray();
$balance = bcadd($this->sumTransactions($set, 'amount'), $balance);
Log::debug(sprintf('The balance is now %s', $balance));
// add virtual balance (also needs conversion)
$virtualNative = null === $account->native_virtual_balance ? '0' : $account->native_virtual_balance;
$final = bcadd($virtualNative, $balance);
Log::debug(sprintf('Final balance is %s', $final));
$cache->store($final);
return $final;
}
/** /**
* This method always ignores the virtual balance. * This method always ignores the virtual balance.
* *
* @throws FireflyException * @throws FireflyException
* @deprecated
*/ */
public function balancesByAccounts(Collection $accounts, Carbon $date): array public function balancesByAccounts(Collection $accounts, Carbon $date): array
{ {
@ -758,6 +658,7 @@ class Steam
* This method always ignores the virtual balance. * This method always ignores the virtual balance.
* *
* @throws FireflyException * @throws FireflyException
* @deprecated
*/ */
public function balancesByAccountsConverted(Collection $accounts, Carbon $date): array public function balancesByAccountsConverted(Collection $accounts, Carbon $date): array
{ {
@ -792,6 +693,8 @@ class Steam
/** /**
* Same as above, but also groups per currency. * Same as above, but also groups per currency.
*
* @deprecated
*/ */
public function balancesPerCurrencyByAccounts(Collection $accounts, Carbon $date): array public function balancesPerCurrencyByAccounts(Collection $accounts, Carbon $date): array
{ {
@ -819,7 +722,14 @@ class Steam
return $result; return $result;
} }
public function balancePerCurrency(Account $account, Carbon $date): array /**
* @param Account $account
* @param Carbon $date
*
* @return array
* @deprecated
*/
private function balancePerCurrency(Account $account, Carbon $date): array
{ {
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__)); // Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
// abuse chart properties: // abuse chart properties:
@ -834,10 +744,10 @@ class Steam
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->groupBy('transactions.transaction_currency_id'); ->groupBy('transactions.transaction_currency_id');
$balances = $query->get(['transactions.transaction_currency_id', \DB::raw('SUM(transactions.amount) as sum_for_currency')]); // @phpstan-ignore-line $balances = $query->get(['transactions.transaction_currency_id', DB::raw('SUM(transactions.amount) as sum_for_currency')]); // @phpstan-ignore-line
$return = []; $return = [];
/** @var \stdClass $entry */ /** @var stdClass $entry */
foreach ($balances as $entry) { foreach ($balances as $entry) {
$return[(int) $entry->transaction_currency_id] = (string) $entry->sum_for_currency; $return[(int) $entry->transaction_currency_id] = (string) $entry->sum_for_currency;
} }
@ -933,6 +843,103 @@ class Steam
return str_replace($search, '', $string); return str_replace($search, '', $string);
} }
/**
* Returns the balance of an account at exact moment given. Array with at least one value.
*
* "balance" the balance in whatever currency the account has, so the sum of all transaction that happen to have
* THAT currency.
* "native_balance" the balance according to the "native_amount" + "native_foreign_amount" fields.
* "ABC" the balance in this particular currency code (may repeat for each found currency).
*
* @param Account $account
* @param Carbon $date
*
* @return array
*/
public function finalAccountBalance(Account $account, Carbon $date): array
{
$native = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
$currency = $this->getAccountCurrency($account) ?? $native;
$return = [
'native_balance' => '0',
];
Log::debug(sprintf('Now in finalAccountBalance("%s", "%s")', $account->name, $date->format('Y-m-d H:i:s')));
// first, the "balance", as described earlier.
$array = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->where('transactions.transaction_currency_id', $currency->id)
->get(['transactions.amount'])->toArray();
$return['balance'] = $this->sumTransactions($array, 'amount');
Log::debug(sprintf('balance is %s', $return['balance']));
// add virtual balance:
$return['balance'] = bcadd('' === (string) $account->virtual_balance ? '0' : $account->virtual_balance, $return['balance']);
Log::debug(sprintf('balance is %s (with virtual balance)', $return['balance']));
// then, native balance (if necessary(
if ($native->id !== $currency->id) {
$array = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->get(['transactions.native_amount'])->toArray();
$return['native_balance'] = $this->sumTransactions($array, 'native_amount');
Log::debug(sprintf('native_balance is %s', $return['native_balance']));
$return['native_balance'] = bcadd('' === (string) $account->native_virtual_balance ? '0' : $account->native_virtual_balance, $return['balance']);
Log::debug(sprintf('native_balance is %s (with virtual balance)', $return['native_balance']));
}
// balance(s) in other currencies.
$array = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->get(['transaction_currencies.code', 'transactions.amount'])->toArray();
$others = $this->groupAndSumTransactions($array, 'code', 'amount');
Log::debug('All others are (joined)', $others);
return array_merge($return, $others);
}
public function getAccountCurrency(Account $account): ?TransactionCurrency
{
$type = $account->accountType->type;
$list = config('firefly.valid_currency_account_types');
// return null if not in this list.
if (!in_array($type, $list, true)) {
return null;
}
$result = $account->accountMeta->where('name', 'currency_id')->first();
if (null === $result) {
return null;
}
return TransactionCurrency::find((int) $result->data);
}
private function sumTransactions(array $transactions, string $key): string
{
$sum = '0';
/** @var array $transaction */
foreach ($transactions as $transaction) {
$value = (string) ($transaction[$key] ?? '0');
$value = '' === $value ? '0' : $value;
$sum = bcadd($sum, $value);
}
return $sum;
}
private function groupAndSumTransactions(array $array, string $group, string $field): array
{
$return = [];
foreach ($array as $item) {
$groupKey = $item[$group] ?? 'unknown';
$return[$groupKey] = bcadd($return[$groupKey] ?? '0', $item[$field]);
}
return $return;
}
/** /**
* @throws FireflyException * @throws FireflyException
*/ */
@ -942,7 +949,7 @@ class Steam
try { try {
$hostName = gethostbyaddr($ipAddress); $hostName = gethostbyaddr($ipAddress);
} catch (\Exception $e) { } catch (Exception $e) {
app('log')->error($e->getMessage()); app('log')->error($e->getMessage());
$hostName = $ipAddress; $hostName = $ipAddress;
} }
@ -961,7 +968,7 @@ class Steam
$set = auth()->user()->transactions() $set = auth()->user()->transactions()
->whereIn('transactions.account_id', $accounts) ->whereIn('transactions.account_id', $accounts)
->groupBy(['transactions.account_id', 'transaction_journals.user_id']) ->groupBy(['transactions.account_id', 'transaction_journals.user_id'])
->get(['transactions.account_id', \DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line
; ;
/** @var Transaction $entry */ /** @var Transaction $entry */
@ -1051,7 +1058,7 @@ class Steam
// URL must not lead to weird pages // URL must not lead to weird pages
$forbiddenWords = ['jscript', 'json', 'debug', 'serviceworker', 'offline', 'delete', '/login', '/attachments/view']; $forbiddenWords = ['jscript', 'json', 'debug', 'serviceworker', 'offline', 'delete', '/login', '/attachments/view'];
if (\Str::contains($returnUrl, $forbiddenWords)) { if (Str::contains($returnUrl, $forbiddenWords)) {
$returnUrl = $safeUrl; $returnUrl = $safeUrl;
} }
@ -1149,7 +1156,7 @@ class Steam
if (-1 === bccomp($amount, '0')) { if (-1 === bccomp($amount, '0')) {
$amount = bcmul($amount, '-1'); $amount = bcmul($amount, '-1');
} }
} catch (\ValueError $e) { } catch (ValueError $e) {
Log::error(sprintf('ValueError in Steam::positive("%s"): %s', $amount, $e->getMessage())); Log::error(sprintf('ValueError in Steam::positive("%s"): %s', $amount, $e->getMessage()));
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());

View File

@ -25,8 +25,11 @@ namespace FireflyIII\Support\Twig;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Search\OperatorQuerySearch; use FireflyIII\Support\Search\OperatorQuerySearch;
use League\CommonMark\GithubFlavoredMarkdownConverter; use League\CommonMark\GithubFlavoredMarkdownConverter;
use Route; use Route;
@ -64,27 +67,28 @@ class General extends AbstractExtension
/** @var Carbon $date */ /** @var Carbon $date */
$date = session('end', today(config('app.timezone'))->endOfMonth()); $date = session('end', today(config('app.timezone'))->endOfMonth());
$runningBalance = config('firefly.feature_flags.running_balance_column'); $info = Steam::finalAccountBalance($account, $date);
$info = []; $currency = Steam::getAccountCurrency($account);
if (true === $runningBalance) { $native = Amount::getDefaultCurrency();
$info = app('steam')->balanceByTransactions($account, $date, null); $convertToNative = app('preferences')->get('convert_to_native', false)->data;
}
if (false === $runningBalance) {
$info[] = app('steam')->balance($account, $date);
}
$strings = []; $strings = [];
foreach ($info as $currencyId => $balance) { foreach ($info as $key => $balance) {
$balance = (string) $balance; if ('balance' === $key) {
if (0 === $currencyId) { // balance in account currency.
// not good code but OK if (!$convertToNative || $currency->code === $native->code) {
/** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class);
$currency = $accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
$strings[] = app('amount')->formatAnything($currency, $balance, false); $strings[] = app('amount')->formatAnything($currency, $balance, false);
} }
if (0 !== $currencyId) { continue;
$strings[] = app('amount')->formatByCurrencyId($currencyId, $balance, false); }
if ('native_balance' === $key) {
// balance in native currency.
if($convertToNative) {
$strings[] = app('amount')->formatAnything($native, $balance, false);
}
continue;
}
if ($key !== $currency->code) {
$strings[] = app('amount')->formatAnything(TransactionCurrency::where('code', $key)->first(), $balance, false);
} }
} }
@ -104,15 +108,15 @@ class General extends AbstractExtension
static function (int $size): string { static function (int $size): string {
// less than one GB, more than one MB // less than one GB, more than one MB
if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) { if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) {
return round($size / (1024 * 1024), 2).' MB'; return round($size / (1024 * 1024), 2) . ' MB';
} }
// less than one MB // less than one MB
if ($size < (1024 * 1024)) { if ($size < (1024 * 1024)) {
return round($size / 1024, 2).' KB'; return round($size / 1024, 2) . ' KB';
} }
return $size.' bytes'; return $size . ' bytes';
} }
); );
} }

View File

@ -28,6 +28,7 @@ use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Steam;
use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\ParameterBag;
/** /**
@ -103,7 +104,7 @@ class AccountTransformer extends AbstractTransformer
'currency_code' => $currencyCode, 'currency_code' => $currencyCode,
'currency_symbol' => $currencySymbol, 'currency_symbol' => $currencySymbol,
'currency_decimal_places' => $decimalPlaces, 'currency_decimal_places' => $decimalPlaces,
'current_balance' => app('steam')->bcround(app('steam')->balance($account, $date), $decimalPlaces), 'current_balance' => app('steam')->bcround(Steam::finalAccountBalance($account, $date)['balance'], $decimalPlaces),
'current_balance_date' => $date->toAtomString(), 'current_balance_date' => $date->toAtomString(),
'notes' => $this->repository->getNoteText($account), 'notes' => $this->repository->getNoteText($account),
'monthly_payment_date' => $monthlyPaymentDate, 'monthly_payment_date' => $monthlyPaymentDate,