Merge pull request #9952 from firefly-iii/speed-up-account-show

Speed up account show
This commit is contained in:
James Cole
2025-03-09 09:57:24 +01:00
committed by GitHub
8 changed files with 316 additions and 172 deletions

View File

@@ -29,10 +29,11 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Debug\Timer;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Controllers\PeriodOverview;
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -82,7 +83,7 @@ class ShowController extends Controller
public function show(Request $request, Account $account, ?Carbon $start = null, ?Carbon $end = null)
{
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
if (!$this->isEditableAccount($account)) {
return $this->redirectAccountToAccount($account);
@@ -114,33 +115,55 @@ class ShowController extends Controller
$subTitle = (string) trans('firefly.journals_in_period_for_account', ['name' => $account->name, 'start' => $fStart, 'end' => $fEnd]);
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$firstTransaction = $this->repository->oldestJournalDate($account) ?? $start;
Log::debug('Start period overview');
Timer::start('period-overview');
$periods = $this->getAccountPeriodOverview($account, $firstTransaction, $end);
Log::debug('End period overview');
Timer::stop('period-overview');
// if layout = v2, overrule the page title.
if ('v1' !== config('view.layout')) {
$subTitle = (string) trans('firefly.all_journals_for_account', ['name' => $account->name]);
}
Log::debug('Collect transactions');
Timer::start('collection');
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page)->withAccountInformation()->withCategoryInformation()->setRange($start, $end);
$collector = app(GroupCollectorInterface::class);
$collector
->setAccounts(new Collection([$account]))
->setLimit($pageSize)
->setPage($page)
->withAPIInformation()
->setRange($start, $end);
// this search will not include transaction groups where this asset account (or liability)
// is just part of ONE of the journals. To force this:
$collector->setExpandGroupSearch(true);
$groups = $collector->getPaginatedGroups();
Log::debug('End collect transactions');
Timer::stop('collection');
// enrich data in arrays.
// enrich
// $enrichment = new TransactionGroupEnrichment();
// $enrichment->setUser(auth()->user());
// $groups->setCollection($enrichment->enrich($groups->getCollection()));
$groups = $collector->getPaginatedGroups();
$groups->setPath(route('accounts.show', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]));
$showAll = false;
$showAll = false;
// correct
$now = today()->endOfDay();
$now = today()->endOfDay();
if ($now->gt($end) || $now->lt($start)) {
$now = $end;
}
Log::debug(sprintf('show: Call finalAccountBalance with date/time "%s"', $now->toIso8601String()));
$balances = Steam::filterAccountBalance(Steam::finalAccountBalance($account, $now), $account, $this->convertToNative, $accountCurrency);
$balances = Steam::filterAccountBalance(Steam::finalAccountBalance($account, $now), $account, $this->convertToNative, $accountCurrency);
return view(
'accounts.show',
@@ -184,7 +207,7 @@ class ShowController extends Controller
$today = today(config('app.timezone'));
$accountCurrency = $this->repository->getAccountCurrency($account);
$start = $this->repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth();
$subTitleIcon = config('firefly.subIconsByIdentifier.'.$account->accountType->type);
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$currency = $this->repository->getAccountCurrency($account) ?? $this->defaultCurrency;
@@ -194,20 +217,20 @@ class ShowController extends Controller
$end->endOfDay();
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->setLimit($pageSize)->setPage($page)->withAccountInformation()->withCategoryInformation();
// this search will not include transaction groups where this asset account (or liability)
// is just part of ONE of the journals. To force this:
$collector->setExpandGroupSearch(true);
$groups = $collector->getPaginatedGroups();
$groups = $collector->getPaginatedGroups();
$groups->setPath(route('accounts.show.all', [$account->id]));
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$showAll = true;
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
$showAll = true;
// correct
Log::debug(sprintf('showAll: Call finalAccountBalance with date/time "%s"', $end->toIso8601String()));
$balances = Steam::filterAccountBalance(Steam::finalAccountBalance($account, $end), $account, $this->convertToNative, $accountCurrency);
$balances = Steam::filterAccountBalance(Steam::finalAccountBalance($account, $end), $account, $this->convertToNative, $accountCurrency);
return view(
'accounts.show',

View File

@@ -641,4 +641,34 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
return $factory->create($data);
}
#[\Override] public function periodCollection(Account $account, Carbon $start, Carbon $end): array
{
return $account->transactions()
->leftJoin('transaction_journals','transaction_journals.id','=','transactions.transaction_journal_id')
->leftJoin('transaction_types','transaction_types.id','=','transaction_journals.transaction_type_id')
->leftJoin('transaction_currencies','transaction_currencies.id','=','transactions.transaction_currency_id')
->leftJoin('transaction_currencies as foreign_currencies','foreign_currencies.id','=','transactions.foreign_currency_id')
->where('transaction_journals.date','>=',$start)
->where('transaction_journals.date','<=',$end)
->get([
// currencies
'transaction_currencies.id as currency_id',
'transaction_currencies.code as currency_code',
'transaction_currencies.name as currency_name',
'transaction_currencies.symbol as currency_symbol',
'transaction_currencies.decimal_places as currency_decimal_places',
// foreign
'foreign_currencies.id as foreign_currency_id',
'foreign_currencies.code as foreign_currency_code',
'foreign_currencies.name as foreign_currency_name',
'foreign_currencies.symbol as foreign_currency_symbol',
'foreign_currencies.decimal_places as foreign_decimal_places',
// fields
'transaction_journals.date', 'transaction_types.type', 'transaction_journals.transaction_currency_id', 'transactions.amount'])
->toArray();
}
}

