Fix charts and balances.

This commit is contained in:
James Cole
2024-12-26 05:11:32 +01:00
parent 756bb9cf5e
commit 6d22663ca2
6 changed files with 361 additions and 201 deletions

View File

@@ -116,7 +116,7 @@ class AccountController extends Controller
]; ];
// TODO this code is also present in the V2 chart account controller so this method is due to be deprecated. // TODO this code is also present in the V2 chart account controller so this method is due to be deprecated.
$currentStart = clone $start; $currentStart = clone $start;
$range = app('steam')->finalAccountBalanceInRange($account, $start, clone $end); $range = app('steam')->finalAccountBalanceInRange($account, $start, clone $end, $this->convertToNative);
// 2022-10-11 this method no longer converts to float. // 2022-10-11 this method no longer converts to float.
$previous = array_values($range)[0]; $previous = array_values($range)[0];
while ($currentStart <= $end) { while ($currentStart <= $end) {

View File

@@ -55,6 +55,7 @@ abstract class Controller extends BaseController
/** @var array<int, string> */ /** @var array<int, string> */
protected array $allowedSort; protected array $allowedSort;
protected ParameterBag $parameters; protected ParameterBag $parameters;
protected bool $convertToNative = false;
/** /**
* Controller constructor. * Controller constructor.
@@ -68,7 +69,9 @@ abstract class Controller extends BaseController
$this->parameters = $this->getParameters(); $this->parameters = $this->getParameters();
if (auth()->check()) { if (auth()->check()) {
$language = app('steam')->getLanguage(); $language = app('steam')->getLanguage();
$this->convertToNative = app('preferences')->get('convert_to_native', false)->data;
app()->setLocale($language); app()->setLocale($language);
} }
return $next($request); return $next($request);

View File

@@ -118,7 +118,7 @@ class AccountController extends Controller
'native_entries' => [], 'native_entries' => [],
]; ];
$currentStart = clone $params['start']; $currentStart = clone $params['start'];
$range = app('steam')->finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $currency); $range = app('steam')->finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $this->convertToNative);
$previous = array_values($range)[0]['balance']; $previous = array_values($range)[0]['balance'];
$previousNative = array_values($range)[0]['native_balance']; $previousNative = array_values($range)[0]['native_balance'];

View File

@@ -54,9 +54,10 @@ class Controller extends BaseController
{ {
use ValidatesUserGroupTrait; use ValidatesUserGroupTrait;
protected const string CONTENT_TYPE = 'application/vnd.api+json'; protected const string CONTENT_TYPE = 'application/vnd.api+json';
protected array $acceptedRoles = [UserRoleEnum::READ_ONLY]; protected array $acceptedRoles = [UserRoleEnum::READ_ONLY];
protected ParameterBag $parameters; protected ParameterBag $parameters;
protected bool $convertToNative = false;
public function __construct() public function __construct()
{ {
@@ -77,12 +78,12 @@ class Controller extends BaseController
*/ */
private function getParameters(): ParameterBag private function getParameters(): ParameterBag
{ {
$bag = new ParameterBag(); $bag = new ParameterBag();
$bag->set('limit', 50); $bag->set('limit', 50);
try { try {
$page = (int) request()->get('page'); $page = (int) request()->get('page');
} catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) { } catch (ContainerExceptionInterface | NotFoundExceptionInterface $e) {
$page = 1; $page = 1;
} }
@@ -112,7 +113,7 @@ class Controller extends BaseController
if (null !== $date) { if (null !== $date) {
try { try {
$obj = Carbon::parse((string) $date, config('app.timezone')); $obj = Carbon::parse((string) $date, config('app.timezone'));
} catch (InvalidDateException|InvalidFormatException $e) { } catch (InvalidDateException | InvalidFormatException $e) {
// don't care // don't care
app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr((string) $date, 0, 20), $e->getMessage())); app('log')->warning(sprintf('Ignored invalid date "%s" in API v2 controller parameter check: %s', substr((string) $date, 0, 20), $e->getMessage()));
} }
@@ -155,18 +156,18 @@ class Controller extends BaseController
final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array final protected function jsonApiList(string $key, LengthAwarePaginator $paginator, AbstractTransformer $transformer): array
{ {
$manager = new Manager(); $manager = new Manager();
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2'; $baseUrl = request()->getSchemeAndHttpHost() . '/api/v2';
// TODO add stuff to path? // TODO add stuff to path?
$manager->setSerializer(new JsonApiSerializer($baseUrl)); $manager->setSerializer(new JsonApiSerializer($baseUrl));
$objects = $paginator->getCollection(); $objects = $paginator->getCollection();
// the transformer, at this point, needs to collect information that ALL items in the collection // the transformer, at this point, needs to collect information that ALL items in the collection
// require, like meta-data and stuff like that, and save it for later. // require, like meta-data and stuff like that, and save it for later.
$objects = $transformer->collectMetaData($objects); $objects = $transformer->collectMetaData($objects);
$paginator->setCollection($objects); $paginator->setCollection($objects);
$resource = new FractalCollection($objects, $transformer, $key); $resource = new FractalCollection($objects, $transformer, $key);
@@ -180,11 +181,11 @@ class Controller extends BaseController
* *
* @param array<int, mixed>|Model $object * @param array<int, mixed>|Model $object
*/ */
final protected function jsonApiObject(string $key, array|Model $object, AbstractTransformer $transformer): array final protected function jsonApiObject(string $key, array | Model $object, AbstractTransformer $transformer): array
{ {
// create some objects: // create some objects:
$manager = new Manager(); $manager = new Manager();
$baseUrl = request()->getSchemeAndHttpHost().'/api/v2'; $baseUrl = request()->getSchemeAndHttpHost() . '/api/v2';
$manager->setSerializer(new JsonApiSerializer($baseUrl)); $manager->setSerializer(new JsonApiSerializer($baseUrl));
$transformer->collectMetaData(new Collection([$object])); $transformer->collectMetaData(new Collection([$object]));

View File

@@ -30,7 +30,6 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Helpers\Collector\GroupCollectorInterface; 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\Models\AccountType;
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;
@@ -86,11 +85,11 @@ class AccountController extends Controller
Log::debug('RevenueAccounts'); Log::debug('RevenueAccounts');
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', today(config('app.timezone'))->startOfMonth()); $start = clone session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone session('end', today(config('app.timezone'))->endOfMonth()); $end = clone session('end', today(config('app.timezone'))->endOfMonth());
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToNative); $cache->addProperty($this->convertToNative);
@@ -101,14 +100,14 @@ class AccountController extends Controller
$start->subDay(); $start->subDay();
// prep some vars: // prep some vars:
$currencies = []; $currencies = [];
$chartData = []; $chartData = [];
$tempData = []; $tempData = [];
$default = Amount::getDefaultCurrency(); $default = Amount::getDefaultCurrency();
// grab all accounts and names // grab all accounts and names
$accounts = $this->accountRepository->getAccountsByType([AccountTypeEnum::EXPENSE->value]); $accounts = $this->accountRepository->getAccountsByType([AccountTypeEnum::EXPENSE->value]);
$accountNames = $this->extractNames($accounts); $accountNames = $this->extractNames($accounts);
// grab all balances // grab all balances
$startBalances = app('steam')->finalAccountsBalance($accounts, $start); $startBalances = app('steam')->finalAccountsBalance($accounts, $start);
@@ -140,13 +139,13 @@ class AccountController extends Controller
continue; continue;
} }
Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance)); Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance));
$searchCode = $this->convertToNative ? $default->code : $key; $searchCode = $this->convertToNative ? $default->code : $key;
Log::debug(sprintf('Search code is %s', $searchCode)); Log::debug(sprintf('Search code is %s', $searchCode));
// see if there is an accompanying start amount. // see if there is an accompanying start amount.
// grab the difference and find the currency. // grab the difference and find the currency.
$startBalance = ($startBalances[$account->id][$key] ?? '0'); $startBalance = ($startBalances[$account->id][$key] ?? '0');
Log::debug(sprintf('Start balance is %s', $startBalance)); Log::debug(sprintf('Start balance is %s', $startBalance));
$diff = bcsub($endBalance, $startBalance); $diff = bcsub($endBalance, $startBalance);
$currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode); $currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode);
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array. // store the values in a temporary array.
@@ -164,10 +163,10 @@ class AccountController extends Controller
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
$newCurrencies[$currency->id] = $currency; $newCurrencies[$currency->id] = $currency;
} }
$currencies = $newCurrencies; $currencies = $newCurrencies;
// sort temp array by amount. // sort temp array by amount.
$amounts = array_column($tempData, 'diff_float'); $amounts = array_column($tempData, 'diff_float');
array_multisort($amounts, SORT_DESC, $tempData); array_multisort($amounts, SORT_DESC, $tempData);
// loop all found currencies and build the data array for the chart. // loop all found currencies and build the data array for the chart.
@@ -178,12 +177,12 @@ class AccountController extends Controller
foreach ($currencies as $currencyId => $currency) { foreach ($currencies as $currencyId => $currency) {
$dataSet $dataSet
= [ = [
'label' => (string) trans('firefly.spent'), 'label' => (string) trans('firefly.spent'),
'type' => 'bar', 'type' => 'bar',
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'entries' => $this->expandNames($tempData), 'entries' => $this->expandNames($tempData),
]; ];
$chartData[$currencyId] = $dataSet; $chartData[$currencyId] = $dataSet;
} }
@@ -194,7 +193,7 @@ class AccountController extends Controller
$chartData[$currencyId]['entries'][$name] = (float) $entry['difference']; $chartData[$currencyId]['entries'][$name] = (float) $entry['difference'];
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -216,7 +215,7 @@ class AccountController extends Controller
*/ */
public function expenseBudget(Account $account, Carbon $start, Carbon $end): JsonResponse public function expenseBudget(Account $account, Carbon $start, Carbon $end): JsonResponse
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
@@ -235,9 +234,9 @@ class AccountController extends Controller
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$budgetId = (int) $journal['budget_id']; $budgetId = (int) $journal['budget_id'];
$key = sprintf('%d-%d', $budgetId, $journal['currency_id']); $key = sprintf('%d-%d', $budgetId, $journal['currency_id']);
$budgetIds[] = $budgetId; $budgetIds[] = $budgetId;
if (!array_key_exists($key, $result)) { if (!array_key_exists($key, $result)) {
$result[$key] = [ $result[$key] = [
'total' => '0', 'total' => '0',
@@ -250,7 +249,7 @@ class AccountController extends Controller
$result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']); $result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']);
} }
$names = $this->getBudgetNames($budgetIds); $names = $this->getBudgetNames($budgetIds);
foreach ($result as $row) { foreach ($result as $row) {
$budgetId = $row['budget_id']; $budgetId = $row['budget_id'];
@@ -259,7 +258,7 @@ class AccountController extends Controller
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']]; $chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
} }
$data = $this->generator->multiCurrencyPieChart($chartData); $data = $this->generator->multiCurrencyPieChart($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -281,7 +280,7 @@ class AccountController extends Controller
*/ */
public function expenseCategory(Account $account, Carbon $start, Carbon $end): JsonResponse public function expenseCategory(Account $account, Carbon $start, Carbon $end): JsonResponse
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
@@ -299,7 +298,7 @@ class AccountController extends Controller
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
if (!array_key_exists($key, $result)) { if (!array_key_exists($key, $result)) {
$result[$key] = [ $result[$key] = [
'total' => '0', 'total' => '0',
@@ -311,7 +310,7 @@ class AccountController extends Controller
} }
$result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']); $result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']);
} }
$names = $this->getCategoryNames(array_keys($result)); $names = $this->getCategoryNames(array_keys($result));
foreach ($result as $row) { foreach ($result as $row) {
$categoryId = $row['category_id']; $categoryId = $row['category_id'];
@@ -320,7 +319,7 @@ class AccountController extends Controller
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']]; $chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
} }
$data = $this->generator->multiCurrencyPieChart($chartData); $data = $this->generator->multiCurrencyPieChart($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -333,9 +332,9 @@ class AccountController extends Controller
* */ * */
public function frontpage(AccountRepositoryInterface $repository): JsonResponse public function frontpage(AccountRepositoryInterface $repository): JsonResponse
{ {
$start = clone session('start', today(config('app.timezone'))->startOfMonth()); $start = clone session('start', today(config('app.timezone'))->startOfMonth());
$end = clone session('end', today(config('app.timezone'))->endOfMonth()); $end = clone session('end', today(config('app.timezone'))->endOfMonth());
$defaultSet = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value])->pluck('id')->toArray(); $defaultSet = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value])->pluck('id')->toArray();
Log::debug('Default set is ', $defaultSet); Log::debug('Default set is ', $defaultSet);
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet); $frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
$frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data; $frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data;
@@ -344,7 +343,7 @@ class AccountController extends Controller
app('preferences')->set('frontpageAccounts', $defaultSet); app('preferences')->set('frontpageAccounts', $defaultSet);
Log::debug('frontpage set is empty!'); Log::debug('frontpage set is empty!');
} }
$accounts = $repository->getAccountsById($frontpageArray); $accounts = $repository->getAccountsById($frontpageArray);
return response()->json($this->accountBalanceChart($accounts, $start, $end)); return response()->json($this->accountBalanceChart($accounts, $start, $end));
} }
@@ -365,7 +364,7 @@ class AccountController extends Controller
*/ */
public function incomeCategory(Account $account, Carbon $start, Carbon $end): JsonResponse public function incomeCategory(Account $account, Carbon $start, Carbon $end): JsonResponse
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
@@ -385,7 +384,7 @@ class AccountController extends Controller
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']); $key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
if (!array_key_exists($key, $result)) { if (!array_key_exists($key, $result)) {
$result[$key] = [ $result[$key] = [
'total' => '0', 'total' => '0',
@@ -398,14 +397,14 @@ class AccountController extends Controller
$result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']); $result[$key]['total'] = bcadd($journal['amount'], $result[$key]['total']);
} }
$names = $this->getCategoryNames(array_keys($result)); $names = $this->getCategoryNames(array_keys($result));
foreach ($result as $row) { foreach ($result as $row) {
$categoryId = $row['category_id']; $categoryId = $row['category_id'];
$name = $names[$categoryId] ?? '(unknown)'; $name = $names[$categoryId] ?? '(unknown)';
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]); $label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']]; $chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
} }
$data = $this->generator->multiCurrencyPieChart($chartData); $data = $this->generator->multiCurrencyPieChart($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
@@ -418,59 +417,103 @@ class AccountController extends Controller
*/ */
public function period(Account $account, Carbon $start, Carbon $end): JsonResponse public function period(Account $account, Carbon $start, Carbon $end): JsonResponse
{ {
$chartData = []; Log::debug('Now in period()');
$cache = new CacheProperties(); $chartData = [];
$cache = new CacheProperties();
$cache->addProperty('chart.account.period'); $cache->addProperty('chart.account.period');
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToNative);
$cache->addProperty($account->id); $cache->addProperty($account->id);
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); // return response()->json($cache->get());
}
$currencies = $this->accountRepository->getUsedCurrencies($account);
// if the account is not expense or revenue, just use the account's default currency.
if (!in_array($account->accountType->type, [AccountType::REVENUE, AccountType::EXPENSE], true)) {
$currencies = [$this->accountRepository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency()];
} }
/** @var TransactionCurrency $currency */ // collect and filter balances for the entire period.
foreach ($currencies as $currency) { $step = $this->calculateStep($start, $end);
$chartData[] = $this->periodByCurrency($start, $end, $account, $currency); Log::debug(sprintf('Step is %s', $step));
$locale = app('steam')->getLocale();
$return = [];
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
// have to make sure this chart is always based on the balance at the END of the period.
// This period depends on the size of the chart
$current = clone $start;
$current = app('navigation')->endOfX($current, $step, null);
$format = (string) trans('config.month_and_day_js', [], $locale);
$accountCurrency = $this->accountRepository->getAccountCurrency($account);
Log::debug('One');
$range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToNative);
Log::debug('Two');
$range = Steam::filterAccountBalances($range, $account, $this->convertToNative, $accountCurrency);
Log::debug('Three');
$previous = array_values($range)[0];
$accountCurrency = $accountCurrency ?? $this->defaultCurrency; // do this AFTER getting the balances.
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
// each day contains multiple balances, and this may even be different over time.
$momentBalance = $range[$theDate] ?? $previous;
$return = $this->updateChartKeys($return, $momentBalance);
// process each balance thing.
foreach($momentBalance as $key => $amount) {
$label = $current->isoFormat($format);
$return[$key]['entries'][$label] = $amount;
}
$current = app('navigation')->addPeriod($current, $step, 0);
// here too, to fix #8041, the data is corrected to the end of the period.
$current = app('navigation')->endOfX($current, $step, null);
$previous = $momentBalance;
}
// second loop (yes) to create nice array with info! Yay!
$chartData = [];
foreach($return as $key => $info) {
if(3 === strlen($key)) {
// assume it's a currency:
$setCurrency = $this->currencyRepository->findByCode($key);
$info['currency_symbol'] = $setCurrency->symbol;
$info['currency_code'] = $setCurrency->code;
$info['label'] = sprintf('%s (%s)', $account->name, $setCurrency->symbol);
}
if('balance' === $key) {
$info['currency_symbol'] = $accountCurrency->symbol;
$info['currency_code'] = $accountCurrency->code;
$info['label'] = sprintf('%s (%s)', $account->name, $accountCurrency->symbol);
}
if('native_balance' === $key) {
$info['currency_symbol'] = $this->defaultCurrency->symbol;
$info['currency_code'] = $this->defaultCurrency->code;
$info['label'] = sprintf('%s (%s) (%s)', $account->name, (string)trans('firefly.sum'), $this->defaultCurrency->symbol);
}
$chartData[] = $info;
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
}
/**
* @throws FireflyException
*/ var_dump($chartData);exit;
private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array
{
Log::debug(sprintf('Now in periodByCurrency("%s", "%s", %s, "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'), $account->id, $currency->code));
$locale = app('steam')->getLocale();
$step = $this->calculateStep($start, $end); $result = [
$result = [
'label' => sprintf('%s (%s)', $account->name, $currency->symbol), 'label' => sprintf('%s (%s)', $account->name, $currency->symbol),
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
]; ];
$entries = []; $entries = [];
$current = clone $start; $current = clone $start;
Log::debug(sprintf('Step is %s', $step));
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
// have to make sure this chart is always based on the balance at the END of the period.
// This period depends on the size of the chart
$current = app('navigation')->endOfX($current, $step, null);
Log::debug(sprintf('$current date is %s', $current->format('Y-m-d'))); Log::debug(sprintf('$current date is %s', $current->format('Y-m-d')));
if ('1D' === $step) { if ('1D' === $step) {
// per day the entire period, balance for every day. // per day the entire period, balance for every day.
$format = (string) trans('config.month_and_day_js', [], $locale); $format = (string) trans('config.month_and_day_js', [], $locale);
$range = app('steam')->finalAccountBalanceInRange($account, $start, $end); $range = app('steam')->finalAccountBalanceInRange($account, $start, $end, $this->convertToNative);
$previous = array_values($range)[0]; $previous = array_values($range)[0];
while ($end >= $current) { while ($end >= $current) {
$theDate = $current->format('Y-m-d'); $theDate = $current->format('Y-m-d');
@@ -489,7 +532,70 @@ class AccountController extends Controller
$entries[$label] = $balance; $entries[$label] = $balance;
$current = app('navigation')->addPeriod($current, $step, 0); $current = app('navigation')->addPeriod($current, $step, 0);
// here too, to fix #8041, the data is corrected to the end of the period. // here too, to fix #8041, the data is corrected to the end of the period.
$current = app('navigation')->endOfX($current, $step, null); $current = app('navigation')->endOfX($current, $step, null);
}
}
$result['entries'] = $entries;
return $result;
/** @var TransactionCurrency $currency */
foreach ($currencies as $currency) {
$chartData[] = $this->periodByCurrency($start, $end, $account, $currency);
}
$data = $this->generator->multiSet($chartData);
$cache->store($data);
return response()->json($data);
}
/**
* @throws FireflyException
*/
private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array
{
Log::debug(sprintf('Now in periodByCurrency("%s", "%s", %s, "%s")', $start->format('Y-m-d'), $end->format('Y-m-d'), $account->id, $currency->code));
$locale = app('steam')->getLocale();
$step = $this->calculateStep($start, $end);
$result = [
'label' => sprintf('%s (%s)', $account->name, $currency->symbol),
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
];
$entries = [];
$current = clone $start;
Log::debug(sprintf('Step is %s', $step));
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041
// have to make sure this chart is always based on the balance at the END of the period.
// This period depends on the size of the chart
$current = app('navigation')->endOfX($current, $step, null);
Log::debug(sprintf('$current date is %s', $current->format('Y-m-d')));
if ('1D' === $step) {
// per day the entire period, balance for every day.
$format = (string) trans('config.month_and_day_js', [], $locale);
$range = app('steam')->finalAccountBalanceInRange($account, $start, $end, $this->convertToNative);
$previous = array_values($range)[0];
while ($end >= $current) {
$theDate = $current->format('Y-m-d');
$balance = $range[$theDate]['balance'] ?? $previous;
$label = $current->isoFormat($format);
$entries[$label] = (float) $balance;
$previous = $balance;
$current->addDay();
}
}
if ('1W' === $step || '1M' === $step || '1Y' === $step) {
while ($end >= $current) {
Log::debug(sprintf('Current is: %s', $current->format('Y-m-d')));
$balance = Steam::finalAccountBalance($account, $current)[$currency->code] ?? '0';
$label = app('navigation')->periodShow($current, $step);
$entries[$label] = $balance;
$current = app('navigation')->addPeriod($current, $step, 0);
// here too, to fix #8041, the data is corrected to the end of the period.
$current = app('navigation')->endOfX($current, $step, null);
} }
} }
$result['entries'] = $entries; $result['entries'] = $entries;
@@ -517,11 +623,11 @@ class AccountController extends Controller
public function revenueAccounts(): JsonResponse public function revenueAccounts(): JsonResponse
{ {
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone session('start', today(config('app.timezone'))->startOfMonth()); $start = clone session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone session('end', today(config('app.timezone'))->endOfMonth()); $end = clone session('end', today(config('app.timezone'))->endOfMonth());
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToNative); $cache->addProperty($this->convertToNative);
@@ -532,14 +638,14 @@ class AccountController extends Controller
$start->subDay(); $start->subDay();
// prep some vars: // prep some vars:
$currencies = []; $currencies = [];
$chartData = []; $chartData = [];
$tempData = []; $tempData = [];
$default = Amount::getDefaultCurrency(); $default = Amount::getDefaultCurrency();
// grab all accounts and names // grab all accounts and names
$accounts = $this->accountRepository->getAccountsByType([AccountTypeEnum::REVENUE->value]); $accounts = $this->accountRepository->getAccountsByType([AccountTypeEnum::REVENUE->value]);
$accountNames = $this->extractNames($accounts); $accountNames = $this->extractNames($accounts);
// grab all balances // grab all balances
$startBalances = app('steam')->finalAccountsBalance($accounts, $start); $startBalances = app('steam')->finalAccountsBalance($accounts, $start);
@@ -572,13 +678,13 @@ class AccountController extends Controller
continue; continue;
} }
Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance)); Log::debug(sprintf('Will process expense array "%s" with amount %s', $key, $endBalance));
$searchCode = $this->convertToNative ? $default->code : $key; $searchCode = $this->convertToNative ? $default->code : $key;
Log::debug(sprintf('Search code is %s', $searchCode)); Log::debug(sprintf('Search code is %s', $searchCode));
// see if there is an accompanying start amount. // see if there is an accompanying start amount.
// grab the difference and find the currency. // grab the difference and find the currency.
$startBalance = ($startBalances[$account->id][$key] ?? '0'); $startBalance = ($startBalances[$account->id][$key] ?? '0');
Log::debug(sprintf('Start balance is %s', $startBalance)); Log::debug(sprintf('Start balance is %s', $startBalance));
$diff = bcsub($endBalance, $startBalance); $diff = bcsub($endBalance, $startBalance);
$currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode); $currencies[$searchCode] ??= $this->currencyRepository->findByCode($searchCode);
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array. // store the values in a temporary array.
@@ -598,10 +704,10 @@ class AccountController extends Controller
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
$newCurrencies[$currency->id] = $currency; $newCurrencies[$currency->id] = $currency;
} }
$currencies = $newCurrencies; $currencies = $newCurrencies;
// sort temp array by amount. // sort temp array by amount.
$amounts = array_column($tempData, 'diff_float'); $amounts = array_column($tempData, 'diff_float');
array_multisort($amounts, SORT_ASC, $tempData); array_multisort($amounts, SORT_ASC, $tempData);
// loop all found currencies and build the data array for the chart. // loop all found currencies and build the data array for the chart.
@@ -612,12 +718,12 @@ class AccountController extends Controller
foreach ($currencies as $currencyId => $currency) { foreach ($currencies as $currencyId => $currency) {
$dataSet $dataSet
= [ = [
'label' => (string) trans('firefly.earned'), 'label' => (string) trans('firefly.earned'),
'type' => 'bar', 'type' => 'bar',
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'entries' => $this->expandNames($tempData), 'entries' => $this->expandNames($tempData),
]; ];
$chartData[$currencyId] = $dataSet; $chartData[$currencyId] = $dataSet;
} }
@@ -628,9 +734,19 @@ class AccountController extends Controller
$chartData[$currencyId]['entries'][$name] = bcmul($entry['difference'], '-1'); $chartData[$currencyId]['entries'][$name] = bcmul($entry['difference'], '-1');
} }
$data = $this->generator->multiSet($chartData); $data = $this->generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return response()->json($data); return response()->json($data);
} }
private function updateChartKeys(array $array, array $balances): array
{
foreach (array_keys($balances) as $key) {
$array[$key] ??= [
'key' => $key,
];
}
return $array;
}
} }

