🤖 Auto commit for release 'develop' on 2025-08-08

This commit is contained in:
JC5
2025-08-08 21:03:45 +02:00
parent 76e91be4dc
commit 92f534bcb3
14 changed files with 323 additions and 318 deletions

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Chart; namespace FireflyIII\Api\V1\Controllers\Chart;
use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Chart\ChartRequest; use FireflyIII\Api\V1\Requests\Chart\ChartRequest;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
@@ -43,7 +42,7 @@ class BalanceController extends Controller
$userGroup = $this->validateUserGroup($request); $userGroup = $this->validateUserGroup($request);
$this->repository->setUserGroup($userGroup); $this->repository->setUserGroup($userGroup);
$this->collector->setUserGroup($userGroup); $this->collector->setUserGroup($userGroup);
$this->chartData = new ChartData(); $this->chartData = new ChartData();
// $this->default = app('amount')->getPrimaryCurrency(); // $this->default = app('amount')->getPrimaryCurrency();
return $next($request); return $next($request);
@@ -69,17 +68,18 @@ class BalanceController extends Controller
// prepare for currency conversion and data collection: // prepare for currency conversion and data collection:
/** @var TransactionCurrency $primary */ /** @var TransactionCurrency $primary */
$primary = Amount::getPrimaryCurrency(); $primary = Amount::getPrimaryCurrency();
// get journals for entire period: // get journals for entire period:
$this->collector->setRange($queryParameters['start'], $queryParameters['end']) $this->collector->setRange($queryParameters['start'], $queryParameters['end'])
->withAccountInformation() ->withAccountInformation()
->setXorAccounts($accounts) ->setXorAccounts($accounts)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::RECONCILIATION->value, TransactionTypeEnum::TRANSFER->value]); ->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::RECONCILIATION->value, TransactionTypeEnum::TRANSFER->value])
$journals = $this->collector->getExtractedJournals(); ;
$journals = $this->collector->getExtractedJournals();
$object = new AccountBalanceGrouped(); $object = new AccountBalanceGrouped();
$object->setPreferredRange($queryParameters['period']); $object->setPreferredRange($queryParameters['period']);
$object->setPrimary($primary); $object->setPrimary($primary);
$object->setAccounts($accounts); $object->setAccounts($accounts);
@@ -87,7 +87,7 @@ class BalanceController extends Controller
$object->setStart($queryParameters['start']); $object->setStart($queryParameters['start']);
$object->setEnd($queryParameters['end']); $object->setEnd($queryParameters['end']);
$object->groupByCurrencyAndPeriod(); $object->groupByCurrencyAndPeriod();
$data = $object->convertToChartData(); $data = $object->convertToChartData();
foreach ($data as $entry) { foreach ($data as $entry) {
$this->chartData->add($entry); $this->chartData->add($entry);
} }

View File