View File

@@ -71,6 +71,8 @@ interface AccountRepositoryInterface
public function findByName(string $name, array $types): ?Account;
public function periodCollection(Account $account, Carbon $start, Carbon $end): array;
public function getAccountBalances(Account $account): Collection;
public function getAccountCurrency(Account $account): ?TransactionCurrency;

View File

@@ -305,15 +305,21 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte
public function getPiggyBanks(): Collection
{
return PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
->where('accounts.user_id', $this->user->id)
->with(
[
'objectGroups',
]
)
->orderBy('piggy_banks.order', 'ASC')->distinct()->get(['piggy_banks.*']);
$query = PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id');
if (null === $this->user) {
$query->where('accounts.user_group_id', $this->userGroup->id);
}
if (null !== $this->user) {
$query->where('accounts.user_id', $this->user->id);
}
return $query
->with(
[
'objectGroups',
]
)
->orderBy('piggy_banks.order', 'ASC')->distinct()->get(['piggy_banks.*']);
}
public function getRepetition(PiggyBank $piggyBank, bool $overrule = false): ?PiggyBankRepetition

View File

@@ -0,0 +1,46 @@
<?php
/*
* Timer.php
* Copyright (c) 2025 james@firefly-iii.org.
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
declare(strict_types=1);
namespace FireflyIII\Support\Debug;
use Illuminate\Support\Facades\Log;
class Timer
{
private static array $times = [];
public static function start(string $title): void
{
self::$times[$title] = microtime(true);
}
public static function stop(string $title): void
{
$start = self::$times[$title] ?? 0;
$end = microtime(true);
$diff = $end - $start;
unset(self::$times[$title]);
Log::debug(sprintf('Timer "%s" took %f seconds', $title, $diff));
}
}

View File

@@ -31,9 +31,12 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Debug\Timer;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* Trait PeriodOverview.
@@ -65,6 +68,7 @@ use Illuminate\Support\Collection;
trait PeriodOverview
{
protected JournalRepositoryInterface $journalRepos;
protected AccountRepositoryInterface $accountRepository;
/**
* This method returns "period entries", so nov-2015, dec-2015, etc etc (this depends on the users session range)
@@ -75,11 +79,13 @@ trait PeriodOverview
*/
protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array
{
$range = app('navigation')->getViewRange(true);
Timer::start('account-period-total');
$this->accountRepository = app(AccountRepositoryInterface::class);
$range = app('navigation')->getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('account-show-period-entries');
@@ -89,56 +95,75 @@ trait PeriodOverview
}
/** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
// collect all expenses in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]));
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals();
// collect all income in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]));
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals();
// collect all transfers in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]));
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
$transferSet = $collector->getExtractedJournals();
// run a custom query because doing this with the collector is MEGA slow.
$transactions = $this->accountRepository->periodCollection($account, $start, $end);
// loop dates
foreach ($dates as $currentDate) {
$title = app('navigation')->periodShow($currentDate['start'], $currentDate['period']);
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
$transferredAway = $this->filterTransferredAway($account, $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']));
$transferredIn = $this->filterTransferredIn($account, $this->filterJournalsByDate($transferSet, $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, $transferredAway] = $this->filterTransfers('away',$transactions, $currentDate['start'], $currentDate['end']);
[$transactions, $transferredIn] = $this->filterTransfers('in',$transactions, $currentDate['start'], $currentDate['end']);
$entries[]
= [
'title' => $title,
'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),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred_away' => $this->groupByCurrency($transferredAway),
'transferred_in' => $this->groupByCurrency($transferredIn),
];
= [
'title' => $title,
'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),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred_away' => $this->groupByCurrency($transferredAway),
'transferred_in' => $this->groupByCurrency($transferredIn),
];
}
$cache->store($entries);
Timer::stop('account-period-total');
return $entries;
}
private function filterTransfers(string $direction, array $transactions, Carbon $start, Carbon $end): array
{
$result = [];
/**
* @var int $index
* @var array $item
*/
foreach ($transactions as $index => $item) {
$date = Carbon::parse($item['date']);
if ($date >= $start && $date <= $end) {
if ($direction === 'away' && bccomp($item['amount'], '0') === -1) {
$result[] = $item;
unset($transactions[$index]);
}
if ($direction === 'in' && bccomp($item['amount'], '0') === 1) {
$result[] = $item;
unset($transactions[$index]);
}
}
}
return [$transactions, $result];
}
private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array
{
$result = [];
/**
* @var int $index
* @var array $item
*/
foreach ($transactions as $index => $item) {
$date = Carbon::parse($item['date']);
if($item['type'] === $type->value && $date >= $start && $date <= $end) {
$result[] = $item;
unset($transactions[$index]);
}
}
return [$transactions, $result];
}
/**
* Filter a list of journals by a set of dates, and then group them by currency.
@@ -197,13 +222,13 @@ trait PeriodOverview
/** @var array $journal */
foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id'];
$currencyCode = $journal['currency_code'];
$currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$foreignCurrencyId = $journal['foreign_currency_id'];
$amount = $journal['amount'] ?? '0';
$currencyId = (int) $journal['currency_id'];
$currencyCode = $journal['currency_code'];
$currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol'];
$currencyDecimalPlaces = $journal['currency_decimal_places'];
$foreignCurrencyId = $journal['foreign_currency_id'];
$amount = $journal['amount'] ?? '0';
if ($this->convertToNative && $currencyId !== $this->defaultCurrency->id && $foreignCurrencyId !== $this->defaultCurrency->id) {
$amount = $journal['native_amount'] ?? '0';
@@ -246,11 +271,11 @@ trait PeriodOverview
*/
protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array
{
$range = app('navigation')->getViewRange(true);
$range = app('navigation')->getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for entries with their amounts.
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($range);
@@ -262,32 +287,32 @@ trait PeriodOverview
}
/** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
// collect all expenses in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setCategory($category);
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals();
$earnedSet = $collector->getExtractedJournals();
// collect all income in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setCategory($category);
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals();
$spentSet = $collector->getExtractedJournals();
// collect all transfers in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setCategory($category);
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
$transferSet = $collector->getExtractedJournals();
$transferSet = $collector->getExtractedJournals();
foreach ($dates as $currentDate) {
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
@@ -295,17 +320,17 @@ trait PeriodOverview
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
$entries[]
= [
'transactions' => 0,
'title' => $title,
'route' => route(
'categories.show',
[$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]
),
'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
'transactions' => 0,
'title' => $title,
'route' => route(
'categories.show',
[$category->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]
),
'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
}
$cache->store($entries);
@@ -321,11 +346,11 @@ trait PeriodOverview
*/
protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array
{
$range = app('navigation')->getViewRange(true);
$range = app('navigation')->getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty($this->convertToNative);
@@ -336,28 +361,28 @@ trait PeriodOverview
}
/** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
// get all expenses without a budget.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$journals = $collector->getExtractedJournals();
$journals = $collector->getExtractedJournals();
foreach ($dates as $currentDate) {
$set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']);
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
$entries[]
= [
'title' => $title,
'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($set),
'spent' => $this->groupByCurrency($set),
'earned' => [],
'transferred_away' => [],
'transferred_in' => [],
];
'title' => $title,
'route' => route('budgets.no-budget', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($set),
'spent' => $this->groupByCurrency($set),
'earned' => [],
'transferred_away' => [],
'transferred_in' => [],
];
}
$cache->store($entries);
@@ -374,38 +399,38 @@ trait PeriodOverview
protected function getNoCategoryPeriodOverview(Carbon $theDate): array
{
app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d')));
$range = app('navigation')->getViewRange(true);
$first = $this->journalRepos->firstNull();
$start = null === $first ? new Carbon() : $first->date;
$end = clone $theDate;
$end = app('navigation')->endOfPeriod($end, $range);
$range = app('navigation')->getViewRange(true);
$first = $this->journalRepos->firstNull();
$start = null === $first ? new Carbon() : $first->date;
$end = clone $theDate;
$end = app('navigation')->endOfPeriod($end, $range);
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')));
// properties for cache
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
// collect all expenses in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->withoutCategory();
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals();
$earnedSet = $collector->getExtractedJournals();
// collect all income in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->withoutCategory();
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals();
$spentSet = $collector->getExtractedJournals();
// collect all transfers in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->withoutCategory();
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
@@ -419,13 +444,13 @@ trait PeriodOverview
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
$entries[]
= [
'title' => $title,
'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),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
'title' => $title,
'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),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
}
app('log')->debug('End of loops');
@@ -439,11 +464,11 @@ trait PeriodOverview
*/
protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags.
{
$range = app('navigation')->getViewRange(true);
$range = app('navigation')->getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('tag-period-entries');
@@ -453,37 +478,37 @@ trait PeriodOverview
}
/** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
// collect all expenses in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setTag($tag);
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::DEPOSIT->value]);
$earnedSet = $collector->getExtractedJournals();
$earnedSet = $collector->getExtractedJournals();
// collect all income in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setTag($tag);
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
$spentSet = $collector->getExtractedJournals();
$spentSet = $collector->getExtractedJournals();
// collect all transfers in this period:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setTag($tag);
$collector->setRange($start, $end);
$collector->setTypes([TransactionTypeEnum::TRANSFER->value]);
$transferSet = $collector->getExtractedJournals();
$transferSet = $collector->getExtractedJournals();
// filer all of them:
$earnedSet = $this->filterJournalsByTag($earnedSet, $tag);
$spentSet = $this->filterJournalsByTag($spentSet, $tag);
$transferSet = $this->filterJournalsByTag($transferSet, $tag);
$earnedSet = $this->filterJournalsByTag($earnedSet, $tag);
$spentSet = $this->filterJournalsByTag($spentSet, $tag);
$transferSet = $this->filterJournalsByTag($transferSet, $tag);
foreach ($dates as $currentDate) {
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
@@ -492,17 +517,17 @@ trait PeriodOverview
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
$entries[]
= [
'transactions' => 0,
'title' => $title,
'route' => route(
'tags.show',
[$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]
),
'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
'transactions' => 0,
'title' => $title,
'route' => route(
'tags.show',
[$tag->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]
),
'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
}
return $entries;
@@ -512,7 +537,7 @@ trait PeriodOverview
{
$return = [];
foreach ($set as $entry) {
$found = false;
$found = false;
/** @var array $localTag */
foreach ($entry['tags'] as $localTag) {
@@ -534,12 +559,12 @@ trait PeriodOverview
*/
protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array
{
$range = app('navigation')->getViewRange(true);
$types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
$range = app('navigation')->getViewRange(true);
$types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache
$cache = new CacheProperties();
$cache = new CacheProperties();
$cache->addProperty($start);
$cache->addProperty($end);
$cache->addProperty('transactions-period-entries');
@@ -549,13 +574,13 @@ trait PeriodOverview
}
/** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
$dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = [];
// collect all journals in this period (regardless of type)
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setTypes($types)->setRange($start, $end);
$genericSet = $collector->getExtractedJournals();
$genericSet = $collector->getExtractedJournals();
foreach ($dates as $currentDate) {
$spent = [];
@@ -574,14 +599,14 @@ trait PeriodOverview
$transferred = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
}
$entries[]
= [
'title' => $title,
'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),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
= [
'title' => $title,
'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),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
}
return $entries;

View File

@@ -72,7 +72,7 @@ class Preferences
public function getForUser(User $user, string $name, null|array|bool|int|string $default = null): ?Preference
{
Log::debug(sprintf('getForUser(#%d, "%s")', $user->id, $name));
//Log::debug(sprintf('getForUser(#%d, "%s")', $user->id, $name));
// don't care about user group ID, except for some specific preferences.
$userGroupId = $this->getUserGroupId($user, $name);
$query = Preference::where('user_id', $user->id)->where('name', $name);
@@ -90,7 +90,7 @@ class Preferences
}
if (null !== $preference) {
Log::debug(sprintf('Found preference #%d for user #%d: %s', $preference->id, $user->id, $name));
//Log::debug(sprintf('Found preference #%d for user #%d: %s', $preference->id, $user->id, $name));
return $preference;
}

View File

@@ -32,7 +32,11 @@
<td style="width:33%;">{{ 'earned'|_ }}</td>
<td style="text-align: right;">
<span title="{{ entry.count }}">
{{ formatAmountBySymbol(entry.amount*-1, entry.currency_symbol, entry.currency_decimal_places) }}
{% if entry.amount < 0 %}
{{ formatAmountBySymbol(entry.amount*-1, entry.currency_symbol, entry.currency_decimal_places) }}
{% else %}
{{ formatAmountBySymbol(entry.amount, entry.currency_symbol, entry.currency_decimal_places) }}
{% endif %}
</span>
</td>
</tr>
@@ -58,7 +62,11 @@
<td style="width:33%;">{{ 'transferred_away'|_ }}</td>
<td style="text-align: right;">
<span title="{{ entry.count }}">
{{ formatAmountBySymbol(entry.amount*-1, entry.currency_symbol, entry.currency_decimal_places) }}
{% if entry.amount < 0 %}
{{ formatAmountBySymbol(entry.amount, entry.currency_symbol, entry.currency_decimal_places) }}
{% else %}
{{ formatAmountBySymbol(entry.amount*-1, entry.currency_symbol, entry.currency_decimal_places) }}
{% endif %}
</span>
</td>
</tr>
@@ -71,7 +79,11 @@
<td style="width:33%;">{{ 'transferred_in'|_ }}</td>
<td style="text-align: right;">
<span title="{{ entry.count }}">
{{ formatAmountBySymbol(entry.amount*-1, entry.currency_symbol, entry.currency_decimal_places) }}
{% if entry.amount < 0 %}
{{ formatAmountBySymbol(entry.amount*-1, entry.currency_symbol, entry.currency_decimal_places) }}
{% else %}
{{ formatAmountBySymbol(entry.amount, entry.currency_symbol, entry.currency_decimal_places) }}
{% endif %}
</span>
</td>
</tr>