View File

@@ -38,8 +38,8 @@ class Steam
{ {
public function getAccountCurrency(Account $account): ?TransactionCurrency public function getAccountCurrency(Account $account): ?TransactionCurrency
{ {
$type = $account->accountType->type; $type = $account->accountType->type;
$list = config('firefly.valid_currency_account_types'); $list = config('firefly.valid_currency_account_types');
// return null if not in this list. // return null if not in this list.
if (!in_array($type, $list, true)) { if (!in_array($type, $list, true)) {
@@ -69,14 +69,15 @@ class Steam
return $sum; return $sum;
} }
public function finalAccountBalanceInRange(Account $account, Carbon $start, Carbon $end): array public function finalAccountBalanceInRange(Account $account, Carbon $start, Carbon $end, bool $convertToNative): array
{ {
// expand period. // expand period.
$start->subDay()->startOfDay(); $start->subDay()->startOfDay();
$end->addDay()->endOfDay(); $end->addDay()->endOfDay();
Log::debug(sprintf('finalAccountBalanceInRange(#%d, %s, %s)', $account->id, $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// set up cache // set up cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty('final-balance-in-range'); $cache->addProperty('final-balance-in-range');
$cache->addProperty($start); $cache->addProperty($start);
@@ -85,71 +86,108 @@ class Steam
// return $cache->get(); // return $cache->get();
} }
$balances = []; $balances = [];
$formatted = $start->format('Y-m-d'); $formatted = $start->format('Y-m-d');
$startBalance = $this->finalAccountBalance($account, $start); $startBalance = $this->finalAccountBalance($account, $start);
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup); $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
$currency = $this->getAccountCurrency($account) ?? $defaultCurrency; $accountCurrency = $this->getAccountCurrency($account);
$hasCurrency = null !== $accountCurrency;
$currency = $accountCurrency ?? $defaultCurrency;
Log::debug(sprintf('Currency is %s', $currency->code));
if (!$hasCurrency) {
Log::debug(sprintf('Also set start balance in %s', $defaultCurrency->code));
$startBalance[$defaultCurrency->code] ??= '0';
}
$currencies = [ $currencies = [
$currency->id => $currency, $currency->id => $currency,
$defaultCurrency->id => $defaultCurrency, $defaultCurrency->id => $defaultCurrency,
]; ];
$startBalance[$defaultCurrency->code] ??= '0';
$startBalance[$currency->code] ??= '0';
$balances[$formatted] = $startBalance; $startBalance[$currency->code] ??= '0';
$balances[$formatted] = $startBalance;
Log::debug('Final start balance: ', $startBalance);
// sums up the balance changes per day, for foreign, native and normal amounts. // sums up the balance changes per day, for foreign, native and normal amounts.
$set = $account->transactions() $set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s'))
->groupBy('transaction_journals.date') ->groupBy('transaction_journals.date')
->groupBy('transactions.transaction_currency_id') ->groupBy('transactions.transaction_currency_id')
->groupBy('transactions.foreign_currency_id') ->groupBy('transactions.foreign_currency_id')
->orderBy('transaction_journals.date', 'ASC') ->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at') ->whereNull('transaction_journals.deleted_at')
->get( ->get(
[ // @phpstan-ignore-line [ // @phpstan-ignore-line
'transaction_journals.date', 'transaction_journals.date',
'transactions.transaction_currency_id', 'transactions.transaction_currency_id',
\DB::raw('SUM(transactions.amount) AS modified'), \DB::raw('SUM(transactions.amount) AS modified'),
'transactions.foreign_currency_id', 'transactions.foreign_currency_id',
\DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'), \DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'),
\DB::raw('SUM(transactions.native_amount) AS modified_native'), \DB::raw('SUM(transactions.native_amount) AS modified_native'),
] ]
) );
;
$currentBalance = $startBalance; $currentBalance = $startBalance;
/** @var Transaction $entry */ /** @var Transaction $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
// normal, native and foreign amount // normal, native and foreign amount
$carbon = new Carbon($entry->date, $entry->date_tz); $carbon = new Carbon($entry->date, $entry->date_tz);
$modified = (string) (null === $entry->modified ? '0' : $entry->modified); $modified = (string) (null === $entry->modified ? '0' : $entry->modified);
$foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign); $foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign);
$nativeModified = (string) (null === $entry->modified_native ? '0' : $entry->modified_native); $nativeModified = (string) (null === $entry->modified_native ? '0' : $entry->modified_native);
// add "modified" to amount if the currency id matches the account currency id. // find currency of this entry.
if ($entry->transaction_currency_id === $currency->id) { $currencies[$entry->transaction_currency_id] = $currencies[$entry->transaction_currency_id] ?? TransactionCurrency::find($entry->transaction_currency_id);
$currentBalance['balance'] = bcadd($currentBalance['balance'], $modified); $entryCurrency = $currencies[$entry->transaction_currency_id];
$currentBalance[$currency->code] = bcadd($currentBalance[$currency->code], $modified);
Log::debug(sprintf('Processing transaction(s) on date %s', $carbon->format('Y-m-d H:i:s')));
// if convert to native, if NOT convert to native.
if($convertToNative) {
Log::debug(sprintf('Amount is %s %s, foreign amount is %s, native amount is %s', $entryCurrency->code, $modified, $foreignModified, $nativeModified));
// add to native balance.
$currentBalance['native_balance'] = bcadd($currentBalance['native_balance'], $nativeModified);
if($entry->foreign_currency_id === $defaultCurrency->id) {
$currentBalance['native_balance'] = bcadd($currentBalance['native_balance'], $foreignModified);
}
// add to balance if is the same.
if($entry->transaction_currency_id === $accountCurrency?->id) {
$currentBalance['balance'] = bcadd($currentBalance['balance'], $modified);
}
// add currency balance
$currentBalance[$entryCurrency->code] = bcadd($currentBalance[$entryCurrency->code], $modified);
}
if(!$convertToNative) {
Log::debug(sprintf('Amount is %s %s, foreign amount is %s, native amount is %s', $entryCurrency->code, $modified, $foreignModified, $nativeModified));
// add to balance, as expected.
$currentBalance['balance'] = bcadd($currentBalance['balance'] ?? '0', $modified);
// add to GBP, as expected.
$currentBalance[$entryCurrency->code] = bcadd($currentBalance[$entryCurrency->code], $modified);
} }
// always add the native balance, even if it ends up at zero. // // add "modified" to amount if the currency id matches the account currency id.
$currentBalance['native_balance'] = bcadd($currentBalance['native_balance'], $nativeModified); // if ($entry->transaction_currency_id === $currency->id) {
// $currentBalance['balance'] = bcadd($currentBalance['balance'], $modified);
// $currentBalance[$currency->code] = bcadd($currentBalance[$currency->code], $modified);
// }
//
// // always add the native balance, even if it ends up at zero.
// $currentBalance['native_balance'] = bcadd($currentBalance['native_balance'], $nativeModified);
// add modified foreign to the array // DO NOT add modified foreign to the array
if (null !== $entry->foreign_currency_id) { // if (null !== $entry->foreign_currency_id) {
$foreignId = $entry->foreign_currency_id; // $foreignId = $entry->foreign_currency_id;
$currencies[$foreignId] ??= TransactionCurrency::find($foreignId); // $currencies[$foreignId] ??= TransactionCurrency::find($foreignId);
$foreignCurrency = $currencies[$foreignId]; // $foreignCurrency = $currencies[$foreignId];
$currentBalance[$foreignCurrency->code] ??= '0'; // $currentBalance[$foreignCurrency->code] ??= '0';
$currentBalance[$foreignCurrency->code] = bcadd($currentBalance[$foreignCurrency->code], $foreignModified); // $currentBalance[$foreignCurrency->code] = bcadd($currentBalance[$foreignCurrency->code], $foreignModified);
} // }
$balances[$carbon->format('Y-m-d')] = $currentBalance; $balances[$carbon->format('Y-m-d')] = $currentBalance;
Log::debug('Updated entry',$currentBalance);
} }
$cache->store($balances); $cache->store($balances);
@@ -185,10 +223,10 @@ class Steam
// Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); // Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision));
if (str_contains($number, '.')) { if (str_contains($number, '.')) {
if ('-' !== $number[0]) { if ('-' !== $number[0]) {
return bcadd($number, '0.'.str_repeat('0', $precision).'5', $precision); return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision);
} }
return bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision); return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
} }
return $number; return $number;
@@ -287,12 +325,11 @@ class Steam
// first, the "balance", as described earlier. // first, the "balance", as described earlier.
if ($convertToNative) { if ($convertToNative) {
// normal balance // normal balance
$return['balance'] = (string) $account->transactions() $return['balance'] = (string) $account->transactions()
->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 H:i:s')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->where('transactions.transaction_currency_id', $native->id) ->where('transactions.transaction_currency_id', $native->id)
->sum('transactions.amount') ->sum('transactions.amount');
;
// plus virtual balance, if the account has a virtual_balance in the native currency // plus virtual balance, if the account has a virtual_balance in the native currency
if ($native->id === $accountCurrency?->id) { if ($native->id === $accountCurrency?->id) {
$return['balance'] = bcadd('' === (string) $account->virtual_balance ? '0' : $account->virtual_balance, $return['balance']); $return['balance'] = bcadd('' === (string) $account->virtual_balance ? '0' : $account->virtual_balance, $return['balance']);
@@ -301,36 +338,33 @@ class Steam
// native balance // native balance
$return['native_balance'] = (string) $account->transactions() $return['native_balance'] = (string) $account->transactions()
->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 H:i:s')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->whereNot('transactions.transaction_currency_id', $native->id) ->whereNot('transactions.transaction_currency_id', $native->id)
->sum('transactions.native_amount') ->sum('transactions.native_amount');
;
// plus native virtual balance. // plus native virtual balance.
$return['native_balance'] = bcadd('' === (string) $account->native_virtual_balance ? '0' : $account->native_virtual_balance, $return['native_balance']); $return['native_balance'] = bcadd('' === (string) $account->native_virtual_balance ? '0' : $account->native_virtual_balance, $return['native_balance']);
Log::debug(sprintf('native_balance is (all transactions to %s) %s (with virtual balance)', $native->code, $return['native_balance'])); Log::debug(sprintf('native_balance is (all transactions to %s) %s (with virtual balance)', $native->code, $return['native_balance']));
// plus foreign transactions in THIS currency. // plus foreign transactions in THIS currency.
$sum = (string) $account->transactions() $sum = (string) $account->transactions()
->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 H:i:s')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->whereNot('transactions.transaction_currency_id', $native->id) ->whereNot('transactions.transaction_currency_id', $native->id)
->where('transactions.foreign_currency_id', $native->id) ->where('transactions.foreign_currency_id', $native->id)
->sum('transactions.foreign_amount') ->sum('transactions.foreign_amount');
;
$return['native_balance'] = bcadd($return['native_balance'], $sum); $return['native_balance'] = bcadd($return['native_balance'], $sum);
Log::debug(sprintf('Foreign amount transactions add (%s only) %s, total native_balance is now %s', $native->code, $sum, $return['native_balance'])); Log::debug(sprintf('Foreign amount transactions add (%s only) %s, total native_balance is now %s', $native->code, $sum, $return['native_balance']));
} }
// balance(s) in other (all) currencies. // balance(s) in other (all) currencies.
$array = $account->transactions() $array = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->get(['transaction_currencies.code', 'transactions.amount'])->toArray() ->get(['transaction_currencies.code', 'transactions.amount'])->toArray();
; $others = $this->groupAndSumTransactions($array, 'code', 'amount');
$others = $this->groupAndSumTransactions($array, 'code', 'amount');
Log::debug('All balances are (joined)', $others); Log::debug('All balances are (joined)', $others);
// if the account has no own currency preference, drop balance in favor of native balance // if the account has no own currency preference, drop balance in favor of native balance
if ($hasCurrency && !$convertToNative) { if ($hasCurrency && !$convertToNative) {
@@ -346,30 +380,36 @@ class Steam
if (!$hasCurrency && array_key_exists('balance', $return) && array_key_exists('native_balance', $return)) { if (!$hasCurrency && array_key_exists('balance', $return) && array_key_exists('native_balance', $return)) {
Log::debug('Account has no currency preference, dropping balance in favor of native balance.'); Log::debug('Account has no currency preference, dropping balance in favor of native balance.');
$sum = bcadd($return['balance'], $return['native_balance']); $sum = bcadd($return['balance'], $return['native_balance']);
Log::debug(sprintf('%s + %s = %s', $return['balance'], $return['native_balance'], $sum)); Log::debug(sprintf('%s + %s = %s', $return['balance'], $return['native_balance'], $sum));
$return['native_balance'] = $sum; $return['native_balance'] = $sum;
unset($return['balance']); unset($return['balance']);
} }
$final = array_merge($return, $others);
return array_merge($return, $others); Log::debug('Return is', $final);
return $final;
} }
public function filterAccountBalances(array $total, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array { public function filterAccountBalances(array $total, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array
{
Log::debug(sprintf('filterAccountBalances(#%d)', $account->id));
$return = []; $return = [];
foreach($total as $key => $value) { foreach ($total as $key => $value) {
$return[$key] = $this->filterAccountBalance($value, $account, $convertToNative, $currency); $return[$key] = $this->filterAccountBalance($value, $account, $convertToNative, $currency);
} }
Log::debug(sprintf('end of filterAccountBalances(#%d)', $account->id));
return $return; return $return;
} }
public function filterAccountBalance(array $set, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array { public function filterAccountBalance(array $set, Account $account, bool $convertToNative, ?TransactionCurrency $currency = null): array
if(0 === count($set)) { {
Log::debug(sprintf('filterAccountBalance(#%d)', $account->id), $set);
if (0 === count($set)) {
Log::debug(sprintf('Return empty array for account #%d', $account->id)); Log::debug(sprintf('Return empty array for account #%d', $account->id));
return []; return [];
} }
$defaultCurrency = app('amount')->getDefaultCurrency(); $defaultCurrency = app('amount')->getDefaultCurrency();
if($convertToNative) { if ($convertToNative) {
if ($defaultCurrency->id === $currency?->id) { if ($defaultCurrency->id === $currency?->id) {
Log::debug(sprintf('Unset "native_balance" and "%s" for account #%d', $defaultCurrency->code, $account->id)); Log::debug(sprintf('Unset "native_balance" and "%s" for account #%d', $defaultCurrency->code, $account->id));
unset($set['native_balance'], $set[$defaultCurrency->code]); unset($set['native_balance'], $set[$defaultCurrency->code]);
@@ -385,10 +425,10 @@ class Steam
} }
} }
if(!$convertToNative) { if (!$convertToNative) {
if (null === $currency) { if (null === $currency) {
Log::debug(sprintf('Unset native_balance and make defaultCurrency balance the balance for account #%d', $account->id)); Log::debug(sprintf('Unset native_balance and make defaultCurrency balance the balance for account #%d', $account->id));
$set['balance'] = $set[$this->defaultCurrency->code] ?? '0'; $set['balance'] = $set[$defaultCurrency->code] ?? '0';
unset($set['native_balance'], $set[$defaultCurrency->code]); unset($set['native_balance'], $set[$defaultCurrency->code]);
} }
@@ -448,15 +488,15 @@ class Steam
{ {
$list = []; $list = [];
$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 */
foreach ($set as $entry) { foreach ($set as $entry) {
$date = new Carbon($entry->max_date, config('app.timezone')); $date = new Carbon($entry->max_date, config('app.timezone'));
$date->setTimezone(config('app.timezone')); $date->setTimezone(config('app.timezone'));
$list[(int) $entry->account_id] = $date; $list[(int) $entry->account_id] = $date;
} }
@@ -531,9 +571,9 @@ class Steam
public function getSafeUrl(string $unknownUrl, string $safeUrl): string public function getSafeUrl(string $unknownUrl, string $safeUrl): string
{ {
// Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); // Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl));
$returnUrl = $safeUrl; $returnUrl = $safeUrl;
$unknownHost = parse_url($unknownUrl, PHP_URL_HOST); $unknownHost = parse_url($unknownUrl, PHP_URL_HOST);
$safeHost = parse_url($safeUrl, PHP_URL_HOST); $safeHost = parse_url($safeUrl, PHP_URL_HOST);
if (null !== $unknownHost && $unknownHost === $safeHost) { if (null !== $unknownHost && $unknownHost === $safeHost) {
$returnUrl = $unknownUrl; $returnUrl = $unknownUrl;
@@ -570,7 +610,7 @@ class Steam
*/ */
public function floatalize(string $value): string public function floatalize(string $value): string
{ {
$value = strtoupper($value); $value = strtoupper($value);
if (!str_contains($value, 'E')) { if (!str_contains($value, 'E')) {
return $value; return $value;
} }