Expand native amount things.

This commit is contained in:
James Cole 2024-12-24 06:34:12 +01:00
parent 8805bcf6f6
commit e8ef630424
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
9 changed files with 233 additions and 286 deletions

View File

@ -27,6 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\Summary;
use Carbon\Carbon;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Data\DateRequest;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Report\NetWorthInterface;
@ -38,6 +39,7 @@ use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
@ -102,10 +104,10 @@ class BasicController extends Controller
$billData = $this->getBillInformation($start, $end);
$spentData = $this->getLeftToSpendInfo($start, $end);
$netWorthData = $this->getNetWorthInfo($start, $end);
$balanceData = [];
$billData = [];
// $spentData = [];
$netWorthData = [];
// $balanceData = [];
// $billData = [];
// $spentData = [];
// $netWorthData = [];
$total = array_merge($balanceData, $billData, $spentData, $netWorthData);
// give new keys
@ -121,6 +123,9 @@ class BasicController extends Controller
private function getBalanceInformation(Carbon $start, Carbon $end): array
{
// some config settings
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$default = app('amount')->getDefaultCurrency();
// prep some arrays:
$incomes = [];
$expenses = [];
@ -134,16 +139,17 @@ class BasicController extends Controller
$set = $collector->getExtractedJournals();
/** @var array $transactionJournal */
foreach ($set as $transactionJournal) {
$currencyId = (int) $transactionJournal['currency_id'];
/** @var array $journal */
foreach ($set as $journal) {
$currencyId = $convertToNative ? $default->id : (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal);
$incomes[$currencyId] ??= '0';
$incomes[$currencyId] = bcadd(
$incomes[$currencyId],
bcmul($transactionJournal['amount'], '-1')
bcmul($amount, '-1')
);
$sums[$currencyId] ??= '0';
$sums[$currencyId] = bcadd($sums[$currencyId], bcmul($transactionJournal['amount'], '-1'));
$sums[$currencyId] = bcadd($sums[$currencyId], bcmul($amount, '-1'));
}
// collect expenses of user using the new group collector.
@ -152,13 +158,14 @@ class BasicController extends Controller
$collector->setRange($start, $end)->setPage($this->parameters->get('page'))->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$set = $collector->getExtractedJournals();
/** @var array $transactionJournal */
foreach ($set as $transactionJournal) {
$currencyId = (int) $transactionJournal['currency_id'];
/** @var array $journal */
foreach ($set as $journal) {
$currencyId = $convertToNative ? $default->id : (int) $journal['currency_id'];
$amount = Amount::getAmountFromJournal($journal);
$expenses[$currencyId] ??= '0';
$expenses[$currencyId] = bcadd($expenses[$currencyId], $transactionJournal['amount']);
$expenses[$currencyId] = bcadd($expenses[$currencyId], $amount);
$sums[$currencyId] ??= '0';
$sums[$currencyId] = bcadd($sums[$currencyId], $transactionJournal['amount']);
$sums[$currencyId] = bcadd($sums[$currencyId], $amount);
}
// format amounts:
@ -317,7 +324,7 @@ class BasicController extends Controller
{
/** @var User $user */
$user = auth()->user();
$date = today(config('app.timezone'))->startOfDay();
$date = now(config('app.timezone'));
// start and end in the future? use $end
if ($this->notInDateRange($date, $start, $end)) {
/** @var Carbon $date */
@ -327,9 +334,7 @@ class BasicController extends Controller
/** @var NetWorthInterface $netWorthHelper */
$netWorthHelper = app(NetWorthInterface::class);
$netWorthHelper->setUser($user);
$allAccounts = $this->accountRepository->getActiveAccountsByType(
[AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT]
);
$allAccounts = $this->accountRepository->getActiveAccountsByType([AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]);
// filter list on preference of being included.
$filtered = $allAccounts->filter(

View File

@ -33,7 +33,8 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface as AdminAccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Collection;
@ -65,87 +66,58 @@ class NetWorth implements NetWorthInterface
public function byAccounts(Collection $accounts, Carbon $date): array
{
// start in the past, end in the future? use $date
$ids = implode(',', $accounts->pluck('id')->toArray());
$cache = new CacheProperties();
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$ids = implode(',', $accounts->pluck('id')->toArray());
$cache = new CacheProperties();
$cache->addProperty($date);
$cache->addProperty($convertToNative);
$cache->addProperty('net-worth-by-accounts');
$cache->addProperty($ids);
if ($cache->has()) {
return $cache->get();
// return $cache->get();
}
app('log')->debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d')));
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$default = app('amount')->getDefaultCurrency();
$converter = new ExchangeRateConverter();
// default "native" currency has everything twice, for consistency.
$netWorth = [
'native' => [
'balance' => '0',
'native_balance' => '0',
'currency_id' => $default->id,
'currency_code' => $default->code,
'currency_name' => $default->name,
'currency_symbol' => $default->symbol,
'currency_decimal_places' => $default->decimal_places,
'native_currency_id' => $default->id,
'native_currency_code' => $default->code,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_decimal_places' => $default->decimal_places,
],
];
$balances = app('steam')->finalAccountsBalance($accounts, $date);
Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s')));
$default = Amount::getDefaultCurrency();
$netWorth = [];
$balances = Steam::finalAccountsBalance($accounts, $date);
/** @var Account $account */
foreach ($accounts as $account) {
app('log')->debug(sprintf('Now at account #%d ("%s")', $account->id, $account->name));
$currency = $this->getRepository()->getAccountCurrency($account);
if (null === $currency) {
$currency = app('amount')->getDefaultCurrency();
}
$currencyCode = $currency->code;
$balance = '0';
$nativeBalance = '0';
Log::debug(sprintf('Now at account #%d ("%s")', $account->id, $account->name));
$currency = $this->getRepository()->getAccountCurrency($account) ?? $default;
$useNative = $convertToNative && $default->id !== $currency->id;
$currency = $useNative ? $default : $currency;
$currencyCode = $currency->code;
$balance = '0';
$nativeBalance = '0';
if (array_key_exists($account->id, $balances)) {
$balance = $balances[$account->id]['balance'] ?? '0';
$nativeBalance = $balances[$account->id]['native_balance'] ?? '0';
}
app('log')->debug(sprintf('Balance is %s, native balance is %s', $balance, $nativeBalance));
// always subtract virtual balance
$virtualBalance = $account->virtual_balance;
if ('' !== $virtualBalance) {
$balance = bcsub($balance, $virtualBalance);
$nativeVirtualBalance = $converter->convert($default, $currency, $account->created_at, $virtualBalance);
$nativeBalance = bcsub($nativeBalance, $nativeVirtualBalance);
}
Log::debug(sprintf('Balance is %s, native balance is %s', $balance, $nativeBalance));
// always subtract virtual balance again.
$balance = '' !== (string) $account->virtual_balance ? bcsub($balance, $account->virtual_balance) : $balance;
$nativeBalance = '' !== (string) $account->native_virtual_balance ? bcsub($nativeBalance, $account->native_virtual_balance) : $nativeBalance;
$amountToUse = $useNative ? $nativeBalance : $balance;
Log::debug(sprintf('Will use %s %s', $currencyCode, $amountToUse));
$netWorth[$currencyCode] ??= [
'balance' => '0',
'native_balance' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
'native_currency_id' => (string) $default->id,
'native_currency_code' => $default->code,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_decimal_places' => $default->decimal_places,
'balance' => '0',
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => $currency->decimal_places,
];
$netWorth[$currencyCode]['balance'] = bcadd($balance, $netWorth[$currencyCode]['balance']);
$netWorth[$currencyCode]['native_balance'] = bcadd($nativeBalance, $netWorth[$currencyCode]['native_balance']);
$netWorth['native']['balance'] = bcadd($nativeBalance, $netWorth['native']['balance']);
$netWorth['native']['native_balance'] = bcadd($nativeBalance, $netWorth['native']['native_balance']);
$netWorth[$currencyCode]['balance'] = bcadd($amountToUse, $netWorth[$currencyCode]['balance']);
}
$cache->store($netWorth);
$converter->summarize();
return $netWorth;
}
private function getRepository(): AccountRepositoryInterface|AdminAccountRepositoryInterface
private function getRepository(): AccountRepositoryInterface | AdminAccountRepositoryInterface
{
if (null === $this->userGroup) {
return $this->accountRepository;
@ -154,19 +126,19 @@ class NetWorth implements NetWorthInterface
return $this->adminAccountRepository;
}
public function setUser(null|Authenticatable|User $user): void
public function setUser(null | Authenticatable | User $user): void
{
if (!$user instanceof User) {
return;
}
$this->user = $user;
$this->userGroup = null;
$this->user = $user;
$this->userGroup = null;
// make repository:
$this->accountRepository = app(AccountRepositoryInterface::class);
$this->accountRepository->setUser($this->user);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
$this->currencyRepos->setUser($this->user);
}
@ -187,18 +159,18 @@ class NetWorth implements NetWorthInterface
*/
$accounts = $this->getAccounts();
$return = [];
$balances = app('steam')->finalAccountsBalance($accounts, $date);
$balances = Steam::finalAccountsBalance($accounts, $date);
foreach ($accounts as $account) {
$currency = $this->getRepository()->getAccountCurrency($account);
$balance = $balances[$account->id]['balance'] ?? '0';
$currency = $this->getRepository()->getAccountCurrency($account);
$balance = $balances[$account->id]['balance'] ?? '0';
// always subtract virtual balance.
$virtualBalance = $account->virtual_balance;
$virtualBalance = $account->virtual_balance;
if ('' !== $virtualBalance) {
$balance = bcsub($balance, $virtualBalance);
}
$return[$currency->id] ??= [
$return[$currency->id] ??= [
'id' => (string) $currency->id,
'name' => $currency->name,
'symbol' => $currency->symbol,

View File

@ -29,13 +29,11 @@ use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\AvailableBudget;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Controllers\DateCalculation;
use Illuminate\Http\JsonResponse;
@ -48,6 +46,7 @@ class BoxController extends Controller
/**
* Deprecated method, no longer in use.
*
* @deprecated
*/
public function available(): JsonResponse
@ -73,7 +72,7 @@ class BoxController extends Controller
$cache->addProperty($convertToNative);
$cache->addProperty('box-balance');
if ($cache->has()) {
return response()->json($cache->get());
// return response()->json($cache->get());
}
// prep some arrays:
$incomes = [];
@ -91,9 +90,8 @@ class BoxController extends Controller
/** @var array $journal */
foreach ($set as $journal) {
$field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount';
$currencyId = $convertToNative ? $currency->id : (int) $journal['currency_id'];
$amount = $journal[$field] ?? '0';
$amount = Amount::getAmountFromJournal($journal);
$incomes[$currencyId] ??= '0';
$incomes[$currencyId] = bcadd($incomes[$currencyId], app('steam')->positive($amount));
$sums[$currencyId] ??= '0';
@ -109,9 +107,8 @@ class BoxController extends Controller
/** @var array $journal */
foreach ($set as $journal) {
$field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount';
$currencyId = $convertToNative ? $currency->id : (int) $journal['currency_id'];
$amount = $journal[$field] ?? '0';
$amount = Amount::getAmountFromJournal($journal);
$expenses[$currencyId] ??= '0';
$expenses[$currencyId] = bcadd($expenses[$currencyId], $amount);
$sums[$currencyId] ??= '0';

View File

@ -61,7 +61,7 @@ class Account extends Model
'virtual_balance' => 'string',
];
protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban'];
protected $fillable = ['user_id', 'user_group_id', 'account_type_id', 'name', 'active', 'virtual_balance', 'iban','native_virtual_balance'];
protected $hidden = ['encrypted'];
private bool $joinedAccountTypes = false;

View File

@ -37,6 +37,7 @@ use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
use FireflyIII\Services\Internal\Destroy\BillDestroyService;
use FireflyIII\Services\Internal\Update\BillUpdateService;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Query\JoinClause;
@ -501,6 +502,7 @@ class BillRepository implements BillRepositoryInterface
public function sumPaidInRange(Carbon $start, Carbon $end): array
{
Log::debug(sprintf('sumPaidInRange from %s to %s', $start->toW3cString(), $end->toW3cString()));
$bills = $this->getActiveBills();
$return = [];
$convertToNative = app('preferences')->getForUser($this->user, 'convert_to_native', false)->data;
@ -508,12 +510,11 @@ class BillRepository implements BillRepositoryInterface
/** @var Bill $bill */
foreach ($bills as $bill) {
/** @var Collection $set */
$set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']);
$currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency;
$field = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount' : 'amount';
$foreignField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_foreign_amount' : 'foreign_amount';
$return[$currency->id] ??= [
$set = $bill->transactionJournals()->after($start)->before($end)->get(['transaction_journals.*']);
$currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency;
$return[(int) $currency->id] ??= [
'id' => (string) $currency->id,
'name' => $currency->name,
'symbol' => $currency->symbol,
@ -521,20 +522,14 @@ class BillRepository implements BillRepositoryInterface
'decimal_places' => $currency->decimal_places,
'sum' => '0',
];
$setAmount = '0';
/** @var TransactionJournal $transactionJournal */
foreach ($set as $transactionJournal) {
/** @var null|Transaction $sourceTransaction */
$sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first();
if (null !== $sourceTransaction) {
$amount = $sourceTransaction->$field;
if ((int) $sourceTransaction->foreign_currency_id === $currency->id) {
// use foreign amount instead!
$amount = (string) $sourceTransaction->$foreignField;
}
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount);
}
$setAmount = bcadd($setAmount, Amount::getAmountFromJournalObject($transactionJournal));
}
Log::debug(sprintf('Bill #%d ("%s") with %d transaction(s) and sum %s %s', $bill->id, $bill->name, $set->count(), $currency->code, $setAmount));
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $setAmount);
Log::debug(sprintf('Total sum is now %s', $return[$currency->id]['sum']));
}
return $return;
@ -568,6 +563,7 @@ class BillRepository implements BillRepositoryInterface
$minField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount_min' : 'amount_min';
$maxField = $convertToNative && $bill->transactionCurrency->id !== $default->id ? 'native_amount_max' : 'amount_max';
Log::debug(sprintf('min field is %s, max field is %s', $minField, $maxField));
if ($total > 0) {
$currency = $convertToNative && $bill->transactionCurrency->id !== $default->id ? $default : $bill->transactionCurrency;

View File

@ -31,9 +31,11 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Class OperationsRepository
@ -209,11 +211,12 @@ class OperationsRepository implements OperationsRepositoryInterface
?TransactionCurrency $currency = null
): array
{
// this collector excludes all transfers TO
// liabilities (which are also withdrawals)
// because those expenses only become expenses
// once they move from the liability to the friend.
// this collector excludes all transfers TO liabilities (which are also withdrawals)
// because those expenses only become expenses once they move from the liability to the friend.
// TODO this filter must be somewhere in AccountRepositoryInterface because I suspect its needed more often (A113)
// 2024-12-24 disable the exclusion for now.
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user);
$subset = $repository->getAccountsByType(config('firefly.valid_liabilities'));
@ -234,7 +237,7 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)
->setRange($start, $end)
->excludeDestinationAccounts($selection)
// ->excludeDestinationAccounts($selection)
->setTypes([TransactionType::WITHDRAWAL]);
if (null !== $accounts) {
@ -249,13 +252,12 @@ class OperationsRepository implements OperationsRepositoryInterface
$collector->setBudgets($budgets);
$journals = $collector->getExtractedJournals();
// same but for foreign currencies:
// same but for transactions in the foreign currency:
if (null !== $currency) {
// app('log')->debug(sprintf('Currency is "%s".', $currency->name));
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])
->setForeignCurrency($currency)->setBudgets($budgets);
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->setForeignCurrency($currency)->setBudgets($budgets);
if (null !== $accounts) {
$collector->setAccounts($accounts);
@ -273,9 +275,9 @@ class OperationsRepository implements OperationsRepositoryInterface
$currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$field = 'amount';
$foreignField = 'foreign_amount';
// if the user wants everything in native currency, use it.
// if the foreign amount is in this currency it's OK because Amount::getAmountFromJournal catches that.
if ($convertToNative && $default->id !== (int) $journal['currency_id']) {
// use default currency info
$currencyId = $default->id;
@ -283,8 +285,6 @@ class OperationsRepository implements OperationsRepositoryInterface
$currencySymbol = $default->symbol;
$currencyCode = $default->code;
$currencyDecimalPlaces = $default->decimal_places;
$field = 'native_amount';
$foreignField = 'native_foreign_amount';
}
$array[$currencyId] ??= [
'sum' => '0',
@ -294,21 +294,9 @@ class OperationsRepository implements OperationsRepositoryInterface
'currency_code' => $currencyCode,
'currency_decimal_places' => $currencyDecimalPlaces,
];
$array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal[$field]));
// also do foreign amount:
$foreignId = (int) $journal['foreign_currency_id'];
if (0 !== $foreignId && $foreignId !== $currencyId) {
$array[$foreignId] ??= [
'sum' => '0',
'currency_id' => $foreignId,
'currency_name' => $journal['foreign_currency_name'],
'currency_symbol' => $journal['foreign_currency_symbol'],
'currency_code' => $journal['foreign_currency_code'],
'currency_decimal_places' => $journal['foreign_currency_decimal_places'],
];
$array[$foreignId]['sum'] = bcadd($array[$foreignId]['sum'], app('steam')->negative($journal['foreign_amount']));
}
$amount = Amount::getAmountFromJournal($journal);
$array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($amount));
Log::debug(sprintf('Journal #%d adds amount %s %s', $journal['transaction_journal_id'], $currencyCode, $amount));
}
return $array;

View File

@ -24,7 +24,9 @@ declare(strict_types=1);
namespace FireflyIII\Support;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\UserGroup;
use FireflyIII\User;
use Illuminate\Support\Collection;
@ -45,6 +47,46 @@ class Amount
return $this->formatFlat($format->symbol, $format->decimal_places, $amount, $coloured);
}
/**
* Experimental function to see if we can quickly and quietly get the amount from a journal.
* This depends on the user's default currency and the wish to have it converted.
*/
public function getAmountFromJournal(array $journal): string
{
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$currency = app('amount')->getDefaultCurrency();
$field = $convertToNative && $currency->id !== $journal['currency_id'] ? 'native_amount' : 'amount';
$amount = $journal[$field] ?? '0';
// fallback, the transaction has a foreign amount in $currency.
if ($convertToNative && null !== $journal['foreign_amount'] && $currency->id === $journal['foreign_currency_id']) {
$amount = $journal['foreign_amount'];
}
return $amount;
}
/**
* Experimental function to see if we can quickly and quietly get the amount from a journal.
* This depends on the user's default currency and the wish to have it converted.
*/
public function getAmountFromJournalObject(TransactionJournal $journal): string
{
$convertToNative = app('preferences')->get('convert_to_native', false)->data;
$currency = app('amount')->getDefaultCurrency();
$field = $convertToNative && $currency->id !== $journal->transaction_currency_id ? 'native_amount' : 'amount';
/** @var null|Transaction $sourceTransaction */
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
if (null === $sourceTransaction) {
return '0';
}
$amount = $sourceTransaction->$field;
if ((int) $sourceTransaction->foreign_currency_id === $currency->id) {
// use foreign amount instead!
$amount = (string) $sourceTransaction->foreign_amount; // hard coded to be foreign amount.
}
return $amount;
}
/**
* This method will properly format the given number, in color or "black and white",
* as a currency, given two things: the currency required and the current locale.
@ -55,15 +97,15 @@ class Amount
*/
public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string
{
$locale = app('steam')->getLocale();
$rounded = app('steam')->bcround($amount, $decimalPlaces);
$locale = app('steam')->getLocale();
$rounded = app('steam')->bcround($amount, $decimalPlaces);
$coloured ??= true;
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $symbol);
$fmt->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces);
$fmt->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces);
$result = (string) $fmt->format((float) $rounded); // intentional float
$result = (string) $fmt->format((float) $rounded); // intentional float
if (true === $coloured) {
if (1 === bccomp($rounded, '0')) {
@ -109,7 +151,7 @@ class Amount
public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency
{
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty('getDefaultCurrencyByGroup');
$cache->addProperty($userGroup->id);
if ($cache->has()) {
@ -172,20 +214,20 @@ class Amount
private function getLocaleInfo(): array
{
// get config from preference, not from translation:
$locale = app('steam')->getLocale();
$array = app('steam')->getLocaleArray($locale);
$locale = app('steam')->getLocale();
$array = app('steam')->getLocaleArray($locale);
setlocale(LC_MONETARY, $array);
$info = localeconv();
$info = localeconv();
// correct variables
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes');
$info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes');
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes');
$info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes');
$info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space');
$info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space');
$info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space');
$info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space');
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$info['mon_decimal_point'] = $fmt->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL);
$info['mon_thousands_sep'] = $fmt->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL);
@ -208,7 +250,7 @@ class Amount
public static function getAmountJsConfig(bool $sepBySpace, int $signPosn, string $sign, bool $csPrecedes): string
{
// negative first:
$space = ' ';
$space = ' ';
// require space between symbol and amount?
if (false === $sepBySpace) {
@ -217,11 +259,11 @@ class Amount
// there are five possible positions for the "+" or "-" sign (if it is even used)
// pos_a and pos_e could be the ( and ) symbol.
$posA = ''; // before everything
$posB = ''; // before currency symbol
$posC = ''; // after currency symbol
$posD = ''; // before amount
$posE = ''; // after everything
$posA = ''; // before everything
$posB = ''; // before currency symbol
$posC = ''; // after currency symbol
$posD = ''; // before amount
$posE = ''; // after everything
// format would be (currency before amount)
// AB%sC_D%vE
@ -263,11 +305,11 @@ class Amount
}
// default is amount before currency
$format = $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE;
$format = $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE;
if ($csPrecedes) {
// alternative is currency before amount
$format = $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE;
$format = $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE;
}
return $format;

View File

@ -289,7 +289,7 @@ class Steam
;
$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']);
$return['native_balance'] = bcadd('' === (string) $account->native_virtual_balance ? '0' : $account->native_virtual_balance, $return['native_balance']);
// Log::debug(sprintf('native_balance is %s (with virtual balance)', $return['native_balance']));
}

View File

@ -26,6 +26,7 @@ $(function () {
});
function drawChart() {
"use strict";
lineChart(accountFrontpageUrl, 'accounts-chart');
@ -37,13 +38,77 @@ function drawChart() {
columnChart('chart/category/frontpage', 'categories-chart');
columnChart(accountExpenseUrl, 'expense-accounts-chart');
columnChart(accountRevenueUrl, 'revenue-accounts-chart');
// get balance box:
getBalanceBox();
getBillsBox();
getAvailableBox();
getNetWorthBox();
getPiggyBanks();
getAllBoxes();
function getAllBoxes() {
// get summary.
$.getJSON('api/v1/summary/basic?start=' + sessionStart + '&end=' + sessionEnd).done(function (data) {
var key;
// balance.
var balance_top = [];
var balance_bottom = [];
// bills
var unpaid = [];
var paid = [];
// left to spend.
var left_to_spend_top = [];
var left_to_spend_bottom = [];
// net worth
var net_worth = [];
for (key in data) {
// balance
if (key.substring(0, 11) === 'balance-in-') {
balance_top.push(data[key].value_parsed);
balance_bottom.push(data[key].sub_title);
}
// bills
if (key.substring(0, 16) === 'bills-unpaid-in-') {
unpaid.push(data[key].value_parsed);
}
if (key.substring(0, 14) === 'bills-paid-in-') {
paid.push(data[key].value_parsed);
}
// left to spend
if (key.substring(0, 17) === 'left-to-spend-in-') {
left_to_spend_top.push(data[key].value_parsed);
left_to_spend_bottom.push(data[key].sub_title);
if(parseFloat(data[key].monetary_value) < 0) {
$('#box-left-to-spend-box').removeClass('bg-green-gradient').addClass('bg-red-gradient');
}
}
// net worth
if (key.substring(0, 13) === 'net-worth-in-') {
net_worth.push(data[key].value_parsed);
}
}
// balance
$('#box-balance-sums').html(balance_top.join(', '));
$('#box-balance-list').html(balance_bottom.join(', '));
// bills
$('#box-bills-unpaid').html(unpaid.join(', '));
$('#box-bills-paid').html(paid.join(', '));
// left to spend
$('#box-left-to-spend').html(left_to_spend_top.join(', '));
$('#box-left-per-day').html(left_to_spend_bottom.join(', '));
// net worth
$('#box-net-worth').html(net_worth.join(', '));
});
}
//getBoxAmounts();
}
@ -58,121 +123,3 @@ function getPiggyBanks() {
}
});
}
function getNetWorthBox() {
// box-net-worth
$.getJSON('json/box/net-worth').done(function (data) {
$('#box-net-worth').html(data.net_worths.join(', '));
});
}
/**
*
*/
function getAvailableBox() {
// box-left-to-spend
// box-left-per-day
// * 0) If the user has available amount this period and has overspent: overspent box.
// * 1) If the user has available amount this period and has NOT overspent: left to spend box.
// * 2) if the user has no available amount set this period: spent per day
$.getJSON('json/box/available').done(function (data) {
$('#box-left-to-spend-text').text(data.title);
if (0 === data.display) {
$('#box-left-to-spend-box').removeClass('bg-green-gradient').addClass('bg-red-gradient');
$('#box-left-to-spend').html(data.left_to_spend);
$('#box-left-per-day').html(data.left_per_day);
}
if (1 === data.display) {
$('#box-left-to-spend').html(data.left_to_spend);
$('#box-left-per-day').html(data.left_per_day);
}
if (2 === data.display) {
$('#box-left-to-spend').html(data.spent_total);
$('#box-left-per-day').html(data.spent_per_day);
}
});
}
/**
*
*/
function getBillsBox() {
// box-bills-unpaid
// box-bills-paid
// get summary.
$.getJSON('api/v1/summary/basic?start=' + sessionStart + '&end=' + sessionEnd).done(function (data) {
var key;
var unpaid = [];
var paid = [];
for (key in data) {
//console.log(key);
if (key.substr(0, 16) === 'bills-unpaid-in-') {
// only when less than 3.
if (unpaid.length < 3) {
unpaid.push(data[key].value_parsed);
}
}
if (key.substr(0, 14) === 'bills-paid-in-') {
// only when less than 5.
if (paid.length < 3) {
paid.push(data[key].value_parsed);
}
}
}
$('#box-bills-unpaid').html(unpaid.join(', '));
$('#box-bills-paid').html(paid.join(', '));
});
}
/**
*
*/
function getBalanceBox() {
// box-balance-sums
// box-balance-list
$.getJSON('json/box/balance').done(function (data) {
if (data.size === 1) {
// show balance in "sums", show single entry in list.
for (var x in data.sums) {
$('#box-balance-sums').html(data.sums[x]);
$('#box-balance-list').html(data.incomes[x] + ' + ' + data.expenses[x]);
}
return;
}
// do not use "sums", only use list.
$('#box-balance-progress').remove();
var expense, string, sum, income, current;
// first loop, echo only "preferred".
for (x in data.sums) {
current = $('#box-balance-list').html();
sum = data.sums[x];
expense = data.expenses[x];
income = data.incomes[x];
string = income + ' + ' + expense + ': ' + sum;
if (data.preferred == x) {
$('#box-balance-list').html(current + '<span title="' + string + '">' + string + '</span>' + '<br>');
}
}
// then list the others (only 1 space)
var count = 0;
for (x in data.sums) {
if (count > 2) {
return;
}
current = $('#box-balance-list').html();
sum = data.sums[x];
expense = data.expenses[x];
income = data.incomes[x];
string = income + ' + ' + expense + ': ' + sum;
if (data.preferred != x) {
$('#box-balance-list').html(current + '<span title="' + string + '">' + string + '</span>' + '<br>');
}
count++;
}
});
}