@@ -43,6 +43,7 @@ use FireflyIII\Support\Http\Controllers\DateCalculation;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use function Safe\json_encode; use function Safe\json_encode;
/** /**
@@ -87,14 +88,14 @@ class AccountController extends Controller
Log::debug('ExpenseAccounts'); Log::debug('ExpenseAccounts');
/** @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());
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToPrimary); $cache->addProperty($this->convertToPrimary);
@@ -104,13 +105,13 @@ class AccountController extends Controller
} }
// prep some vars: // prep some vars:
$currencies = []; $currencies = [];
$chartData = []; $chartData = [];
$tempData = []; $tempData = [];
// 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
Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s'))); Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
@@ -144,14 +145,14 @@ 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->convertToPrimary ? $this->primaryCurrency->code : $key; $searchCode = $this->convertToPrimary ? $this->primaryCurrency->code : $key;
$searchCode = 'balance' === $searchCode || 'pc_balance' === $searchCode ? $this->primaryCurrency->code : $searchCode; $searchCode = 'balance' === $searchCode || 'pc_balance' === $searchCode ? $this->primaryCurrency->code : $searchCode;
// 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.
@@ -169,26 +170,26 @@ 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.
/** /**
* @var int $currencyId * @var int $currencyId
* @var TransactionCurrency $currency * @var TransactionCurrency $currency
*/ */
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;
} }
@@ -199,7 +200,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);
@@ -207,7 +208,6 @@ class AccountController extends Controller
/** /**
* Expenses per budget for all time, as shown on account overview. * Expenses per budget for all time, as shown on account overview.
*
*/ */
public function expenseBudgetAll(AccountRepositoryInterface $repository, Account $account): JsonResponse public function expenseBudgetAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
{ {
@@ -222,7 +222,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($this->convertToPrimary); $cache->addProperty($this->convertToPrimary);
@@ -235,8 +235,9 @@ class AccountController extends Controller
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account])) $collector->setAccounts(new Collection([$account]))
->setRange($start, $end) ->setRange($start, $end)
->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); ->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
;
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$chartData = []; $chartData = [];
$result = []; $result = [];
@@ -244,9 +245,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;
// currency info: // currency info:
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
@@ -277,7 +278,7 @@ class AccountController extends Controller
$result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']); $result[$key]['total'] = bcadd((string)$journal[$field], $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'];
@@ -286,7 +287,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);
@@ -308,14 +309,14 @@ 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);
$cache->addProperty($this->convertToPrimary); $cache->addProperty($this->convertToPrimary);
$cache->addProperty('chart.account.expense-category'); $cache->addProperty('chart.account.expense-category');
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); return response()->json($cache->get());
} }
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
@@ -327,7 +328,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)) {
// currency info: // currency info:
@@ -345,7 +346,7 @@ class AccountController extends Controller
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places; $currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
} }
$result[$key] = [ $result[$key] = [
'total' => '0', 'total' => '0',
'category_id' => (int)$journal['category_id'], 'category_id' => (int)$journal['category_id'],
'currency_name' => $currencyName, 'currency_name' => $currencyName,
@@ -356,7 +357,7 @@ class AccountController extends Controller
} }
$result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']); $result[$key]['total'] = bcadd((string)$journal[$field], $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'];
@@ -365,7 +366,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);
@@ -378,9 +379,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 = Preferences::get('frontpageAccounts', $defaultSet); $frontpage = Preferences::get('frontpageAccounts', $defaultSet);
$frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data; $frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data;
@@ -389,7 +390,7 @@ class AccountController extends Controller
Preferences::set('frontpageAccounts', $defaultSet); Preferences::set('frontpageAccounts', $defaultSet);
Log::debug('frontpage set is empty!'); Log::debug('frontpage set is empty!');
} }
$accounts = $repository->getAccountsById($frontpageArray); $accounts = $repository->getAccountsById($frontpageArray);
// move to end of day for $end. // move to end of day for $end.
$end->endOfDay(); $end->endOfDay();
@@ -413,14 +414,14 @@ 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($this->convertToPrimary); $cache->addProperty($this->convertToPrimary);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('chart.account.income-category'); $cache->addProperty('chart.account.income-category');
if ($cache->has()) { if ($cache->has()) {
return response()->json($cache->get()); return response()->json($cache->get());
} }
// grab all journals: // grab all journals:
@@ -434,7 +435,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)) {
// currency info: // currency info:
@@ -452,26 +453,26 @@ class AccountController extends Controller
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places; $currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
} }
$result[$key] = [ $result[$key] = [
'total' => '0', 'total' => '0',
'category_id' => $journal['category_id'], 'category_id' => $journal['category_id'],
'currency_name' => $currencyName, 'currency_name' => $currencyName,
'currency_code' => $currencyCode, 'currency_code' => $currencyCode,
'currency_symbol' => $currencySymbol, 'currency_symbol' => $currencySymbol,
'currency_decimal_places' => $currencyDecimalPlaces, 'currency_decimal_places' => $currencyDecimalPlaces,
]; ];
} }
$result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']); $result[$key]['total'] = bcadd((string)$journal[$field], $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);
@@ -488,7 +489,7 @@ class AccountController extends Controller
$end->endOfDay(); $end->endOfDay();
// TODO not sure if these date ranges will work as expected. // TODO not sure if these date ranges will work as expected.
Log::debug(sprintf('Now in period("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('Now in period("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
$cache = new CacheProperties(); $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);
@@ -499,10 +500,10 @@ class AccountController extends Controller
} }
// collect and filter balances for the entire period. // collect and filter balances for the entire period.
$step = $this->calculateStep($start, $end); $step = $this->calculateStep($start, $end);
Log::debug(sprintf('Step is %s', $step)); Log::debug(sprintf('Step is %s', $step));
$locale = Steam::getLocale(); $locale = Steam::getLocale();
$return = []; $return = [];
// fix for issue https://github.com/firefly-iii/firefly-iii/issues/8041 // 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. // have to make sure this chart is always based on the balance at the END of the period.
@@ -512,8 +513,8 @@ class AccountController extends Controller
$format = (string)trans('config.month_and_day_js', [], $locale); $format = (string)trans('config.month_and_day_js', [], $locale);
$accountCurrency = $this->accountRepository->getAccountCurrency($account); $accountCurrency = $this->accountRepository->getAccountCurrency($account);
$range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary); $range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary);
$range = Steam::filterAccountBalances($range, $account, $this->convertToPrimary, $accountCurrency); $range = Steam::filterAccountBalances($range, $account, $this->convertToPrimary, $accountCurrency);
// temp, get end balance. // temp, get end balance.
Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String())); Log::debug(sprintf('period: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
@@ -524,14 +525,14 @@ class AccountController extends Controller
$accountCurrency ??= $this->primaryCurrency; // do this AFTER getting the balances. $accountCurrency ??= $this->primaryCurrency; // do this AFTER getting the balances.
Log::debug('Start chart loop.'); Log::debug('Start chart loop.');
$newRange = []; $newRange = [];
$expectedIndex = 0; $expectedIndex = 0;
Log::debug('Balances exist at:'); Log::debug('Balances exist at:');
foreach ($range as $key => $value) { foreach ($range as $key => $value) {
$newRange[] = ['date' => $key, 'info' => $value]; $newRange[] = ['date' => $key, 'info' => $value];
Log::debug(sprintf('%d - %s (%s)', count($newRange) - 1, $key, json_encode($value))); Log::debug(sprintf('%d - %s (%s)', count($newRange) - 1, $key, json_encode($value)));
} }
$carbon = Carbon::createFromFormat('Y-m-d', $newRange[0]['date'])->endOfDay(); $carbon = Carbon::createFromFormat('Y-m-d', $newRange[0]['date'])->endOfDay();
Log::debug(sprintf('Start of loop, $carbon is %s', $carbon->format('Y-m-d H:i:s'))); Log::debug(sprintf('Start of loop, $carbon is %s', $carbon->format('Y-m-d H:i:s')));
while ($end->gte($current)) { while ($end->gte($current)) {
$momentBalance = $previous; $momentBalance = $previous;
@@ -552,21 +553,21 @@ class AccountController extends Controller
} }
} }
Log::debug(sprintf('momentBalance is now %s', json_encode($momentBalance))); Log::debug(sprintf('momentBalance is now %s', json_encode($momentBalance)));
$return = $this->updateChartKeys($return, $momentBalance); $return = $this->updateChartKeys($return, $momentBalance);
$previous = $momentBalance; $previous = $momentBalance;
// process each balance thing. // process each balance thing.
foreach ($momentBalance as $key => $amount) { foreach ($momentBalance as $key => $amount) {
$label = $current->isoFormat($format); $label = $current->isoFormat($format);
$return[$key]['entries'][$label] = $amount; $return[$key]['entries'][$label] = $amount;
} }
$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);
} }
Log::debug('End of chart loop.'); Log::debug('End of chart loop.');
// second loop (yes) to create nice array with info! Yay! // second loop (yes) to create nice array with info! Yay!
$chartData = []; $chartData = [];
foreach ($return as $key => $info) { foreach ($return as $key => $info) {
if ('balance' !== $key && 'pc_balance' !== $key) { if ('balance' !== $key && 'pc_balance' !== $key) {
@@ -589,7 +590,7 @@ class AccountController extends Controller
$chartData[] = $info; $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);
@@ -626,15 +627,15 @@ 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());
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToPrimary); $cache->addProperty($this->convertToPrimary);
@@ -644,13 +645,13 @@ class AccountController extends Controller
} }
// prep some vars: // prep some vars:
$currencies = []; $currencies = [];
$chartData = []; $chartData = [];
$tempData = []; $tempData = [];
// 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
Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s'))); Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
@@ -685,14 +686,14 @@ 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->convertToPrimary ? $this->primaryCurrency->code : $key; $searchCode = $this->convertToPrimary ? $this->primaryCurrency->code : $key;
$searchCode = 'balance' === $searchCode || 'pc_balance' === $searchCode ? $this->primaryCurrency->code : $searchCode; $searchCode = 'balance' === $searchCode || 'pc_balance' === $searchCode ? $this->primaryCurrency->code : $searchCode;
// 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.
@@ -712,26 +713,26 @@ 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.
/** /**
* @var int $currencyId * @var int $currencyId
* @var TransactionCurrency $currency * @var TransactionCurrency $currency
*/ */
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;
} }
@@ -742,7 +743,7 @@ 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);

