mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Clean up variety of account balance methods.
This commit is contained in:
parent
df49dd23e2
commit
a0e92b6969
@ -31,6 +31,7 @@ use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\Support\Steam;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
@ -89,11 +90,11 @@ class AccountController extends Controller
|
||||
$currency = $this->repository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||
|
||||
if (in_array($account->accountType->type, $this->balanceTypes, true)) {
|
||||
$balance = app('steam')->balance($account, $date);
|
||||
$balance = Steam::finalAccountBalance($account, $date);
|
||||
$nameWithBalance = sprintf(
|
||||
'%s (%s)',
|
||||
$account->name,
|
||||
app('amount')->formatAnything($currency, $balance, false)
|
||||
app('amount')->formatAnything($currency, $balance['balance'], false)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
@ -129,8 +130,8 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
;
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$journals = array_reverse($journals, true);
|
||||
$dayBeforeBalance = app('steam')->balance($account, $date);
|
||||
$startBalance = $dayBeforeBalance;
|
||||
$dayBeforeBalance = Steam::finalAccountBalance($account, $date);
|
||||
$startBalance = $dayBeforeBalance['balance'];
|
||||
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
|
||||
$currency = $accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||
|
||||
@ -169,7 +170,7 @@ class MonthReportGenerator implements ReportGeneratorInterface
|
||||
'currency' => $currency,
|
||||
'exists' => 0 !== count($journals),
|
||||
'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)),
|
||||
'dayBeforeBalance' => $dayBeforeBalance,
|
||||
];
|
||||
|
@ -34,6 +34,7 @@ use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
@ -110,8 +111,8 @@ class ReconcileController extends Controller
|
||||
|
||||
$startDate = clone $start;
|
||||
$startDate->subDay();
|
||||
$startBalance = app('steam')->bcround(app('steam')->balance($account, $startDate), $currency->decimal_places);
|
||||
$endBalance = app('steam')->bcround(app('steam')->balance($account, $end), $currency->decimal_places);
|
||||
$startBalance = Steam::finalAccountBalance($account, $startDate)['balance'];
|
||||
$endBalance = Steam::finalAccountBalance($account, $end)['balance'];
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
|
||||
$subTitle = (string) trans('firefly.reconcile_account', ['account' => $account->name]);
|
||||
|
||||
|
@ -30,6 +30,7 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\PeriodOverview;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
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')]));
|
||||
$showAll = false;
|
||||
$balance = app('steam')->balance($account, $end);
|
||||
$balance = Steam::finalAccountBalance($account, $end)['balance'];
|
||||
|
||||
return view(
|
||||
'accounts.show',
|
||||
@ -190,7 +191,7 @@ class ShowController extends Controller
|
||||
$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')]);
|
||||
$showAll = true;
|
||||
$balance = app('steam')->balance($account, $end);
|
||||
$balance = Steam::finalAccountBalance($account, $end)['balance'];
|
||||
|
||||
return view(
|
||||
'accounts.show',
|
||||
|
@ -36,6 +36,7 @@ use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Support\CacheProperties;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
||||
use FireflyIII\Support\Http\Controllers\DateCalculation;
|
||||
@ -450,7 +451,7 @@ class AccountController extends Controller
|
||||
if ('1W' === $step || '1M' === $step || '1Y' === $step) {
|
||||
while ($end >= $current) {
|
||||
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);
|
||||
$entries[$label] = $balance;
|
||||
$current = app('navigation')->addPeriod($current, $step, 0);
|
||||
|
@ -29,9 +29,11 @@ use FireflyIII\Events\RequestedVersionCheckStatus;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Http\Middleware\Installer;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@ -120,7 +122,6 @@ class HomeController extends Controller
|
||||
*/
|
||||
public function index(AccountRepositoryInterface $repository): mixed
|
||||
{
|
||||
|
||||
$types = config('firefly.accountTypesByIdentifier.asset');
|
||||
$count = $repository->count($types);
|
||||
Log::channel('audit')->info('User visits homepage.');
|
||||
|
@ -32,6 +32,7 @@ use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
@ -193,8 +194,8 @@ class ReconcileController extends Controller
|
||||
$end->endOfDay();
|
||||
|
||||
$currency = $this->accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
|
||||
$startBalance = app('steam')->bcround(app('steam')->balance($account, $startDate), $currency->decimal_places);
|
||||
$endBalance = app('steam')->bcround(app('steam')->balance($account, $end), $currency->decimal_places);
|
||||
$startBalance = Steam::finalAccountBalance($account, $startDate)['balance'];
|
||||
$endBalance = Steam::finalAccountBalance($account, $end)['balance'];
|
||||
|
||||
// get the transactions
|
||||
$selectionStart = clone $start;
|
||||
|
@ -34,6 +34,7 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Update\JournalUpdateService;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Controllers\ModelInformation;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use FireflyIII\Validation\AccountValidator;
|
||||
@ -222,7 +223,7 @@ class ConvertController extends Controller
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$balance = app('steam')->balance($account, today());
|
||||
$balance = Steam::finalAccountBalance($account, today()->endOfDay())['balance'];
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||
$role = 'l_'.$account->accountType->type;
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
@ -245,7 +246,7 @@ class ConvertController extends Controller
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$balance = app('steam')->balance($account, today());
|
||||
$balance = Steam::finalAccountBalance($account, today()->endOfDay())['balance'];
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
if ('' === $role) {
|
||||
|
@ -35,6 +35,7 @@ use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Collection;
|
||||
@ -334,7 +335,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
|
||||
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')));
|
||||
$balance = app('steam')->balanceConvertedIgnoreVirtual($account, $date, $piggyBank->transactionCurrency);
|
||||
$balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
|
||||
Log::debug(sprintf('Balance is: %s', $balance));
|
||||
|
||||
/** @var Collection $piggies */
|
||||
|
@ -25,6 +25,8 @@ namespace FireflyIII\Support;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Carbon\Exceptions\InvalidFormatException;
|
||||
use DB;
|
||||
use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
@ -33,76 +35,126 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use stdClass;
|
||||
use Str;
|
||||
use ValueError;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
$virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance;
|
||||
// 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;
|
||||
|
||||
// currency of account
|
||||
$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('Start balance on %s is %s', $formatted, $startBalance));
|
||||
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
|
||||
$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
|
||||
@ -124,7 +176,7 @@ class Steam
|
||||
* 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));
|
||||
$cache = new CacheProperties();
|
||||
@ -257,84 +309,15 @@ class Steam
|
||||
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
|
||||
*
|
||||
* @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__));
|
||||
// abuse chart properties:
|
||||
@ -377,250 +360,8 @@ class Steam
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
* @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)
|
||||
*/
|
||||
public function balanceInRangeNative(Account $account, Carbon $start, Carbon $end): array
|
||||
@ -723,10 +464,169 @@ class Steam
|
||||
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.
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @deprecated
|
||||
*/
|
||||
public function balancesByAccounts(Collection $accounts, Carbon $date): array
|
||||
{
|
||||
@ -758,6 +658,7 @@ class Steam
|
||||
* This method always ignores the virtual balance.
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @deprecated
|
||||
*/
|
||||
public function balancesByAccountsConverted(Collection $accounts, Carbon $date): array
|
||||
{
|
||||
@ -792,6 +693,8 @@ class Steam
|
||||
|
||||
/**
|
||||
* Same as above, but also groups per currency.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function balancesPerCurrencyByAccounts(Collection $accounts, Carbon $date): array
|
||||
{
|
||||
@ -819,7 +722,14 @@ class Steam
|
||||
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__));
|
||||
// abuse chart properties:
|
||||
@ -834,10 +744,10 @@ class Steam
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
|
||||
->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 = [];
|
||||
|
||||
/** @var \stdClass $entry */
|
||||
/** @var stdClass $entry */
|
||||
foreach ($balances as $entry) {
|
||||
$return[(int) $entry->transaction_currency_id] = (string) $entry->sum_for_currency;
|
||||
}
|
||||
@ -933,6 +843,103 @@ class Steam
|
||||
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
|
||||
*/
|
||||
@ -942,7 +949,7 @@ class Steam
|
||||
|
||||
try {
|
||||
$hostName = gethostbyaddr($ipAddress);
|
||||
} catch (\Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
app('log')->error($e->getMessage());
|
||||
$hostName = $ipAddress;
|
||||
}
|
||||
@ -961,7 +968,7 @@ class Steam
|
||||
$set = auth()->user()->transactions()
|
||||
->whereIn('transactions.account_id', $accounts)
|
||||
->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 */
|
||||
@ -1051,7 +1058,7 @@ class Steam
|
||||
|
||||
// URL must not lead to weird pages
|
||||
$forbiddenWords = ['jscript', 'json', 'debug', 'serviceworker', 'offline', 'delete', '/login', '/attachments/view'];
|
||||
if (\Str::contains($returnUrl, $forbiddenWords)) {
|
||||
if (Str::contains($returnUrl, $forbiddenWords)) {
|
||||
$returnUrl = $safeUrl;
|
||||
}
|
||||
|
||||
@ -1149,7 +1156,7 @@ class Steam
|
||||
if (-1 === bccomp($amount, '0')) {
|
||||
$amount = bcmul($amount, '-1');
|
||||
}
|
||||
} catch (\ValueError $e) {
|
||||
} catch (ValueError $e) {
|
||||
Log::error(sprintf('ValueError in Steam::positive("%s"): %s', $amount, $e->getMessage()));
|
||||
Log::error($e->getTraceAsString());
|
||||
|
||||
|
@ -25,8 +25,11 @@ namespace FireflyIII\Support\Twig;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Search\OperatorQuerySearch;
|
||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||
use Route;
|
||||
@ -64,27 +67,28 @@ class General extends AbstractExtension
|
||||
|
||||
/** @var Carbon $date */
|
||||
$date = session('end', today(config('app.timezone'))->endOfMonth());
|
||||
$runningBalance = config('firefly.feature_flags.running_balance_column');
|
||||
$info = [];
|
||||
if (true === $runningBalance) {
|
||||
$info = app('steam')->balanceByTransactions($account, $date, null);
|
||||
}
|
||||
if (false === $runningBalance) {
|
||||
$info[] = app('steam')->balance($account, $date);
|
||||
}
|
||||
|
||||
$info = Steam::finalAccountBalance($account, $date);
|
||||
$currency = Steam::getAccountCurrency($account);
|
||||
$native = Amount::getDefaultCurrency();
|
||||
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
|
||||
$strings = [];
|
||||
foreach ($info as $currencyId => $balance) {
|
||||
$balance = (string) $balance;
|
||||
if (0 === $currencyId) {
|
||||
// not good code but OK
|
||||
/** @var AccountRepositoryInterface $accountRepos */
|
||||
$accountRepos = app(AccountRepositoryInterface::class);
|
||||
$currency = $accountRepos->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
|
||||
foreach ($info as $key => $balance) {
|
||||
if ('balance' === $key) {
|
||||
// balance in account currency.
|
||||
if (!$convertToNative || $currency->code === $native->code) {
|
||||
$strings[] = app('amount')->formatAnything($currency, $balance, false);
|
||||
}
|
||||
if (0 !== $currencyId) {
|
||||
$strings[] = app('amount')->formatByCurrencyId($currencyId, $balance, false);
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
|
||||
/**
|
||||
@ -103,7 +104,7 @@ class AccountTransformer extends AbstractTransformer
|
||||
'currency_code' => $currencyCode,
|
||||
'currency_symbol' => $currencySymbol,
|
||||
'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(),
|
||||
'notes' => $this->repository->getNoteText($account),
|
||||
'monthly_payment_date' => $monthlyPaymentDate,
|
||||
|
Loading…
Reference in New Issue
Block a user