View File

@@ -85,52 +85,52 @@ class ShowController extends Controller
public function show(TransactionGroup $transactionGroup) public function show(TransactionGroup $transactionGroup)
{ {
/** @var User $admin */ /** @var User $admin */
$admin = auth()->user(); $admin = auth()->user();
// use new group collector: // use new group collector:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($admin)->setTransactionGroup($transactionGroup)->withAPIInformation(); $collector->setUser($admin)->setTransactionGroup($transactionGroup)->withAPIInformation();
$selectedGroup = $collector->getGroups()->first(); $selectedGroup = $collector->getGroups()->first();
if (null === $selectedGroup) { if (null === $selectedGroup) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
// enrich // enrich
$enrichment = new TransactionGroupEnrichment(); $enrichment = new TransactionGroupEnrichment();
$enrichment->setUser($admin); $enrichment->setUser($admin);
$selectedGroup = $enrichment->enrichSingle($selectedGroup); $selectedGroup = $enrichment->enrichSingle($selectedGroup);
/** @var null|TransactionJournal $first */ /** @var null|TransactionJournal $first */
$first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']); $first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']);
$splits = $transactionGroup->transactionJournals()->count(); $splits = $transactionGroup->transactionJournals()->count();
$splits = count($selectedGroup['transactions']); $splits = count($selectedGroup['transactions']);
$keys = array_keys($selectedGroup['transactions']); $keys = array_keys($selectedGroup['transactions']);
$first = $selectedGroup['transactions'][array_shift($keys)]; $first = $selectedGroup['transactions'][array_shift($keys)];
unset($keys); unset($keys);
if (null === $first) { if (null === $first) {
throw new FireflyException('This transaction is broken :(.'); throw new FireflyException('This transaction is broken :(.');
} }
$type = (string)trans(sprintf('firefly.%s', $first['transaction_type_type'])); $type = (string)trans(sprintf('firefly.%s', $first['transaction_type_type']));
$title = 1 === $splits ? $first['description'] : $selectedGroup['title']; $title = 1 === $splits ? $first['description'] : $selectedGroup['title'];
$subTitle = sprintf('%s: "%s"', $type, $title); $subTitle = sprintf('%s: "%s"', $type, $title);
// enrich // enrich
$enrichment = new TransactionGroupEnrichment(); $enrichment = new TransactionGroupEnrichment();
$enrichment->setUser($admin); $enrichment->setUser($admin);
$selectedGroup = $enrichment->enrichSingle($selectedGroup); $selectedGroup = $enrichment->enrichSingle($selectedGroup);
/** @var TransactionGroupTransformer $transformer */ /** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class); $transformer = app(TransactionGroupTransformer::class);
$transformer->setParameters(new ParameterBag()); $transformer->setParameters(new ParameterBag());
$groupArray = $transformer->transformObject($transactionGroup); $groupArray = $transformer->transformObject($transactionGroup);
// do some calculations: // do some calculations:
$amounts = $this->getAmounts($selectedGroup); $amounts = $this->getAmounts($selectedGroup);
$accounts = $this->getAccounts($selectedGroup); $accounts = $this->getAccounts($selectedGroup);
foreach (array_keys($selectedGroup['transactions']) as $index) { foreach (array_keys($selectedGroup['transactions']) as $index) {
$selectedGroup['transactions'][$index]['tags'] = $this->repository->getTagObjects((int)$selectedGroup['transactions'][$index]['transaction_journal_id']); $selectedGroup['transactions'][$index]['tags'] = $this->repository->getTagObjects((int)$selectedGroup['transactions'][$index]['transaction_journal_id']);
@@ -142,9 +142,9 @@ class ShowController extends Controller
$logEntries[$journal['transaction_journal_id']] = $this->aleRepository->getForId(TransactionJournal::class, $journal['transaction_journal_id']); $logEntries[$journal['transaction_journal_id']] = $this->aleRepository->getForId(TransactionJournal::class, $journal['transaction_journal_id']);
} }
$events = $this->repository->getPiggyEvents($transactionGroup); $events = $this->repository->getPiggyEvents($transactionGroup);
$attachments = $this->repository->getAttachments($transactionGroup); $attachments = $this->repository->getAttachments($transactionGroup);
$links = $this->repository->getLinks($transactionGroup); $links = $this->repository->getLinks($transactionGroup);
return view( return view(
'transactions.show', 'transactions.show',
@@ -173,7 +173,7 @@ class ShowController extends Controller
foreach ($group['transactions'] as $transaction) { foreach ($group['transactions'] as $transaction) {
// add normal amount: // add normal amount:
$symbol = $transaction['currency_symbol']; $symbol = $transaction['currency_symbol'];
$amounts[$symbol] ??= [ $amounts[$symbol] ??= [
'amount' => '0', 'amount' => '0',
'symbol' => $symbol, 'symbol' => $symbol,
'decimal_places' => $transaction['currency_decimal_places'], 'decimal_places' => $transaction['currency_decimal_places'],
@@ -184,7 +184,7 @@ class ShowController extends Controller
if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string)$transaction['foreign_amount'])) { if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string)$transaction['foreign_amount'])) {
// same for foreign currency: // same for foreign currency:
$foreignSymbol = $transaction['foreign_currency_symbol']; $foreignSymbol = $transaction['foreign_currency_symbol'];
$amounts[$foreignSymbol] ??= [ $amounts[$foreignSymbol] ??= [
'amount' => '0', 'amount' => '0',
'symbol' => $foreignSymbol, 'symbol' => $foreignSymbol,
'decimal_places' => $transaction['foreign_currency_decimal_places'], 'decimal_places' => $transaction['foreign_currency_decimal_places'],
@@ -195,7 +195,7 @@ class ShowController extends Controller
if (null !== $transaction['pc_amount'] && $transaction['currency_id'] !== $this->primaryCurrency->id) { if (null !== $transaction['pc_amount'] && $transaction['currency_id'] !== $this->primaryCurrency->id) {
// same for foreign currency: // same for foreign currency:
$primarySymbol = $this->primaryCurrency->symbol; $primarySymbol = $this->primaryCurrency->symbol;
$amounts[$primarySymbol] ??= [ $amounts[$primarySymbol] ??= [
'amount' => '0', 'amount' => '0',
'symbol' => $this->primaryCurrency->symbol, 'symbol' => $this->primaryCurrency->symbol,
'decimal_places' => $this->primaryCurrency->decimal_places, 'decimal_places' => $this->primaryCurrency->decimal_places,
@@ -210,7 +210,7 @@ class ShowController extends Controller
private function getAccounts(array $group): array private function getAccounts(array $group): array
{ {
$accounts = [ $accounts = [
'source' => [], 'source' => [],
'destination' => [], 'destination' => [],
]; ];

View File

@@ -567,8 +567,8 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
'transaction_journals.transaction_currency_id', 'transaction_journals.transaction_currency_id',
'transactions.amount', 'transactions.amount',
'transactions.native_amount', 'transactions.native_amount',
'transactions.foreign_amount' 'transactions.foreign_amount',
]) ])
->toArray() ->toArray()
; ;

View File

@@ -45,8 +45,8 @@ use Illuminate\Support\Collection;
interface ALERepositoryInterface interface ALERepositoryInterface
{ {
public function getForObject(Model $model): Collection; public function getForObject(Model $model): Collection;
public function getForId(string $model, int $modelId): Collection;
public function getForId(string $model, int $modelId): Collection;
public function store(array $data): AuditLogEntry; public function store(array $data): AuditLogEntry;
} }

View File

@@ -58,15 +58,15 @@ class Amount
*/ */
public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string
{ {
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
$rounded = app('steam')->bcround($amount, $decimalPlaces); $rounded = app('steam')->bcround($amount, $decimalPlaces);
$coloured ??= true; $coloured ??= true;
$fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY); $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY);
$fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol); $fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol);
$fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); $fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces);
$fmt->setAttribute(NumberFormatter::MAX_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 (true === $coloured) {
if (1 === bccomp((string)$rounded, '0')) { if (1 === bccomp((string)$rounded, '0')) {
@@ -122,15 +122,18 @@ class Amount
if (null === $pref) { if (null === $pref) {
$res = true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled'); $res = true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled');
$instance->setPreference('convert_to_primary_no_user', $res); $instance->setPreference('convert_to_primary_no_user', $res);
return $res; return $res;
} }
return $pref; return $pref;
} }
$key = sprintf('convert_to_primary_%d', $user->id); $key = sprintf('convert_to_primary_%d', $user->id);
$pref = $instance->getPreference($key); $pref = $instance->getPreference($key);
if(null === $pref) { if (null === $pref) {
$res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled'); $res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled');
$instance->setPreference($key, $res); $instance->setPreference($key, $res);
return $res; return $res;
} }
@@ -152,7 +155,7 @@ class Amount
public function getPrimaryCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency public function getPrimaryCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty('getPrimaryCurrencyByGroup'); $cache->addProperty('getPrimaryCurrencyByGroup');
$cache->addProperty($userGroup->id); $cache->addProperty($userGroup->id);
if ($cache->has()) { if ($cache->has()) {
@@ -182,16 +185,16 @@ class Amount
*/ */
public function getAmountFromJournalObject(TransactionJournal $journal): string public function getAmountFromJournalObject(TransactionJournal $journal): string
{ {
$convertToPrimary = $this->convertToPrimary(); $convertToPrimary = $this->convertToPrimary();
$currency = $this->getPrimaryCurrency(); $currency = $this->getPrimaryCurrency();
$field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount'; $field = $convertToPrimary && $currency->id !== $journal->transaction_currency_id ? 'pc_amount' : 'amount';
/** @var null|Transaction $sourceTransaction */ /** @var null|Transaction $sourceTransaction */
$sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first(); $sourceTransaction = $journal->transactions()->where('amount', '<', 0)->first();
if (null === $sourceTransaction) { if (null === $sourceTransaction) {
return '0'; return '0';
} }
$amount = $sourceTransaction->{$field} ?? '0'; $amount = $sourceTransaction->{$field} ?? '0';
if ((int)$sourceTransaction->foreign_currency_id === $currency->id) { if ((int)$sourceTransaction->foreign_currency_id === $currency->id) {
// use foreign amount instead! // use foreign amount instead!
$amount = (string)$sourceTransaction->foreign_amount; // hard coded to be foreign amount. $amount = (string)$sourceTransaction->foreign_amount; // hard coded to be foreign amount.
@@ -239,20 +242,20 @@ class Amount
private function getLocaleInfo(): array private function getLocaleInfo(): array
{ {
// get config from preference, not from translation: // get config from preference, not from translation:
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
$array = app('steam')->getLocaleArray($locale); $array = app('steam')->getLocaleArray($locale);
setlocale(LC_MONETARY, $array); setlocale(LC_MONETARY, $array);
$info = localeconv(); $info = localeconv();
// correct variables // correct variables
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes');
$info['p_cs_precedes'] = $this->getLocaleField($info, 'p_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['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space');
$info['p_sep_by_space'] = $this->getLocaleField($info, 'p_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_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL);
$info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); $info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL);
@@ -284,11 +287,11 @@ class Amount
// there are five possible positions for the "+" or "-" sign (if it is even used) // there are five possible positions for the "+" or "-" sign (if it is even used)
// pos_a and pos_e could be the ( and ) symbol. // pos_a and pos_e could be the ( and ) symbol.
$posA = ''; // before everything $posA = ''; // before everything
$posB = ''; // before currency symbol $posB = ''; // before currency symbol
$posC = ''; // after currency symbol $posC = ''; // after currency symbol
$posD = ''; // before amount $posD = ''; // before amount
$posE = ''; // after everything $posE = ''; // after everything
// format would be (currency before amount) // format would be (currency before amount)
// AB%sC_D%vE // AB%sC_D%vE
@@ -330,9 +333,9 @@ class Amount
} }
if ($csPrecedes) { if ($csPrecedes) {
return $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE; return $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE;
} }
return $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE; return $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE;
} }
} }

View File

@@ -56,7 +56,7 @@ trait ChartGeneration
$cache->addProperty($accounts); $cache->addProperty($accounts);
$cache->addProperty($convertToPrimary); $cache->addProperty($convertToPrimary);
if ($cache->has()) { if ($cache->has()) {
return $cache->get(); return $cache->get();
} }
Log::debug('Regenerate chart.account.account-balance-chart from scratch.'); Log::debug('Regenerate chart.account.account-balance-chart from scratch.');
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();

View File

@@ -83,10 +83,10 @@ trait PeriodOverview
Timer::start('account-period-total'); Timer::start('account-period-total');
$this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class);
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache // properties for cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('account-show-period-entries'); $cache->addProperty('account-show-period-entries');
@@ -96,35 +96,35 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
$spent = []; $spent = [];
$earned = []; $earned = [];
$transferredAway = []; $transferredAway = [];
$transferredIn = []; $transferredIn = [];
// run a custom query because doing this with the collector is MEGA slow. // run a custom query because doing this with the collector is MEGA slow.
$transactions = $this->accountRepository->periodCollection($account, $start, $end); $transactions = $this->accountRepository->periodCollection($account, $start, $end);
// loop dates // loop dates
Log::debug(sprintf('Count of loops: %d', count($dates))); Log::debug(sprintf('Count of loops: %d', count($dates)));
$loops = 0; $loops = 0;
// stop after 10 loops for memory reasons. // stop after 10 loops for memory reasons.
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$title = Navigation::periodShow($currentDate['start'], $currentDate['period']); $title = Navigation::periodShow($currentDate['start'], $currentDate['period']);
[$transactions, $spent] = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $currentDate['start'], $currentDate['end']); [$transactions, $spent] = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $currentDate['start'], $currentDate['end']);
[$transactions, $earned] = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $currentDate['start'], $currentDate['end']); [$transactions, $earned] = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $currentDate['start'], $currentDate['end']);
[$transactions, $transferredAway] = $this->filterTransfers('away', $transactions, $currentDate['start'], $currentDate['end']); [$transactions, $transferredAway] = $this->filterTransfers('away', $transactions, $currentDate['start'], $currentDate['end']);
[$transactions, $transferredIn] = $this->filterTransfers('in', $transactions, $currentDate['start'], $currentDate['end']); [$transactions, $transferredIn] = $this->filterTransfers('in', $transactions, $currentDate['start'], $currentDate['end']);
$entries[] $entries[]
= [ = [
'title' => $title, 'title' => $title,
'route' => route('accounts.show', [$account->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), 'route' => route('accounts.show', [$account->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferredAway) + count($transferredIn), 'total_transactions' => count($spent) + count($earned) + count($transferredAway) + count($transferredIn),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred_away' => $this->groupByCurrency($transferredAway), 'transferred_away' => $this->groupByCurrency($transferredAway),
'transferred_in' => $this->groupByCurrency($transferredIn), 'transferred_in' => $this->groupByCurrency($transferredIn),
]; ];
++$loops; ++$loops;
} }
$cache->store($entries); $cache->store($entries);
@@ -136,11 +136,11 @@ trait PeriodOverview
private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array
{ {
$result = []; $result = [];
$filtered = []; $filtered = [];
/** /**
* @var int $index * @var int $index
* @var array $item * @var array $item
*/ */
foreach ($transactions as $index => $item) { foreach ($transactions as $index => $item) {
@@ -150,7 +150,7 @@ trait PeriodOverview
$result[] = $item; $result[] = $item;
unset($transactions[$index]); unset($transactions[$index]);
} }
if(!$fits) { if (!$fits) {
$filtered[] = $item; $filtered[] = $item;
} }
} }
@@ -160,11 +160,11 @@ trait PeriodOverview
private function filterTransfers(string $direction, array $transactions, Carbon $start, Carbon $end): array private function filterTransfers(string $direction, array $transactions, Carbon $start, Carbon $end): array
{ {
$result = []; $result = [];
$filtered = []; $filtered = [];
/** /**
* @var int $index * @var int $index
* @var array $item * @var array $item
*/ */
foreach ($transactions as $index => $item) { foreach ($transactions as $index => $item) {
@@ -172,10 +172,12 @@ trait PeriodOverview
if ($date >= $start && $date <= $end) { if ($date >= $start && $date <= $end) {
if ('away' === $direction && -1 === bccomp((string)$item['amount'], '0')) { if ('away' === $direction && -1 === bccomp((string)$item['amount'], '0')) {
$result[] = $item; $result[] = $item;
continue; continue;
} }
if ('in' === $direction && 1 === bccomp((string)$item['amount'], '0')) { if ('in' === $direction && 1 === bccomp((string)$item['amount'], '0')) {
$result[] = $item; $result[] = $item;
continue; continue;
} }
$filtered[] = $item; $filtered[] = $item;
@@ -191,13 +193,13 @@ trait PeriodOverview
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
$currencyCode = $journal['currency_code']; $currencyCode = $journal['currency_code'];
$currencyName = $journal['currency_name']; $currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol']; $currencySymbol = $journal['currency_symbol'];
$currencyDecimalPlaces = $journal['currency_decimal_places']; $currencyDecimalPlaces = $journal['currency_decimal_places'];
$foreignCurrencyId = $journal['foreign_currency_id']; $foreignCurrencyId = $journal['foreign_currency_id'];
$amount = $journal['amount'] ?? '0'; $amount = $journal['amount'] ?? '0';
if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) { if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId !== $this->primaryCurrency->id) {
$amount = $journal['pc_amount'] ?? '0'; $amount = $journal['pc_amount'] ?? '0';
@@ -240,11 +242,11 @@ trait PeriodOverview
*/ */
protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array
{ {
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for entries with their amounts. // properties for entries with their amounts.
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($range); $cache->addProperty($range);
@@ -256,32 +258,32 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
// collect all expenses in this period: // collect all expenses in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setCategory($category); $collector->setCategory($category);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals(); $earnedSet = $collector->getExtractedJournals();
// collect all income in this period: // collect all income in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setCategory($category); $collector->setCategory($category);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals(); $spentSet = $collector->getExtractedJournals();
// collect all transfers in this period: // collect all transfers in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setCategory($category); $collector->setCategory($category);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
$transferSet = $collector->getExtractedJournals(); $transferSet = $collector->getExtractedJournals();
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
@@ -289,17 +291,17 @@ trait PeriodOverview
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
$entries[] $entries[]
= [ = [
'transactions' => 0, 'transactions' => 0,
'title' => $title, 'title' => $title,
'route' => route( 'route' => route(
'categories.show', 'categories.show',
[$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] [$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]
), ),
'total_transactions' => count($spent) + count($earned) + count($transferred), 'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred), 'transferred' => $this->groupByCurrency($transferred),
]; ];
} }
$cache->store($entries); $cache->store($entries);
@@ -332,11 +334,11 @@ trait PeriodOverview
*/ */
protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array
{ {
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($this->convertToPrimary); $cache->addProperty($this->convertToPrimary);
@@ -347,28 +349,28 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
// get all expenses without a budget. // get all expenses without a budget.
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']); $set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']);
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
$entries[] $entries[]
= [ = [
'title' => $title, 'title' => $title,
'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), 'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($set), 'total_transactions' => count($set),
'spent' => $this->groupByCurrency($set), 'spent' => $this->groupByCurrency($set),
'earned' => [], 'earned' => [],
'transferred_away' => [], 'transferred_away' => [],
'transferred_in' => [], 'transferred_in' => [],
]; ];
} }
$cache->store($entries); $cache->store($entries);
@@ -385,38 +387,38 @@ trait PeriodOverview
protected function getNoCategoryPeriodOverview(Carbon $theDate): array protected function getNoCategoryPeriodOverview(Carbon $theDate): array
{ {
app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d')));
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
$first = $this->journalRepos->firstNull(); $first = $this->journalRepos->firstNull();
$start = null === $first ? new Carbon() : $first->date; $start = null === $first ? new Carbon() : $first->date;
$end = clone $theDate; $end = clone $theDate;
$end = Navigation::endOfPeriod($end, $range); $end = Navigation::endOfPeriod($end, $range);
app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d')));
app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d')));
// properties for cache // properties for cache
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
// collect all expenses in this period: // collect all expenses in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->withoutCategory(); $collector->withoutCategory();
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals(); $earnedSet = $collector->getExtractedJournals();
// collect all income in this period: // collect all income in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->withoutCategory(); $collector->withoutCategory();
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals(); $spentSet = $collector->getExtractedJournals();
// collect all transfers in this period: // collect all transfers in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->withoutCategory(); $collector->withoutCategory();
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
@@ -430,13 +432,13 @@ trait PeriodOverview
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
$entries[] $entries[]
= [ = [
'title' => $title, 'title' => $title,
'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferred), 'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred), 'transferred' => $this->groupByCurrency($transferred),
]; ];
} }
app('log')->debug('End of loops'); app('log')->debug('End of loops');
@@ -450,11 +452,11 @@ trait PeriodOverview
*/ */
protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags. protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags.
{ {
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache // properties for cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('tag-period-entries'); $cache->addProperty('tag-period-entries');
@@ -464,37 +466,37 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
// collect all expenses in this period: // collect all expenses in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTag($tag); $collector->setTag($tag);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]); $collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals(); $earnedSet = $collector->getExtractedJournals();
// collect all income in this period: // collect all income in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTag($tag); $collector->setTag($tag);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals(); $spentSet = $collector->getExtractedJournals();
// collect all transfers in this period: // collect all transfers in this period:
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTag($tag); $collector->setTag($tag);
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]); $collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
$transferSet = $collector->getExtractedJournals(); $transferSet = $collector->getExtractedJournals();
// filer all of them: // filer all of them:
$earnedSet = $this->filterJournalsByTag($earnedSet, $tag); $earnedSet = $this->filterJournalsByTag($earnedSet, $tag);
$spentSet = $this->filterJournalsByTag($spentSet, $tag); $spentSet = $this->filterJournalsByTag($spentSet, $tag);
$transferSet = $this->filterJournalsByTag($transferSet, $tag); $transferSet = $this->filterJournalsByTag($transferSet, $tag);
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
@@ -503,17 +505,17 @@ trait PeriodOverview
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
$entries[] $entries[]
= [ = [
'transactions' => 0, 'transactions' => 0,
'title' => $title, 'title' => $title,
'route' => route( 'route' => route(
'tags.show', 'tags.show',
[$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')] [$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]
), ),
'total_transactions' => count($spent) + count($earned) + count($transferred), 'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred), 'transferred' => $this->groupByCurrency($transferred),
]; ];
} }
return $entries; return $entries;
@@ -523,7 +525,7 @@ trait PeriodOverview
{ {
$return = []; $return = [];
foreach ($set as $entry) { foreach ($set as $entry) {
$found = false; $found = false;
/** @var array $localTag */ /** @var array $localTag */
foreach ($entry['tags'] as $localTag) { foreach ($entry['tags'] as $localTag) {
@@ -545,12 +547,12 @@ trait PeriodOverview
*/ */
protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array
{ {
$range = Navigation::getViewRange(true); $range = Navigation::getViewRange(true);
$types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType)); $types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache // properties for cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('transactions-period-entries'); $cache->addProperty('transactions-period-entries');
@@ -560,16 +562,16 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
$entries = []; $entries = [];
$spent = []; $spent = [];
$earned = []; $earned = [];
$transferred = []; $transferred = [];
// collect all journals in this period (regardless of type) // collect all journals in this period (regardless of type)
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($start, $end); $collector->setTypes($types)->setRange($start, $end);
$genericSet = $collector->getExtractedJournals(); $genericSet = $collector->getExtractedJournals();
$loops = 0; $loops = 0;
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']); $title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
@@ -587,14 +589,14 @@ trait PeriodOverview
} }
} }
$entries[] $entries[]
= [ = [
'title' => $title, 'title' => $title,
'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), 'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferred), 'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred), 'transferred' => $this->groupByCurrency($transferred),
]; ];
++$loops; ++$loops;
} }

View File

@@ -284,9 +284,9 @@ class Preferences
*/ */
public function lastActivity(): string public function lastActivity(): string
{ {
$instance = PreferencesSingleton::getInstance(); $instance = PreferencesSingleton::getInstance();
$pref = $instance->getPreference('last_activity'); $pref = $instance->getPreference('last_activity');
if(null !== $pref) { if (null !== $pref) {
// Log::debug(sprintf('Found last activity in singleton: %s', $pref)); // Log::debug(sprintf('Found last activity in singleton: %s', $pref));
return $pref; return $pref;
} }
@@ -299,8 +299,9 @@ class Preferences
if (is_array($lastActivity)) { if (is_array($lastActivity)) {
$lastActivity = implode(',', $lastActivity); $lastActivity = implode(',', $lastActivity);
} }
$setting = hash('sha256', (string) $lastActivity); $setting = hash('sha256', (string) $lastActivity);
$instance->setPreference('last_activity', $setting); $instance->setPreference('last_activity', $setting);
return $setting; return $setting;
} }

View File

@@ -1,28 +1,31 @@
<?php <?php
declare(strict_types=1);
namespace FireflyIII\Support\Singleton; namespace FireflyIII\Support\Singleton;
class PreferencesSingleton class PreferencesSingleton
{ {
private static ?PreferencesSingleton $instance = null; private static ?PreferencesSingleton $instance = null;
private array $preferences = []; private array $preferences = [];
private function __construct() private function __construct()
{ {
// Private constructor to prevent direct instantiation. // Private constructor to prevent direct instantiation.
} }
public static function getInstance(): PreferencesSingleton public static function getInstance(): self
{ {
if (self::$instance === null) { if (null === self::$instance) {
self::$instance = new PreferencesSingleton(); self::$instance = new self();
} }
return self::$instance; return self::$instance;
} }
public function resetPreferences(): void { public function resetPreferences(): void
{
$this->preferences = []; $this->preferences = [];
} }
@@ -35,5 +38,4 @@ class PreferencesSingleton
{ {
return $this->preferences[$key] ?? null; return $this->preferences[$key] ?? null;
} }
} }

22
composer.lock generated
View File

@@ -939,16 +939,16 @@
}, },
{ {
"name": "filp/whoops", "name": "filp/whoops",
"version": "2.18.3", "version": "2.18.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/filp/whoops.git", "url": "https://github.com/filp/whoops.git",
"reference": "59a123a3d459c5a23055802237cb317f609867e5" "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5", "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d",
"reference": "59a123a3d459c5a23055802237cb317f609867e5", "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -998,7 +998,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/filp/whoops/issues", "issues": "https://github.com/filp/whoops/issues",
"source": "https://github.com/filp/whoops/tree/2.18.3" "source": "https://github.com/filp/whoops/tree/2.18.4"
}, },
"funding": [ "funding": [
{ {
@@ -1006,7 +1006,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-06-16T00:02:10+00:00" "time": "2025-08-08T12:00:00+00:00"
}, },
{ {
"name": "firebase/php-jwt", "name": "firebase/php-jwt",
@@ -1879,16 +1879,16 @@
}, },
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v12.22.0", "version": "v12.22.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/framework.git", "url": "https://github.com/laravel/framework.git",
"reference": "6ab00c913ef6ec6fad0bd506f7452c0bb9e792c3" "reference": "d33ee45184126f32f593d4b809a846ed88a1dc43"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/6ab00c913ef6ec6fad0bd506f7452c0bb9e792c3", "url": "https://api.github.com/repos/laravel/framework/zipball/d33ee45184126f32f593d4b809a846ed88a1dc43",
"reference": "6ab00c913ef6ec6fad0bd506f7452c0bb9e792c3", "reference": "d33ee45184126f32f593d4b809a846ed88a1dc43",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2090,7 +2090,7 @@
"issues": "https://github.com/laravel/framework/issues", "issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework" "source": "https://github.com/laravel/framework"
}, },
"time": "2025-08-07T13:49:53+00:00" "time": "2025-08-08T13:58:03+00:00"
}, },
{ {
"name": "laravel/passport", "name": "laravel/passport",

View File

@@ -79,7 +79,7 @@ return [
// see cer.php for exchange rates feature flag. // see cer.php for exchange rates feature flag.
], ],
'version' => 'develop/2025-08-08', 'version' => 'develop/2025-08-08',
'build_time' => 1754630161, 'build_time' => 1754679717,
'api_version' => '2.1.0', // field is no longer used. 'api_version' => '2.1.0', // field is no longer used.
'db_version' => 26, 'db_version' => 26,

12
package-lock.json generated
View File

@@ -3148,9 +3148,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "24.2.0", "version": "24.2.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -4486,9 +4486,9 @@
} }
}, },
"node_modules/caniuse-lite": { "node_modules/caniuse-lite": {
"version": "1.0.30001731", "version": "1.0.30001733",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001733.tgz",
"integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "integrity": "sha512-e4QKw/O2Kavj2VQTKZWrwzkt3IxOmIlU6ajRb6LP64LHpBo1J67k2Hi4Vu/TgJWsNtynurfS0uK3MaUTCPfu5Q==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {

View File

@@ -24,10 +24,6 @@ declare(strict_types=1);
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
/* /*
* ____ ____ __ .______ ______ __ __ .___________. _______ _______. * ____ ____ __ .______ ______ __ __ .___________. _______ _______.
* \ \ / / /_ | | _ \ / __ \ | | | | | || ____| / | * \ \ / / /_ | | _ \ / __ \ | | | | | || ____| / |