Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop

# Conflicts:
#	app/Models/PeriodStatistic.php
This commit is contained in:
James Cole
2025-09-26 19:48:46 +02:00
103 changed files with 1409 additions and 1334 deletions

View File

@@ -62,14 +62,14 @@ class StoredGroupEventHandler
} }
Log::debug('Now in StoredGroupEventHandler::processRules()'); Log::debug('Now in StoredGroupEventHandler::processRules()');
$journals = $storedGroupEvent->transactionGroup->transactionJournals; $journals = $storedGroupEvent->transactionGroup->transactionJournals;
$array = []; $array = [];
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$array[] = $journal->id; $array[] = $journal->id;
} }
$journalIds = implode(',', $array); $journalIds = implode(',', $array);
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
// collect rules: // collect rules:
@@ -78,10 +78,10 @@ class StoredGroupEventHandler
// add the groups to the rule engine. // add the groups to the rule engine.
// it should run the rules in the group and cancel the group if necessary. // it should run the rules in the group and cancel the group if necessary.
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal');
// create and fire rule engine. // create and fire rule engine.
$newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine->setUser($storedGroupEvent->transactionGroup->user); $newRuleEngine->setUser($storedGroupEvent->transactionGroup->user);
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
$newRuleEngine->setRuleGroups($groups); $newRuleEngine->setRuleGroups($groups);
@@ -90,7 +90,7 @@ class StoredGroupEventHandler
private function recalculateCredit(StoredTransactionGroup $event): void private function recalculateCredit(StoredTransactionGroup $event): void
{ {
$group = $event->transactionGroup; $group = $event->transactionGroup;
/** @var CreditRecalculateService $object */ /** @var CreditRecalculateService $object */
$object = app(CreditRecalculateService::class); $object = app(CreditRecalculateService::class);
@@ -102,6 +102,7 @@ class StoredGroupEventHandler
{ {
/** @var PeriodStatisticRepositoryInterface $repository */ /** @var PeriodStatisticRepositoryInterface $repository */
$repository = app(PeriodStatisticRepositoryInterface::class); $repository = app(PeriodStatisticRepositoryInterface::class);
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($event->transactionGroup->transactionJournals as $journal) { foreach ($event->transactionGroup->transactionJournals as $journal) {
$source = $journal->transactions()->where('amount', '<', '0')->first(); $source = $journal->transactions()->where('amount', '<', '0')->first();
@@ -117,14 +118,14 @@ class StoredGroupEventHandler
private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$group = $storedGroupEvent->transactionGroup; $group = $storedGroupEvent->transactionGroup;
if (false === $storedGroupEvent->fireWebhooks) { if (false === $storedGroupEvent->fireWebhooks) {
Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id));
return; return;
} }
$user = $group->user; $user = $group->user;
/** @var MessageGeneratorInterface $engine */ /** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class); $engine = app(MessageGeneratorInterface::class);

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Handlers\Events;
use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Enums\WebhookTrigger; use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Events\RequestedSendWebhookMessages; use FireflyIII\Events\RequestedSendWebhookMessages;
use FireflyIII\Events\StoredTransactionGroup;
use FireflyIII\Events\UpdatedTransactionGroup; use FireflyIII\Events\UpdatedTransactionGroup;
use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
@@ -63,6 +62,7 @@ class UpdatedGroupEventHandler
{ {
/** @var PeriodStatisticRepositoryInterface $repository */ /** @var PeriodStatisticRepositoryInterface $repository */
$repository = app(PeriodStatisticRepositoryInterface::class); $repository = app(PeriodStatisticRepositoryInterface::class);
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($event->transactionGroup->transactionJournals as $journal) { foreach ($event->transactionGroup->transactionJournals as $journal) {
$source = $journal->transactions()->where('amount', '<', '0')->first(); $source = $journal->transactions()->where('amount', '<', '0')->first();

View File

@@ -52,6 +52,4 @@ class PeriodStatistic extends Model
return $this->morphTo(); return $this->morphTo();
} }
} }

View File

@@ -547,6 +547,7 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
public function periodCollection(Account $account, Carbon $start, Carbon $end): array public function periodCollection(Account $account, Carbon $start, Carbon $end): array
{ {
Log::debug(sprintf('periodCollection(#%d, %s, %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d'))); Log::debug(sprintf('periodCollection(#%d, %s, %s)', $account->id, $start->format('Y-m-d'), $end->format('Y-m-d')));
return $account->transactions() return $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id') ->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')

View File

@@ -34,24 +34,26 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface
public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection public function findPeriodStatistics(Model $model, Carbon $start, Carbon $end, array $types): Collection
{ {
return $model->primaryPeriodStatistics() return $model->primaryPeriodStatistics()
->where('start', $start) ->where('start', $start)
->where('end', $end) ->where('end', $end)
->whereIn('type', $types) ->whereIn('type', $types)
->get(); ->get()
;
} }
public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection
{ {
return $model->primaryPeriodStatistics() return $model->primaryPeriodStatistics()
->where('start', $start) ->where('start', $start)
->where('end', $end) ->where('end', $end)
->where('type', $type) ->where('type', $type)
->get(); ->get()
;
} }
public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic
{ {
$stat = new PeriodStatistic(); $stat = new PeriodStatistic();
$stat->primaryStatable()->associate($model); $stat->primaryStatable()->associate($model);
$stat->transaction_currency_id = $currencyId; $stat->transaction_currency_id = $currencyId;
$stat->start = $start; $stat->start = $start;
@@ -63,9 +65,17 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface
$stat->type = $type; $stat->type = $type;
$stat->save(); $stat->save();
Log::debug(sprintf('Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.', Log::debug(sprintf(
$stat->id, get_class($model), $model->id, $stat->transaction_currency_id, $stat->start->toW3cString(), $stat->end->toW3cString(), $count, $amount 'Saved #%d [currency #%d, Model %s #%d, %s to %s, %d, %s] as new statistic.',
)); $stat->id,
get_class($model),
$model->id,
$stat->transaction_currency_id,
$stat->start->toW3cString(),
$stat->end->toW3cString(),
$count,
$amount
));
return $stat; return $stat;
} }

View File

@@ -59,11 +59,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
@@ -105,10 +105,10 @@ 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;
} }
public function convertToPrimary(?User $user = null): bool public function convertToPrimary(?User $user = null): bool
@@ -125,8 +125,8 @@ class Amount
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);
@@ -163,15 +163,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 = Steam::getLocale(); $locale = Steam::getLocale();
$rounded = Steam::bcround($amount, $decimalPlaces); $rounded = 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($rounded, '0')) { if (1 === bccomp($rounded, '0')) {
@@ -218,16 +218,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.
@@ -284,7 +284,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()) {
@@ -314,7 +314,7 @@ class Amount
$key = sprintf('transaction_currency_%s', $code); $key = sprintf('transaction_currency_%s', $code);
/** @var null|TransactionCurrency $pref */ /** @var null|TransactionCurrency $pref */
$pref = $instance->getPreference($key); $pref = $instance->getPreference($key);
if (null !== $pref) { if (null !== $pref) {
return $pref; return $pref;
} }
@@ -336,7 +336,7 @@ class Amount
$key = sprintf('transaction_currency_%d', $currencyId); $key = sprintf('transaction_currency_%d', $currencyId);
/** @var null|TransactionCurrency $pref */ /** @var null|TransactionCurrency $pref */
$pref = $instance->getPreference($key); $pref = $instance->getPreference($key);
if (null !== $pref) { if (null !== $pref) {
return $pref; return $pref;
} }
@@ -364,20 +364,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 = Steam::getLocale(); $locale = Steam::getLocale();
$array = Steam::getLocaleArray($locale); $array = 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);

View File

@@ -48,7 +48,7 @@ class RemoteUserGuard implements Guard
public function __construct(protected UserProvider $provider, Application $app) public function __construct(protected UserProvider $provider, Application $app)
{ {
/** @var null|Request $request */ /** @var null|Request $request */
$request = $app->get('request'); $request = $app->get('request');
Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri())); Log::debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri()));
$this->application = $app; $this->application = $app;
$this->user = null; $this->user = null;
@@ -63,8 +63,8 @@ class RemoteUserGuard implements Guard
return; return;
} }
// Get the user identifier from $_SERVER or apache filtered headers // Get the user identifier from $_SERVER or apache filtered headers
$header = config('auth.guard_header', 'REMOTE_USER'); $header = config('auth.guard_header', 'REMOTE_USER');
$userID = request()->server($header) ?? null; $userID = request()->server($header) ?? null;
if (function_exists('apache_request_headers')) { if (function_exists('apache_request_headers')) {
Log::debug('Use apache_request_headers to find user ID.'); Log::debug('Use apache_request_headers to find user ID.');
@@ -83,7 +83,7 @@ class RemoteUserGuard implements Guard
$retrievedUser = $this->provider->retrieveById($userID); $retrievedUser = $this->provider->retrieveById($userID);
// store email address if present in header and not already set. // store email address if present in header and not already set.
$header = config('auth.guard_email'); $header = config('auth.guard_email');
if (null !== $header) { if (null !== $header) {
$emailAddress = (string)(request()->server($header) ?? apache_request_headers()[$header] ?? null); $emailAddress = (string)(request()->server($header) ?? apache_request_headers()[$header] ?? null);
@@ -99,7 +99,7 @@ class RemoteUserGuard implements Guard
} }
Log::debug(sprintf('Result of getting user from provider: %s', $retrievedUser->email)); Log::debug(sprintf('Result of getting user from provider: %s', $retrievedUser->email));
$this->user = $retrievedUser; $this->user = $retrievedUser;
} }
public function check(): bool public function check(): bool
@@ -126,14 +126,14 @@ class RemoteUserGuard implements Guard
/** /**
* @SuppressWarnings("PHPMD.ShortMethodName") * @SuppressWarnings("PHPMD.ShortMethodName")
*/ */
public function id(): int | string | null public function id(): int|string|null
{ {
Log::debug(sprintf('Now at %s', __METHOD__)); Log::debug(sprintf('Now at %s', __METHOD__));
return $this->user?->id; return $this->user?->id;
} }
public function setUser(Authenticatable | User | null $user): void // @phpstan-ignore-line public function setUser(Authenticatable|User|null $user): void // @phpstan-ignore-line
{ {
Log::debug(sprintf('Now at %s', __METHOD__)); Log::debug(sprintf('Now at %s', __METHOD__));
if ($user instanceof User) { if ($user instanceof User) {

View File

@@ -48,18 +48,19 @@ class Balance
return $cache->get(); return $cache->get();
} }
$query = Transaction::whereIn('transactions.account_id', $accounts->pluck('id')->toArray()) $query = Transaction::whereIn('transactions.account_id', $accounts->pluck('id')->toArray())
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->orderBy('transaction_journals.date', 'desc') ->orderBy('transaction_journals.date', 'desc')
->orderBy('transaction_journals.order', 'asc') ->orderBy('transaction_journals.order', 'asc')
->orderBy('transaction_journals.description', 'desc') ->orderBy('transaction_journals.description', 'desc')
->orderBy('transactions.amount', 'desc') ->orderBy('transactions.amount', 'desc')
->where('transaction_journals.date', '<=', $date); ->where('transaction_journals.date', '<=', $date)
;
$result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.balance_after']); $result = $query->get(['transactions.account_id', 'transactions.transaction_currency_id', 'transactions.balance_after']);
foreach ($result as $entry) { foreach ($result as $entry) {
$accountId = (int)$entry->account_id; $accountId = (int)$entry->account_id;
$currencyId = (int)$entry->transaction_currency_id; $currencyId = (int)$entry->transaction_currency_id;
$currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId); $currencies[$currencyId] ??= Amount::getTransactionCurrencyById($currencyId);
$return[$accountId] ??= []; $return[$accountId] ??= [];
if (array_key_exists($currencyId, $return[$accountId])) { if (array_key_exists($currencyId, $return[$accountId])) {

View File

@@ -43,21 +43,23 @@ class AccountList implements BinderInterface
if ('allAssetAccounts' === $value) { if ('allAssetAccounts' === $value) {
/** @var Collection $collection */ /** @var Collection $collection */
$collection = auth()->user()->accounts() $collection = auth()->user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('account_types.type', [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]) ->whereIn('account_types.type', [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value])
->orderBy('accounts.name', 'ASC') ->orderBy('accounts.name', 'ASC')
->get(['accounts.*']); ->get(['accounts.*'])
;
} }
if ('allAssetAccounts' !== $value) { if ('allAssetAccounts' !== $value) {
$incoming = array_map('\intval', explode(',', $value)); $incoming = array_map('\intval', explode(',', $value));
$list = array_merge(array_unique($incoming), [0]); $list = array_merge(array_unique($incoming), [0]);
/** @var Collection $collection */ /** @var Collection $collection */
$collection = auth()->user()->accounts() $collection = auth()->user()->accounts()
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id') ->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->whereIn('accounts.id', $list) ->whereIn('accounts.id', $list)
->orderBy('accounts.name', 'ASC') ->orderBy('accounts.name', 'ASC')
->get(['accounts.*']); ->get(['accounts.*'])
;
} }
if ($collection->count() > 0) { if ($collection->count() > 0) {

View File

@@ -41,12 +41,13 @@ class BudgetList implements BinderInterface
if (auth()->check()) { if (auth()->check()) {
if ('allBudgets' === $value) { if ('allBudgets' === $value) {
return auth()->user()->budgets()->where('active', true) return auth()->user()->budgets()->where('active', true)
->orderBy('order', 'ASC') ->orderBy('order', 'ASC')
->orderBy('name', 'ASC') ->orderBy('name', 'ASC')
->get(); ->get()
;
} }
$list = array_unique(array_map('\intval', explode(',', $value))); $list = array_unique(array_map('\intval', explode(',', $value)));
if (0 === count($list)) { // @phpstan-ignore-line if (0 === count($list)) { // @phpstan-ignore-line
app('log')->warning('Budget list count is zero, return 404.'); app('log')->warning('Budget list count is zero, return 404.');
@@ -56,9 +57,10 @@ class BudgetList implements BinderInterface
/** @var Collection $collection */ /** @var Collection $collection */
$collection = auth()->user()->budgets() $collection = auth()->user()->budgets()
->where('active', true) ->where('active', true)
->whereIn('id', $list) ->whereIn('id', $list)
->get(); ->get()
;
// add empty budget if applicable. // add empty budget if applicable.
if (in_array(0, $list, true)) { if (in_array(0, $list, true)) {

View File

@@ -41,19 +41,21 @@ class CategoryList implements BinderInterface
if (auth()->check()) { if (auth()->check()) {
if ('allCategories' === $value) { if ('allCategories' === $value) {
return auth()->user()->categories() return auth()->user()->categories()
->orderBy('name', 'ASC') ->orderBy('name', 'ASC')
->get(); ->get()
;
} }
$list = array_unique(array_map('\intval', explode(',', $value))); $list = array_unique(array_map('\intval', explode(',', $value)));
if (0 === count($list)) { // @phpstan-ignore-line if (0 === count($list)) { // @phpstan-ignore-line
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }
/** @var Collection $collection */ /** @var Collection $collection */
$collection = auth()->user()->categories() $collection = auth()->user()->categories()
->whereIn('id', $list) ->whereIn('id', $list)
->get(); ->get()
;
// add empty category if applicable. // add empty category if applicable.
if (in_array(0, $list, true)) { if (in_array(0, $list, true)) {

View File

@@ -43,16 +43,16 @@ class Date implements BinderInterface
/** @var FiscalHelperInterface $fiscalHelper */ /** @var FiscalHelperInterface $fiscalHelper */
$fiscalHelper = app(FiscalHelperInterface::class); $fiscalHelper = app(FiscalHelperInterface::class);
$magicWords = [ $magicWords = [
'currentMonthStart' => today(config('app.timezone'))->startOfMonth(), 'currentMonthStart' => today(config('app.timezone'))->startOfMonth(),
'currentMonthEnd' => today(config('app.timezone'))->endOfMonth(), 'currentMonthEnd' => today(config('app.timezone'))->endOfMonth(),
'currentYearStart' => today(config('app.timezone'))->startOfYear(), 'currentYearStart' => today(config('app.timezone'))->startOfYear(),
'currentYearEnd' => today(config('app.timezone'))->endOfYear(), 'currentYearEnd' => today(config('app.timezone'))->endOfYear(),
'previousMonthStart' => today(config('app.timezone'))->startOfMonth()->subDay()->startOfMonth(), 'previousMonthStart' => today(config('app.timezone'))->startOfMonth()->subDay()->startOfMonth(),
'previousMonthEnd' => today(config('app.timezone'))->startOfMonth()->subDay()->endOfMonth(), 'previousMonthEnd' => today(config('app.timezone'))->startOfMonth()->subDay()->endOfMonth(),
'previousYearStart' => today(config('app.timezone'))->startOfYear()->subDay()->startOfYear(), 'previousYearStart' => today(config('app.timezone'))->startOfYear()->subDay()->startOfYear(),
'previousYearEnd' => today(config('app.timezone'))->startOfYear()->subDay()->endOfYear(), 'previousYearEnd' => today(config('app.timezone'))->startOfYear()->subDay()->endOfYear(),
'currentFiscalYearStart' => $fiscalHelper->startOfFiscalYear(today(config('app.timezone'))), 'currentFiscalYearStart' => $fiscalHelper->startOfFiscalYear(today(config('app.timezone'))),
'currentFiscalYearEnd' => $fiscalHelper->endOfFiscalYear(today(config('app.timezone'))), 'currentFiscalYearEnd' => $fiscalHelper->endOfFiscalYear(today(config('app.timezone'))),
@@ -68,7 +68,7 @@ class Date implements BinderInterface
try { try {
$result = new Carbon($value); $result = new Carbon($value);
} catch (InvalidDateException | InvalidFormatException $e) { // @phpstan-ignore-line } catch (InvalidDateException|InvalidFormatException $e) { // @phpstan-ignore-line
$message = sprintf('Could not parse date "%s" for user #%d: %s', $value, auth()->user()->id, $e->getMessage()); $message = sprintf('Could not parse date "%s" for user #%d: %s', $value, auth()->user()->id, $e->getMessage());
app('log')->error($message); app('log')->error($message);

View File

@@ -39,7 +39,7 @@ class JournalList implements BinderInterface
public static function routeBinder(string $value, Route $route): array public static function routeBinder(string $value, Route $route): array
{ {
if (auth()->check()) { if (auth()->check()) {
$list = self::parseList($value); $list = self::parseList($value);
// get the journals by using the collector. // get the journals by using the collector.
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
@@ -47,7 +47,7 @@ class JournalList implements BinderInterface
$collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value, TransactionTypeEnum::RECONCILIATION->value]); $collector->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value, TransactionTypeEnum::RECONCILIATION->value]);
$collector->withCategoryInformation()->withBudgetInformation()->withTagInformation()->withAccountInformation(); $collector->withCategoryInformation()->withBudgetInformation()->withTagInformation()->withAccountInformation();
$collector->setJournalIds($list); $collector->setJournalIds($list);
$result = $collector->getExtractedJournals(); $result = $collector->getExtractedJournals();
if (0 === count($result)) { if (0 === count($result)) {
throw new NotFoundHttpException(); throw new NotFoundHttpException();
} }

View File

@@ -43,10 +43,11 @@ class TagList implements BinderInterface
if (auth()->check()) { if (auth()->check()) {
if ('allTags' === $value) { if ('allTags' === $value) {
return auth()->user()->tags() return auth()->user()->tags()
->orderBy('tag', 'ASC') ->orderBy('tag', 'ASC')
->get(); ->get()
;
} }
$list = array_unique(array_map('\strtolower', explode(',', $value))); $list = array_unique(array_map('\strtolower', explode(',', $value)));
app('log')->debug('List of tags is', $list); app('log')->debug('List of tags is', $list);
if (0 === count($list)) { // @phpstan-ignore-line if (0 === count($list)) { // @phpstan-ignore-line
@@ -58,7 +59,7 @@ class TagList implements BinderInterface
/** @var TagRepositoryInterface $repository */ /** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class); $repository = app(TagRepositoryInterface::class);
$repository->setUser(auth()->user()); $repository->setUser(auth()->user());
$allTags = $repository->get(); $allTags = $repository->get();
$collection = $allTags->filter( $collection = $allTags->filter(
static function (Tag $tag) use ($list) { static function (Tag $tag) use ($list) {

View File

@@ -40,7 +40,7 @@ class TagOrId implements BinderInterface
$repository = app(TagRepositoryInterface::class); $repository = app(TagRepositoryInterface::class);
$repository->setUser(auth()->user()); $repository->setUser(auth()->user());
$result = $repository->findByTag($value); $result = $repository->findByTag($value);
if (null === $result) { if (null === $result) {
$result = $repository->find((int)$value); $result = $repository->find((int)$value);
} }

View File

@@ -42,8 +42,9 @@ class UserGroupAccount implements BinderInterface
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$account = Account::where('id', (int)$value) $account = Account::where('id', (int)$value)
->where('user_group_id', $user->user_group_id) ->where('user_group_id', $user->user_group_id)
->first(); ->first()
;
if (null !== $account) { if (null !== $account) {
return $account; return $account;
} }

View File

@@ -42,8 +42,9 @@ class UserGroupBill implements BinderInterface
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$currency = Bill::where('id', (int)$value) $currency = Bill::where('id', (int)$value)
->where('user_group_id', $user->user_group_id) ->where('user_group_id', $user->user_group_id)
->first(); ->first()
;
if (null !== $currency) { if (null !== $currency) {
return $currency; return $currency;
} }

View File

@@ -39,8 +39,9 @@ class UserGroupExchangeRate implements BinderInterface
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$rate = CurrencyExchangeRate::where('id', (int)$value) $rate = CurrencyExchangeRate::where('id', (int)$value)
->where('user_group_id', $user->user_group_id) ->where('user_group_id', $user->user_group_id)
->first(); ->first()
;
if (null !== $rate) { if (null !== $rate) {
return $rate; return $rate;
} }

View File

@@ -39,8 +39,9 @@ class UserGroupTransaction implements BinderInterface
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$group = TransactionGroup::where('id', (int)$value) $group = TransactionGroup::where('id', (int)$value)
->where('user_group_id', $user->user_group_id) ->where('user_group_id', $user->user_group_id)
->first(); ->first()
;
if (null !== $group) { if (null !== $group) {
return $group; return $group;
} }

View File

@@ -27,6 +27,7 @@ use Carbon\Carbon;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use JsonException; use JsonException;
use function Safe\json_encode; use function Safe\json_encode;
/** /**
@@ -87,7 +88,7 @@ class CacheProperties
private function hash(): void private function hash(): void
{ {
$content = ''; $content = '';
foreach ($this->properties as $property) { foreach ($this->properties as $property) {
try { try {
$content = sprintf('%s%s', $content, json_encode($property, JSON_THROW_ON_ERROR)); $content = sprintf('%s%s', $content, json_encode($property, JSON_THROW_ON_ERROR));

View File

@@ -33,7 +33,7 @@ use SplObjectStorage;
*/ */
class Calculator class Calculator
{ {
public const int DEFAULT_INTERVAL = 1; public const int DEFAULT_INTERVAL = 1;
private static ?SplObjectStorage $intervalMap = null; // @phpstan-ignore-line private static ?SplObjectStorage $intervalMap = null; // @phpstan-ignore-line
private static array $intervals = []; private static array $intervals = [];

View File

@@ -181,7 +181,7 @@ class FrontpageChartGenerator
Log::debug(sprintf('Processing limit #%d with %s %s', $limit->id, $limit->transactionCurrency->code, $limit->amount)); Log::debug(sprintf('Processing limit #%d with %s %s', $limit->id, $limit->transactionCurrency->code, $limit->amount));
} }
$spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency); $spent = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection()->push($budget), $currency);
Log::debug(sprintf('Spent array has %d entries.', count($spent))); Log::debug(sprintf('Spent array has %d entries.', count($spent)));
/** @var array $entry */ /** @var array $entry */
@@ -208,7 +208,7 @@ class FrontpageChartGenerator
*/ */
private function processRow(array $data, Budget $budget, BudgetLimit $limit, array $entry): array private function processRow(array $data, Budget $budget, BudgetLimit $limit, array $entry): array
{ {
$title = sprintf('%s (%s)', $budget->name, $entry['currency_name']); $title = sprintf('%s (%s)', $budget->name, $entry['currency_name']);
Log::debug(sprintf('Title is "%s"', $title)); Log::debug(sprintf('Title is "%s"', $title));
if ($limit->start_date->startOfDay()->ne($this->start->startOfDay()) || $limit->end_date->startOfDay()->ne($this->end->startOfDay())) { if ($limit->start_date->startOfDay()->ne($this->start->startOfDay()) || $limit->end_date->startOfDay()->ne($this->end->startOfDay())) {
$title = sprintf( $title = sprintf(
@@ -219,8 +219,8 @@ class FrontpageChartGenerator
$limit->end_date->isoFormat($this->monthAndDayFormat) $limit->end_date->isoFormat($this->monthAndDayFormat)
); );
} }
$usePrimary = $this->convertToPrimary && $this->default->id !== $limit->transaction_currency_id; $usePrimary = $this->convertToPrimary && $this->default->id !== $limit->transaction_currency_id;
$amount = $limit->amount; $amount = $limit->amount;
Log::debug(sprintf('Amount is "%s".', $amount)); Log::debug(sprintf('Amount is "%s".', $amount));
if ($usePrimary && $limit->transaction_currency_id !== $this->default->id) { if ($usePrimary && $limit->transaction_currency_id !== $this->default->id) {
$amount = $limit->native_amount; $amount = $limit->native_amount;

View File

@@ -65,16 +65,16 @@ class FrontpageChartGenerator
public function generate(): array public function generate(): array
{ {
Log::debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$categories = $this->repository->getCategories(); $categories = $this->repository->getCategories();
$accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]); $accounts = $this->accountRepos->getAccountsByType([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value]);
$collection = $this->collectExpensesAll($categories, $accounts); $collection = $this->collectExpensesAll($categories, $accounts);
// collect for no-category: // collect for no-category:
$noCategory = $this->collectNoCatExpenses($accounts); $noCategory = $this->collectNoCatExpenses($accounts);
$collection = array_merge($collection, $noCategory); $collection = array_merge($collection, $noCategory);
// sort temp array by amount. // sort temp array by amount.
$amounts = array_column($collection, 'sum_float'); $amounts = array_column($collection, 'sum_float');
array_multisort($amounts, SORT_ASC, $collection); array_multisort($amounts, SORT_ASC, $collection);
$currencyData = $this->createCurrencyGroups($collection); $currencyData = $this->createCurrencyGroups($collection);

View File

@@ -40,22 +40,22 @@ class WholePeriodChartGenerator
public function generate(Category $category, Carbon $start, Carbon $end): array public function generate(Category $category, Carbon $start, Carbon $end): array
{ {
$collection = new Collection()->push($category); $collection = new Collection()->push($category);
/** @var OperationsRepositoryInterface $opsRepository */ /** @var OperationsRepositoryInterface $opsRepository */
$opsRepository = app(OperationsRepositoryInterface::class); $opsRepository = app(OperationsRepositoryInterface::class);
/** @var AccountRepositoryInterface $accountRepository */ /** @var AccountRepositoryInterface $accountRepository */
$accountRepository = app(AccountRepositoryInterface::class); $accountRepository = app(AccountRepositoryInterface::class);
$types = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value]; $types = [AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value];
$accounts = $accountRepository->getAccountsByType($types); $accounts = $accountRepository->getAccountsByType($types);
$step = $this->calculateStep($start, $end); $step = $this->calculateStep($start, $end);
$chartData = []; $chartData = [];
$spent = []; $spent = [];
$earned = []; $earned = [];
$current = clone $start; $current = clone $start;
while ($current <= $end) { while ($current <= $end) {
$key = $current->format('Y-m-d'); $key = $current->format('Y-m-d');
@@ -65,14 +65,14 @@ class WholePeriodChartGenerator
$current = app('navigation')->addPeriod($current, $step, 0); $current = app('navigation')->addPeriod($current, $step, 0);
} }
$currencies = $this->extractCurrencies($spent) + $this->extractCurrencies($earned); $currencies = $this->extractCurrencies($spent) + $this->extractCurrencies($earned);
// generate chart data (for each currency) // generate chart data (for each currency)
/** @var array $currency */ /** @var array $currency */
foreach ($currencies as $currency) { foreach ($currencies as $currency) {
$code = $currency['currency_code']; $code = $currency['currency_code'];
$name = $currency['currency_name']; $name = $currency['currency_name'];
$chartData[sprintf('spent-in-%s', $code)] = [ $chartData[sprintf('spent-in-%s', $code)] = [
'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $name]), 'label' => (string)trans('firefly.box_spent_in_currency', ['currency' => $name]),
'entries' => [], 'entries' => [],
'type' => 'bar', 'type' => 'bar',
@@ -87,11 +87,11 @@ class WholePeriodChartGenerator
]; ];
} }
$current = clone $start; $current = clone $start;
while ($current <= $end) { while ($current <= $end) {
$key = $current->format('Y-m-d'); $key = $current->format('Y-m-d');
$label = app('navigation')->periodShow($current, $step); $label = app('navigation')->periodShow($current, $step);
/** @var array $currency */ /** @var array $currency */
foreach ($currencies as $currency) { foreach ($currencies as $currency) {

View File

@@ -49,7 +49,7 @@ class ChartData
if (array_key_exists('primary_currency_id', $data)) { if (array_key_exists('primary_currency_id', $data)) {
$data['primary_currency_id'] = (string)$data['primary_currency_id']; $data['primary_currency_id'] = (string)$data['primary_currency_id'];
} }
$required = ['start', 'date', 'end', 'entries']; $required = ['start', 'date', 'end', 'entries'];
foreach ($required as $field) { foreach ($required as $field) {
if (!array_key_exists($field, $data)) { if (!array_key_exists($field, $data)) {
throw new FireflyException(sprintf('Data-set is missing the "%s"-variable.', $field)); throw new FireflyException(sprintf('Data-set is missing the "%s"-variable.', $field));

View File

@@ -55,7 +55,7 @@ class ChartColour
public static function getColour(int $index): string public static function getColour(int $index): string
{ {
$index %= count(self::$colours); $index %= count(self::$colours);
$row = self::$colours[$index]; $row = self::$colours[$index];
return sprintf('rgba(%d, %d, %d, 0.7)', $row[0], $row[1], $row[2]); return sprintf('rgba(%d, %d, %d, 0.7)', $row[0], $row[1], $row[2]);
} }

View File

@@ -70,7 +70,7 @@ class AutoBudgetCronjob extends AbstractCronjob
Log::info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d'))); Log::info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d')));
/** @var CreateAutoBudgetLimits $job */ /** @var CreateAutoBudgetLimits $job */
$job = app(CreateAutoBudgetLimits::class, [$this->date]); $job = app(CreateAutoBudgetLimits::class, [$this->date]);
$job->setDate($this->date); $job->setDate($this->date);
$job->handle(); $job->handle();

View File

@@ -82,7 +82,7 @@ class BillWarningCronjob extends AbstractCronjob
Log::info(sprintf('Will now fire bill notification job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); Log::info(sprintf('Will now fire bill notification job task for date "%s".', $this->date->format('Y-m-d H:i:s')));
/** @var WarnAboutBills $job */ /** @var WarnAboutBills $job */
$job = app(WarnAboutBills::class); $job = app(WarnAboutBills::class);
$job->setDate($this->date); $job->setDate($this->date);
$job->setForce($this->force); $job->setForce($this->force);
$job->handle(); $job->handle();

View File

@@ -71,7 +71,7 @@ class ExchangeRatesCronjob extends AbstractCronjob
Log::info(sprintf('Will now fire exchange rates cron job task for date "%s".', $this->date->format('Y-m-d'))); Log::info(sprintf('Will now fire exchange rates cron job task for date "%s".', $this->date->format('Y-m-d')));
/** @var DownloadExchangeRates $job */ /** @var DownloadExchangeRates $job */
$job = app(DownloadExchangeRates::class); $job = app(DownloadExchangeRates::class);
$job->setDate($this->date); $job->setDate($this->date);
$job->handle(); $job->handle();

View File

@@ -80,7 +80,7 @@ class RecurringCronjob extends AbstractCronjob
{ {
Log::info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); Log::info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s')));
$job = new CreateRecurringTransactions($this->date); $job = new CreateRecurringTransactions($this->date);
$job->setForce($this->force); $job->setForce($this->force);
$job->handle(); $job->handle();

View File

@@ -41,8 +41,8 @@ class UpdateCheckCronjob extends AbstractCronjob
Log::debug('Now in checkForUpdates()'); Log::debug('Now in checkForUpdates()');
// should not check for updates: // should not check for updates:
$permission = FireflyConfig::get('permission_update_check', -1); $permission = FireflyConfig::get('permission_update_check', -1);
$value = (int)$permission->data; $value = (int)$permission->data;
if (1 !== $value) { if (1 !== $value) {
Log::debug('Update check is not enabled.'); Log::debug('Update check is not enabled.');
// get stuff from job: // get stuff from job:
@@ -56,9 +56,9 @@ class UpdateCheckCronjob extends AbstractCronjob
// TODO this is duplicate. // TODO this is duplicate.
/** @var Configuration $lastCheckTime */ /** @var Configuration $lastCheckTime */
$lastCheckTime = FireflyConfig::get('last_update_check', Carbon::now()->getTimestamp()); $lastCheckTime = FireflyConfig::get('last_update_check', Carbon::now()->getTimestamp());
$now = Carbon::now()->getTimestamp(); $now = Carbon::now()->getTimestamp();
$diff = $now - $lastCheckTime->data; $diff = $now - $lastCheckTime->data;
Log::debug(sprintf('Last check time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff)); Log::debug(sprintf('Last check time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff));
if ($diff < 604800 && false === $this->force) { if ($diff < 604800 && false === $this->force) {
// get stuff from job: // get stuff from job:
@@ -71,7 +71,7 @@ class UpdateCheckCronjob extends AbstractCronjob
} }
// last check time was more than a week ago. // last check time was more than a week ago.
Log::debug('Have not checked for a new version in a week!'); Log::debug('Have not checked for a new version in a week!');
$release = $this->getLatestRelease(); $release = $this->getLatestRelease();
if ('error' === $release['level']) { if ('error' === $release['level']) {
// get stuff from job: // get stuff from job:
$this->jobFired = true; $this->jobFired = true;

View File

@@ -43,7 +43,7 @@ class ExpandedForm
*/ */
public function amountNoCurrency(string $name, $value = null, ?array $options = null): string public function amountNoCurrency(string $name, $value = null, ?array $options = null): string
{ {
$options ??= []; $options ??= [];
$label = $this->label($name, $options); $label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options); $options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name); $classes = $this->getHolderClasses($name);
@@ -74,8 +74,8 @@ class ExpandedForm
*/ */
public function checkbox(string $name, ?int $value = null, $checked = null, ?array $options = null): string public function checkbox(string $name, ?int $value = null, $checked = null, ?array $options = null): string
{ {
$options ??= []; $options ??= [];
$value ??= 1; $value ??= 1;
$options['checked'] = true === $checked; $options['checked'] = true === $checked;
if (app('session')->has('preFilled')) { if (app('session')->has('preFilled')) {
@@ -83,10 +83,10 @@ class ExpandedForm
$options['checked'] = $preFilled[$name] ?? $options['checked']; $options['checked'] = $preFilled[$name] ?? $options['checked'];
} }
$label = $this->label($name, $options); $label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options); $options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name); $classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value); $value = $this->fillFieldValue($name, $value);
unset($options['placeholder'], $options['autocomplete'], $options['class']); unset($options['placeholder'], $options['autocomplete'], $options['class']);
@@ -157,10 +157,10 @@ class ExpandedForm
public function integer(string $name, $value = null, ?array $options = null): string public function integer(string $name, $value = null, ?array $options = null): string
{ {
$options ??= []; $options ??= [];
$label = $this->label($name, $options); $label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options); $options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name); $classes = $this->getHolderClasses($name);
$value = $this->fillFieldValue($name, $value); $value = $this->fillFieldValue($name, $value);
$options['step'] ??= '1'; $options['step'] ??= '1';
try { try {
@@ -209,9 +209,9 @@ class ExpandedForm
/** @var Model $entry */ /** @var Model $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
// All Eloquent models have an ID // All Eloquent models have an ID
$entryId = $entry->id; $entryId = $entry->id;
$current = $entry->toArray(); $current = $entry->toArray();
$title = null; $title = null;
foreach ($fields as $field) { foreach ($fields as $field) {
if (array_key_exists($field, $current) && null === $title) { if (array_key_exists($field, $current) && null === $title) {
$title = $current[$field]; $title = $current[$field];

View File

@@ -89,8 +89,8 @@ class ExportDataGenerator
public function __construct() public function __construct()
{ {
$this->accounts = new Collection(); $this->accounts = new Collection();
$this->start = today(config('app.timezone')); $this->start = today(config('app.timezone'));
$this->start->subYear(); $this->start->subYear();
$this->end = today(config('app.timezone')); $this->end = today(config('app.timezone'));
$this->exportTransactions = false; $this->exportTransactions = false;
@@ -234,7 +234,7 @@ class ExportDataGenerator
*/ */
private function exportAccounts(): string private function exportAccounts(): string
{ {
$header = [ $header = [
'user_id', 'user_id',
'account_id', 'account_id',
'created_at', 'created_at',
@@ -255,7 +255,7 @@ class ExportDataGenerator
]; ];
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$allAccounts = $repository->getAccountsByType([]); $allAccounts = $repository->getAccountsByType([]);
$records = []; $records = [];
@@ -285,7 +285,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -318,8 +318,8 @@ class ExportDataGenerator
/** @var BillRepositoryInterface $repository */ /** @var BillRepositoryInterface $repository */
$repository = app(BillRepositoryInterface::class); $repository = app(BillRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$bills = $repository->getBills(); $bills = $repository->getBills();
$header = [ $header = [
'user_id', 'user_id',
'bill_id', 'bill_id',
'created_at', 'created_at',
@@ -333,7 +333,7 @@ class ExportDataGenerator
'skip', 'skip',
'active', 'active',
]; ];
$records = []; $records = [];
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($bills as $bill) { foreach ($bills as $bill) {
@@ -354,7 +354,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -384,7 +384,7 @@ class ExportDataGenerator
*/ */
private function exportBudgets(): string private function exportBudgets(): string
{ {
$header = [ $header = [
'user_id', 'user_id',
'budget_id', 'budget_id',
'name', 'name',
@@ -398,9 +398,9 @@ class ExportDataGenerator
$budgetRepos = app(BudgetRepositoryInterface::class); $budgetRepos = app(BudgetRepositoryInterface::class);
$budgetRepos->setUser($this->user); $budgetRepos->setUser($this->user);
$limitRepos = app(BudgetLimitRepositoryInterface::class); $limitRepos = app(BudgetLimitRepositoryInterface::class);
$budgets = $budgetRepos->getBudgets(); $budgets = $budgetRepos->getBudgets();
$records = []; $records = [];
/** @var Budget $budget */ /** @var Budget $budget */
foreach ($budgets as $budget) { foreach ($budgets as $budget) {
@@ -423,7 +423,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -453,10 +453,10 @@ class ExportDataGenerator
*/ */
private function exportCategories(): string private function exportCategories(): string
{ {
$header = ['user_id', 'category_id', 'created_at', 'updated_at', 'name']; $header = ['user_id', 'category_id', 'created_at', 'updated_at', 'name'];
/** @var CategoryRepositoryInterface $catRepos */ /** @var CategoryRepositoryInterface $catRepos */
$catRepos = app(CategoryRepositoryInterface::class); $catRepos = app(CategoryRepositoryInterface::class);
$catRepos->setUser($this->user); $catRepos->setUser($this->user);
$records = []; $records = [];
@@ -474,7 +474,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -505,14 +505,14 @@ class ExportDataGenerator
private function exportPiggies(): string private function exportPiggies(): string
{ {
/** @var PiggyBankRepositoryInterface $piggyRepos */ /** @var PiggyBankRepositoryInterface $piggyRepos */
$piggyRepos = app(PiggyBankRepositoryInterface::class); $piggyRepos = app(PiggyBankRepositoryInterface::class);
$piggyRepos->setUser($this->user); $piggyRepos->setUser($this->user);
/** @var AccountRepositoryInterface $accountRepos */ /** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class); $accountRepos = app(AccountRepositoryInterface::class);
$accountRepos->setUser($this->user); $accountRepos->setUser($this->user);
$header = [ $header = [
'user_id', 'user_id',
'piggy_bank_id', 'piggy_bank_id',
'created_at', 'created_at',
@@ -528,8 +528,8 @@ class ExportDataGenerator
'order', 'order',
'active', 'active',
]; ];
$records = []; $records = [];
$piggies = $piggyRepos->getPiggyBanks(); $piggies = $piggyRepos->getPiggyBanks();
/** @var PiggyBank $piggy */ /** @var PiggyBank $piggy */
foreach ($piggies as $piggy) { foreach ($piggies as $piggy) {
@@ -554,7 +554,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -587,7 +587,7 @@ class ExportDataGenerator
/** @var RecurringRepositoryInterface $recurringRepos */ /** @var RecurringRepositoryInterface $recurringRepos */
$recurringRepos = app(RecurringRepositoryInterface::class); $recurringRepos = app(RecurringRepositoryInterface::class);
$recurringRepos->setUser($this->user); $recurringRepos->setUser($this->user);
$header = [ $header = [
// recurrence: // recurrence:
'user_id', 'recurrence_id', 'row_contains', 'created_at', 'updated_at', 'type', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active', 'user_id', 'recurrence_id', 'row_contains', 'created_at', 'updated_at', 'type', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active',
@@ -596,8 +596,8 @@ class ExportDataGenerator
// transactions + meta: // transactions + meta:
'currency_code', 'foreign_currency_code', 'source_name', 'source_type', 'destination_name', 'destination_type', 'amount', 'foreign_amount', 'category', 'budget', 'piggy_bank', 'tags', 'currency_code', 'foreign_currency_code', 'source_name', 'source_type', 'destination_name', 'destination_type', 'amount', 'foreign_amount', 'category', 'budget', 'piggy_bank', 'tags',
]; ];
$records = []; $records = [];
$recurrences = $recurringRepos->get(); $recurrences = $recurringRepos->get();
/** @var Recurrence $recurrence */ /** @var Recurrence $recurrence */
foreach ($recurrences as $recurrence) { foreach ($recurrences as $recurrence) {
@@ -630,7 +630,7 @@ class ExportDataGenerator
$piggyBankId = $recurringRepos->getPiggyBank($transaction); $piggyBankId = $recurringRepos->getPiggyBank($transaction);
$tags = $recurringRepos->getTags($transaction); $tags = $recurringRepos->getTags($transaction);
$records[] = [ $records[] = [
// recurrence // recurrence
$this->user->id, $this->user->id,
$recurrence->id, $recurrence->id,
@@ -646,7 +646,7 @@ class ExportDataGenerator
} }
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -683,8 +683,8 @@ class ExportDataGenerator
'action_type', 'action_value', 'action_order', 'action_active', 'action_stop_processing']; 'action_type', 'action_value', 'action_order', 'action_active', 'action_stop_processing'];
$ruleRepos = app(RuleRepositoryInterface::class); $ruleRepos = app(RuleRepositoryInterface::class);
$ruleRepos->setUser($this->user); $ruleRepos->setUser($this->user);
$rules = $ruleRepos->getAll(); $rules = $ruleRepos->getAll();
$records = []; $records = [];
/** @var Rule $rule */ /** @var Rule $rule */
foreach ($rules as $rule) { foreach ($rules as $rule) {
@@ -723,7 +723,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -753,12 +753,12 @@ class ExportDataGenerator
*/ */
private function exportTags(): string private function exportTags(): string
{ {
$header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level']; $header = ['user_id', 'tag_id', 'created_at', 'updated_at', 'tag', 'date', 'description', 'latitude', 'longitude', 'zoom_level'];
$tagRepos = app(TagRepositoryInterface::class); $tagRepos = app(TagRepositoryInterface::class);
$tagRepos->setUser($this->user); $tagRepos->setUser($this->user);
$tags = $tagRepos->get(); $tags = $tagRepos->get();
$records = []; $records = [];
/** @var Tag $tag */ /** @var Tag $tag */
foreach ($tags as $tag) { foreach ($tags as $tag) {
@@ -777,7 +777,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {
@@ -809,26 +809,26 @@ class ExportDataGenerator
{ {
Log::debug('Will now export transactions.'); Log::debug('Will now export transactions.');
// TODO better place for keys? // TODO better place for keys?
$header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'currency_code', 'amount', 'foreign_currency_code', 'foreign_amount', 'primary_currency_code', 'pc_amount', 'pc_foreign_amount', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags', 'notes']; $header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'currency_code', 'amount', 'foreign_currency_code', 'foreign_amount', 'primary_currency_code', 'pc_amount', 'pc_foreign_amount', 'description', 'date', 'source_name', 'source_iban', 'source_type', 'destination_name', 'destination_iban', 'destination_type', 'reconciled', 'category', 'budget', 'bill', 'tags', 'notes'];
$metaFields = config('firefly.journal_meta_fields'); $metaFields = config('firefly.journal_meta_fields');
$header = array_merge($header, $metaFields); $header = array_merge($header, $metaFields);
$primary = Amount::getPrimaryCurrency(); $primary = Amount::getPrimaryCurrency();
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setUser($this->user); $collector->setUser($this->user);
$collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation()->withBudgetInformation()->withTagInformation()->withNotes(); $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation()->withBudgetInformation()->withTagInformation()->withNotes();
if (0 !== $this->accounts->count()) { if (0 !== $this->accounts->count()) {
$collector->setAccounts($this->accounts); $collector->setAccounts($this->accounts);
} }
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
// get repository for meta data: // get repository for meta data:
$repository = app(TransactionGroupRepositoryInterface::class); $repository = app(TransactionGroupRepositoryInterface::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$records = []; $records = [];
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
@@ -853,7 +853,7 @@ class ExportDataGenerator
$pcForeignAmount = null === $journal['pc_foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_foreign_amount']), $primary->decimal_places); $pcForeignAmount = null === $journal['pc_foreign_amount'] ? null : Steam::bcround(Steam::negative($journal['pc_foreign_amount']), $primary->decimal_places);
} }
$records[] = [ $records[] = [
$journal['user_id'], $journal['transaction_group_id'], $journal['transaction_journal_id'], $journal['created_at']->toAtomString(), $journal['updated_at']->toAtomString(), $journal['transaction_group_title'], $journal['transaction_type_type'], $journal['user_id'], $journal['transaction_group_id'], $journal['transaction_journal_id'], $journal['created_at']->toAtomString(), $journal['updated_at']->toAtomString(), $journal['transaction_group_title'], $journal['transaction_type_type'],
// amounts and currencies // amounts and currencies
$journal['currency_code'], $amount, $journal['foreign_currency_code'], $foreignAmount, $primary->code, $pcAmount, $pcForeignAmount, $journal['currency_code'], $amount, $journal['foreign_currency_code'], $foreignAmount, $primary->code, $pcAmount, $pcForeignAmount,
@@ -878,7 +878,7 @@ class ExportDataGenerator
} }
// load the CSV document from a string // load the CSV document from a string
$csv = Writer::createFromString(); $csv = Writer::createFromString();
// insert the header // insert the header
try { try {

View File

@@ -39,7 +39,7 @@ class FireflyConfig
{ {
public function delete(string $name): void public function delete(string $name): void
{ {
$fullName = 'ff3-config-' . $name; $fullName = 'ff3-config-'.$name;
if (Cache::has($fullName)) { if (Cache::has($fullName)) {
Cache::forget($fullName); Cache::forget($fullName);
} }
@@ -53,7 +53,7 @@ class FireflyConfig
*/ */
public function get(string $name, mixed $default = null): ?Configuration public function get(string $name, mixed $default = null): ?Configuration
{ {
$fullName = 'ff3-config-' . $name; $fullName = 'ff3-config-'.$name;
if (Cache::has($fullName)) { if (Cache::has($fullName)) {
return Cache::get($fullName); return Cache::get($fullName);
} }
@@ -61,7 +61,7 @@ class FireflyConfig
try { try {
/** @var null|Configuration $config */ /** @var null|Configuration $config */
$config = Configuration::where('name', $name)->first(['id', 'name', 'data']); $config = Configuration::where('name', $name)->first(['id', 'name', 'data']);
} catch (Exception | QueryException $e) { } catch (Exception|QueryException $e) {
throw new FireflyException(sprintf('Could not poll the database: %s', $e->getMessage()), 0, $e); throw new FireflyException(sprintf('Could not poll the database: %s', $e->getMessage()), 0, $e);
} }
@@ -146,13 +146,13 @@ class FireflyConfig
$item->name = $name; $item->name = $name;
$item->data = $value; $item->data = $value;
$item->save(); $item->save();
Cache::forget('ff3-config-' . $name); Cache::forget('ff3-config-'.$name);
return $item; return $item;
} }
$config->data = $value; $config->data = $value;
$config->save(); $config->save();
Cache::forget('ff3-config-' . $name); Cache::forget('ff3-config-'.$name);
return $config; return $config;
} }

View File

@@ -62,9 +62,9 @@ class AccountForm
*/ */
public function activeWithdrawalDestinations(string $name, mixed $value = null, ?array $options = null): string public function activeWithdrawalDestinations(string $name, mixed $value = null, ?array $options = null): string
{ {
$types = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::EXPENSE->value]; $types = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::EXPENSE->value];
$repository = $this->getAccountRepository(); $repository = $this->getAccountRepository();
$grouped = $this->getAccountsGrouped($types, $repository); $grouped = $this->getAccountsGrouped($types, $repository);
$cash = $repository->getCashAccount(); $cash = $repository->getCashAccount();
$key = (string)trans('firefly.cash_account_type'); $key = (string)trans('firefly.cash_account_type');
@@ -80,15 +80,15 @@ class AccountForm
*/ */
public function assetAccountCheckList(string $name, ?array $options = null): string public function assetAccountCheckList(string $name, ?array $options = null): string
{ {
$options ??= []; $options ??= [];
$label = $this->label($name, $options); $label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options); $options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name); $classes = $this->getHolderClasses($name);
$selected = request()->old($name) ?? []; $selected = request()->old($name) ?? [];
// get all asset accounts: // get all asset accounts:
$types = [AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value]; $types = [AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value];
$grouped = $this->getAccountsGrouped($types); $grouped = $this->getAccountsGrouped($types);
unset($options['class']); unset($options['class']);
@@ -154,7 +154,7 @@ class AccountForm
/** @var Account $account */ /** @var Account $account */
foreach ($accountList as $account) { foreach ($accountList as $account) {
$role = (string)$repository->getMetaValue($account, 'account_role'); $role = (string)$repository->getMetaValue($account, 'account_role');
if (in_array($account->accountType->type, $liabilityTypes, true)) { if (in_array($account->accountType->type, $liabilityTypes, true)) {
$role = sprintf('l_%s', $account->accountType->type); $role = sprintf('l_%s', $account->accountType->type);
} }

View File

@@ -72,12 +72,12 @@ class CurrencyForm
$currencyRepos = app(CurrencyRepositoryInterface::class); $currencyRepos = app(CurrencyRepositoryInterface::class);
// get all currencies: // get all currencies:
$list = $currencyRepos->get(); $list = $currencyRepos->get();
$array = []; $array = [];
/** @var TransactionCurrency $currency */ /** @var TransactionCurrency $currency */
foreach ($list as $currency) { foreach ($list as $currency) {
$array[$currency->id] = $currency->name . ' (' . $currency->symbol . ')'; $array[$currency->id] = $currency->name.' ('.$currency->symbol.')';
} }
return $this->select($name, $array, $value, $options); return $this->select($name, $array, $value, $options);
@@ -94,14 +94,14 @@ class CurrencyForm
$currencyRepos = app(CurrencyRepositoryInterface::class); $currencyRepos = app(CurrencyRepositoryInterface::class);
// get all currencies: // get all currencies:
$list = $currencyRepos->get(); $list = $currencyRepos->get();
$array = [ $array = [
0 => (string)trans('firefly.no_currency'), 0 => (string)trans('firefly.no_currency'),
]; ];
/** @var TransactionCurrency $currency */ /** @var TransactionCurrency $currency */
foreach ($list as $currency) { foreach ($list as $currency) {
$array[$currency->id] = $currency->name . ' (' . $currency->symbol . ')'; $array[$currency->id] = $currency->name.' ('.$currency->symbol.')';
} }
return $this->select($name, $array, $value, $options); return $this->select($name, $array, $value, $options);
@@ -124,16 +124,16 @@ class CurrencyForm
$primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency(); $primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency();
/** @var Collection $currencies */ /** @var Collection $currencies */
$currencies = app('amount')->getAllCurrencies(); $currencies = app('amount')->getAllCurrencies();
unset($options['currency'], $options['placeholder']); unset($options['currency'], $options['placeholder']);
// perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount)
$preFilled = session('preFilled'); $preFilled = session('preFilled');
if (!is_array($preFilled)) { if (!is_array($preFilled)) {
$preFilled = []; $preFilled = [];
} }
$key = 'amount_currency_id_' . $name; $key = 'amount_currency_id_'.$name;
$sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id; $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id;
app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId));
@@ -153,7 +153,7 @@ class CurrencyForm
} }
try { try {
$html = view('form.' . $view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.'.$view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage()));
$html = 'Could not render currencyField.'; $html = 'Could not render currencyField.';
@@ -179,15 +179,15 @@ class CurrencyForm
$primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency(); $primaryCurrency = $options['currency'] ?? app('amount')->getPrimaryCurrency();
/** @var Collection $currencies */ /** @var Collection $currencies */
$currencies = app('amount')->getCurrencies(); $currencies = app('amount')->getCurrencies();
unset($options['currency'], $options['placeholder']); unset($options['currency'], $options['placeholder']);
// perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount) // perhaps the currency has been sent to us in the field $amount_currency_id_$name (amount_currency_id_amount)
$preFilled = session('preFilled'); $preFilled = session('preFilled');
if (!is_array($preFilled)) { if (!is_array($preFilled)) {
$preFilled = []; $preFilled = [];
} }
$key = 'amount_currency_id_' . $name; $key = 'amount_currency_id_'.$name;
$sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id; $sentCurrencyId = array_key_exists($key, $preFilled) ? (int)$preFilled[$key] : $primaryCurrency->id;
app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId));
@@ -207,7 +207,7 @@ class CurrencyForm
} }
try { try {
$html = view('form.' . $view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.'.$view, compact('primaryCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
} catch (Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render currencyField(): %s', $e->getMessage()));
$html = 'Could not render currencyField.'; $html = 'Could not render currencyField.';

View File

@@ -36,7 +36,7 @@ trait FormSupport
{ {
public function multiSelect(string $name, ?array $list = null, mixed $selected = null, ?array $options = null): string public function multiSelect(string $name, ?array $list = null, mixed $selected = null, ?array $options = null): string
{ {
$list ??= []; $list ??= [];
$label = $this->label($name, $options); $label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options); $options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name); $classes = $this->getHolderClasses($name);
@@ -59,7 +59,7 @@ trait FormSupport
*/ */
public function select(string $name, ?array $list = null, $selected = null, ?array $options = null): string public function select(string $name, ?array $list = null, $selected = null, ?array $options = null): string
{ {
$list ??= []; $list ??= [];
$label = $this->label($name, $options); $label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options); $options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name); $classes = $this->getHolderClasses($name);
@@ -81,10 +81,10 @@ trait FormSupport
*/ */
protected function expandOptionArray(string $name, $label, ?array $options = null): array protected function expandOptionArray(string $name, $label, ?array $options = null): array
{ {
$options ??= []; $options ??= [];
$name = str_replace('[]', '', $name); $name = str_replace('[]', '', $name);
$options['class'] = 'form-control'; $options['class'] = 'form-control';
$options['id'] = 'ffInput_' . $name; $options['id'] = 'ffInput_'.$name;
$options['autocomplete'] = 'off'; $options['autocomplete'] = 'off';
$options['placeholder'] = ucfirst((string)$label); $options['placeholder'] = ucfirst((string)$label);
@@ -145,6 +145,6 @@ trait FormSupport
} }
$name = str_replace('[]', '', $name); $name = str_replace('[]', '', $name);
return (string)trans('form.' . $name); return (string)trans('form.'.$name);
} }
} }

View File

@@ -62,14 +62,14 @@ class PiggyBankForm
/** @var PiggyBank $piggy */ /** @var PiggyBank $piggy */
foreach ($piggyBanks as $piggy) { foreach ($piggyBanks as $piggy) {
$group = $piggy->objectGroups->first(); $group = $piggy->objectGroups->first();
$groupTitle = null; $groupTitle = null;
$groupOrder = 0; $groupOrder = 0;
if (null !== $group) { if (null !== $group) {
$groupTitle = $group->title; $groupTitle = $group->title;
$groupOrder = $group->order; $groupOrder = $group->order;
} }
$subList[$groupOrder] ??= [ $subList[$groupOrder] ??= [
'group' => [ 'group' => [
'title' => $groupTitle, 'title' => $groupTitle,
], ],

View File

@@ -41,8 +41,8 @@ class RuleForm
$groupRepos = app(RuleGroupRepositoryInterface::class); $groupRepos = app(RuleGroupRepositoryInterface::class);
// get all currencies: // get all currencies:
$list = $groupRepos->get(); $list = $groupRepos->get();
$array = []; $array = [];
/** @var RuleGroup $group */ /** @var RuleGroup $group */
foreach ($list as $group) { foreach ($list as $group) {
@@ -57,15 +57,15 @@ class RuleForm
*/ */
public function ruleGroupListWithEmpty(string $name, $value = null, ?array $options = null): string public function ruleGroupListWithEmpty(string $name, $value = null, ?array $options = null): string
{ {
$options ??= []; $options ??= [];
$options['class'] = 'form-control'; $options['class'] = 'form-control';
/** @var RuleGroupRepositoryInterface $groupRepos */ /** @var RuleGroupRepositoryInterface $groupRepos */
$groupRepos = app(RuleGroupRepositoryInterface::class); $groupRepos = app(RuleGroupRepositoryInterface::class);
// get all currencies: // get all currencies:
$list = $groupRepos->get(); $list = $groupRepos->get();
$array = [ $array = [
0 => (string)trans('firefly.none_in_select_list'), 0 => (string)trans('firefly.none_in_select_list'),
]; ];

View File

@@ -67,7 +67,7 @@ class AccountBalanceGrouped
/** @var array $currency */ /** @var array $currency */
foreach ($this->data as $currency) { foreach ($this->data as $currency) {
// income and expense array prepped: // income and expense array prepped:
$income = [ $income = [
'label' => 'earned', 'label' => 'earned',
'currency_id' => (string)$currency['currency_id'], 'currency_id' => (string)$currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'], 'currency_symbol' => $currency['currency_symbol'],
@@ -86,7 +86,7 @@ class AccountBalanceGrouped
'entries' => [], 'entries' => [],
'pc_entries' => [], 'pc_entries' => [],
]; ];
$expense = [ $expense = [
'label' => 'spent', 'label' => 'spent',
'currency_id' => (string)$currency['currency_id'], 'currency_id' => (string)$currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'], 'currency_symbol' => $currency['currency_symbol'],
@@ -108,22 +108,22 @@ class AccountBalanceGrouped
// loop all possible periods between $start and $end, and add them to the correct dataset. // loop all possible periods between $start and $end, and add them to the correct dataset.
$currentStart = clone $this->start; $currentStart = clone $this->start;
while ($currentStart <= $this->end) { while ($currentStart <= $this->end) {
$key = $currentStart->format($this->carbonFormat); $key = $currentStart->format($this->carbonFormat);
$label = $currentStart->toAtomString(); $label = $currentStart->toAtomString();
// normal entries // normal entries
$income['entries'][$label] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); $income['entries'][$label] = Steam::bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']);
$expense['entries'][$label] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); $expense['entries'][$label] = Steam::bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']);
// converted entries // converted entries
$income['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_earned'] ?? '0', $currency['primary_currency_decimal_places']); $income['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_earned'] ?? '0', $currency['primary_currency_decimal_places']);
$expense['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_spent'] ?? '0', $currency['primary_currency_decimal_places']); $expense['pc_entries'][$label] = Steam::bcround($currency[$key]['pc_spent'] ?? '0', $currency['primary_currency_decimal_places']);
// next loop // next loop
$currentStart = Navigation::addPeriod($currentStart, $this->preferredRange, 0); $currentStart = Navigation::addPeriod($currentStart, $this->preferredRange, 0);
} }
$chartData[] = $income; $chartData[] = $income;
$chartData[] = $expense; $chartData[] = $expense;
} }
return $chartData; return $chartData;
@@ -193,7 +193,7 @@ class AccountBalanceGrouped
private function createDefaultDataEntry(array $journal): void private function createDefaultDataEntry(array $journal): void
{ {
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
$this->data[$currencyId] ??= [ $this->data[$currencyId] ??= [
'currency_id' => (string)$currencyId, 'currency_id' => (string)$currencyId,
'currency_symbol' => $journal['currency_symbol'], 'currency_symbol' => $journal['currency_symbol'],
@@ -210,8 +210,8 @@ class AccountBalanceGrouped
private function createDefaultPeriodEntry(array $journal): void private function createDefaultPeriodEntry(array $journal): void
{ {
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
$period = $journal['date']->format($this->carbonFormat); $period = $journal['date']->format($this->carbonFormat);
$this->data[$currencyId][$period] ??= [ $this->data[$currencyId][$period] ??= [
'period' => $period, 'period' => $period,
'spent' => '0', 'spent' => '0',
@@ -268,9 +268,9 @@ class AccountBalanceGrouped
private function processJournal(array $journal): void private function processJournal(array $journal): void
{ {
// format the date according to the period // format the date according to the period
$period = $journal['date']->format($this->carbonFormat); $period = $journal['date']->format($this->carbonFormat);
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
$currency = $this->findCurrency($currencyId); $currency = $this->findCurrency($currencyId);
// set the array with monetary info, if it does not exist. // set the array with monetary info, if it does not exist.
$this->createDefaultDataEntry($journal); $this->createDefaultDataEntry($journal);
@@ -278,12 +278,12 @@ class AccountBalanceGrouped
$this->createDefaultPeriodEntry($journal); $this->createDefaultPeriodEntry($journal);
// is this journal's amount in- our outgoing? // is this journal's amount in- our outgoing?
$key = $this->getDataKey($journal); $key = $this->getDataKey($journal);
$amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']); $amount = 'spent' === $key ? Steam::negative($journal['amount']) : Steam::positive($journal['amount']);
// get conversion rate // get conversion rate
$rate = $this->getRate($currency, $journal['date']); $rate = $this->getRate($currency, $journal['date']);
$amountConverted = bcmul($amount, $rate); $amountConverted = bcmul($amount, $rate);
// perhaps transaction already has the foreign amount in the primary currency. // perhaps transaction already has the foreign amount in the primary currency.
if ((int)$journal['foreign_currency_id'] === $this->primary->id) { if ((int)$journal['foreign_currency_id'] === $this->primary->id) {
@@ -292,7 +292,7 @@ class AccountBalanceGrouped
} }
// add normal entry // add normal entry
$this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], $amount); $this->data[$currencyId][$period][$key] = bcadd((string)$this->data[$currencyId][$period][$key], $amount);
// add converted entry // add converted entry
$convertedKey = sprintf('pc_%s', $key); $convertedKey = sprintf('pc_%s', $key);

View File

@@ -128,7 +128,7 @@ class ExchangeRateConverter
if ($cache->has()) { if ($cache->has()) {
return (int)$cache->get(); return (int)$cache->get();
} }
$euro = Amount::getTransactionCurrencyByCode('EUR'); $euro = Amount::getTransactionCurrencyByCode('EUR');
++$this->queryCount; ++$this->queryCount;
$cache->store($euro->id); $cache->store($euro->id);
@@ -144,13 +144,13 @@ class ExchangeRateConverter
if ($euroId === $currency->id) { if ($euroId === $currency->id) {
return '1'; return '1';
} }
$rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d')); $rate = $this->getFromDB($currency->id, $euroId, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
// app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate)); // app('log')->debug(sprintf('Rate for %s to EUR is %s.', $currency->code, $rate));
return $rate; return $rate;
} }
$rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d')); $rate = $this->getFromDB($euroId, $currency->id, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
return bcdiv('1', $rate); return bcdiv('1', $rate);
// app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate)); // app('log')->debug(sprintf('Inverted rate for %s to EUR is %s.', $currency->code, $rate));
@@ -175,7 +175,7 @@ class ExchangeRateConverter
return '1'; return '1';
} }
$key = sprintf('cer-%d-%d-%s', $from, $to, $date); $key = sprintf('cer-%d-%d-%s', $from, $to, $date);
// perhaps the rate has been cached during this particular run // perhaps the rate has been cached during this particular run
$preparedRate = $this->prepared[$date][$from][$to] ?? null; $preparedRate = $this->prepared[$date][$from][$to] ?? null;
@@ -185,7 +185,7 @@ class ExchangeRateConverter
return $preparedRate; return $preparedRate;
} }
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($key); $cache->addProperty($key);
if ($cache->has()) { if ($cache->has()) {
$rate = $cache->get(); $rate = $cache->get();
@@ -198,14 +198,15 @@ class ExchangeRateConverter
} }
/** @var null|CurrencyExchangeRate $result */ /** @var null|CurrencyExchangeRate $result */
$result = $this->userGroup->currencyExchangeRates() $result = $this->userGroup->currencyExchangeRates()
->where('from_currency_id', $from) ->where('from_currency_id', $from)
->where('to_currency_id', $to) ->where('to_currency_id', $to)
->where('date', '<=', $date) ->where('date', '<=', $date)
->orderBy('date', 'DESC') ->orderBy('date', 'DESC')
->first(); ->first()
;
++$this->queryCount; ++$this->queryCount;
$rate = (string)$result?->rate; $rate = (string)$result?->rate;
if ('' === $rate) { if ('' === $rate) {
app('log')->debug(sprintf('ExchangeRateConverter: Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date)); app('log')->debug(sprintf('ExchangeRateConverter: Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date));
@@ -241,8 +242,8 @@ class ExchangeRateConverter
*/ */
private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string private function getRate(TransactionCurrency $from, TransactionCurrency $to, Carbon $date): string
{ {
$key = $this->getCacheKey($from, $to, $date); $key = $this->getCacheKey($from, $to, $date);
$res = Cache::get($key, null); $res = Cache::get($key, null);
// find in cache // find in cache
if (null !== $res) { if (null !== $res) {
@@ -252,7 +253,7 @@ class ExchangeRateConverter
} }
// find in database // find in database
$rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d')); $rate = $this->getFromDB($from->id, $to->id, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
Cache::forever($key, $rate); Cache::forever($key, $rate);
Log::debug(sprintf('ExchangeRateConverter: Return DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d'))); Log::debug(sprintf('ExchangeRateConverter: Return DB rate from %s to %s on %s.', $from->code, $to->code, $date->format('Y-m-d')));
@@ -261,7 +262,7 @@ class ExchangeRateConverter
} }
// find reverse in database // find reverse in database
$rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d')); $rate = $this->getFromDB($to->id, $from->id, $date->format('Y-m-d'));
if (null !== $rate) { if (null !== $rate) {
$rate = bcdiv('1', $rate); $rate = bcdiv('1', $rate);
Cache::forever($key, $rate); Cache::forever($key, $rate);

View File

@@ -31,7 +31,7 @@ use Illuminate\Support\Facades\Log;
class SummaryBalanceGrouped class SummaryBalanceGrouped
{ {
private const string SUM = 'sum'; private const string SUM = 'sum';
private array $amounts = []; private array $amounts = [];
private array $currencies; private array $currencies;
private readonly CurrencyRepositoryInterface $currencyRepository; private readonly CurrencyRepositoryInterface $currencyRepository;
@@ -48,9 +48,9 @@ class SummaryBalanceGrouped
public function groupData(): array public function groupData(): array
{ {
Log::debug('Now going to group data.'); Log::debug('Now going to group data.');
$return = []; $return = [];
foreach ($this->keys as $key) { foreach ($this->keys as $key) {
$title = match ($key) { $title = match ($key) {
'sum' => 'balance', 'sum' => 'balance',
'expense' => 'spent', 'expense' => 'spent',
'income' => 'earned', 'income' => 'earned',
@@ -109,11 +109,11 @@ class SummaryBalanceGrouped
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
// transaction info: // transaction info:
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
$amount = bcmul((string)$journal['amount'], $multiplier); $amount = bcmul((string)$journal['amount'], $multiplier);
$currency = $this->currencies[$currencyId] ?? Amount::getTransactionCurrencyById($currencyId); $currency = $this->currencies[$currencyId] ?? Amount::getTransactionCurrencyById($currencyId);
$this->currencies[$currencyId] = $currency; $this->currencies[$currencyId] = $currency;
$pcAmount = $converter->convert($currency, $this->default, $journal['date'], $amount); $pcAmount = $converter->convert($currency, $this->default, $journal['date'], $amount);
if ((int)$journal['foreign_currency_id'] === $this->default->id) { if ((int)$journal['foreign_currency_id'] === $this->default->id) {
// use foreign amount instead // use foreign amount instead
$pcAmount = $journal['foreign_amount']; $pcAmount = $journal['foreign_amount'];

View File

@@ -59,8 +59,8 @@ trait ValidatesUserGroupTrait
} }
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
$groupId = 0; $groupId = 0;
if (!$request->has('user_group_id')) { if (!$request->has('user_group_id')) {
$groupId = (int)$user->user_group_id; $groupId = (int)$user->user_group_id;
Log::debug(sprintf('validateUserGroup: no user group submitted, use default group #%d.', $groupId)); Log::debug(sprintf('validateUserGroup: no user group submitted, use default group #%d.', $groupId));
@@ -71,7 +71,7 @@ trait ValidatesUserGroupTrait
} }
/** @var UserGroupRepositoryInterface $repository */ /** @var UserGroupRepositoryInterface $repository */
$repository = app(UserGroupRepositoryInterface::class); $repository = app(UserGroupRepositoryInterface::class);
$repository->setUser($user); $repository->setUser($user);
$memberships = $repository->getMembershipsFromGroupId($groupId); $memberships = $repository->getMembershipsFromGroupId($groupId);
@@ -82,14 +82,14 @@ trait ValidatesUserGroupTrait
} }
// need to get the group from the membership: // need to get the group from the membership:
$group = $repository->getById($groupId); $group = $repository->getById($groupId);
if (null === $group) { if (null === $group) {
Log::debug(sprintf('validateUserGroup: group #%d does not exist.', $groupId)); Log::debug(sprintf('validateUserGroup: group #%d does not exist.', $groupId));
throw new AuthorizationException((string)trans('validation.belongs_user_or_user_group')); throw new AuthorizationException((string)trans('validation.belongs_user_or_user_group'));
} }
Log::debug(sprintf('validateUserGroup: validate access of user to group #%d ("%s").', $groupId, $group->title)); Log::debug(sprintf('validateUserGroup: validate access of user to group #%d ("%s").', $groupId, $group->title));
$roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : []; // @phpstan-ignore-line $roles = property_exists($this, 'acceptedRoles') ? $this->acceptedRoles : []; // @phpstan-ignore-line
if (0 === count($roles)) { if (0 === count($roles)) {
Log::debug('validateUserGroup: no roles defined, so no access.'); Log::debug('validateUserGroup: no roles defined, so no access.');

View File

@@ -56,10 +56,10 @@ trait AugumentData
/** @var Account $expenseAccount */ /** @var Account $expenseAccount */
foreach ($accounts as $expenseAccount) { foreach ($accounts as $expenseAccount) {
$collection = new Collection(); $collection = new Collection();
$collection->push($expenseAccount); $collection->push($expenseAccount);
$revenue = $repository->findByName($expenseAccount->name, [AccountTypeEnum::REVENUE->value]); $revenue = $repository->findByName($expenseAccount->name, [AccountTypeEnum::REVENUE->value]);
if (null !== $revenue) { if (null !== $revenue) {
$collection->push($revenue); $collection->push($revenue);
} }
@@ -116,7 +116,7 @@ trait AugumentData
$return[$accountId] = $grouped[$accountId][0]['name']; $return[$accountId] = $grouped[$accountId][0]['name'];
} }
} }
$return[0] = '(no name)'; $return[0] = '(no name)';
return $return; return $return;
} }
@@ -136,7 +136,7 @@ trait AugumentData
$return[$budgetId] = $grouped[$budgetId][0]['name']; $return[$budgetId] = $grouped[$budgetId][0]['name'];
} }
} }
$return[0] = (string)trans('firefly.no_budget'); $return[0] = (string)trans('firefly.no_budget');
return $return; return $return;
} }
@@ -158,7 +158,7 @@ trait AugumentData
$return[$categoryId] = $grouped[$categoryId][0]['name']; $return[$categoryId] = $grouped[$categoryId][0]['name'];
} }
} }
$return[0] = (string)trans('firefly.no_category'); $return[0] = (string)trans('firefly.no_category');
return $return; return $return;
} }
@@ -171,14 +171,14 @@ trait AugumentData
Log::debug('In getLimits'); Log::debug('In getLimits');
/** @var OperationsRepositoryInterface $opsRepository */ /** @var OperationsRepositoryInterface $opsRepository */
$opsRepository = app(OperationsRepositoryInterface::class); $opsRepository = app(OperationsRepositoryInterface::class);
/** @var BudgetLimitRepositoryInterface $blRepository */ /** @var BudgetLimitRepositoryInterface $blRepository */
$blRepository = app(BudgetLimitRepositoryInterface::class); $blRepository = app(BudgetLimitRepositoryInterface::class);
$end->endOfMonth(); $end->endOfMonth();
// 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($budget->id); $cache->addProperty($budget->id);
@@ -189,25 +189,25 @@ trait AugumentData
return $cache->get(); return $cache->get();
} }
$set = $blRepository->getBudgetLimits($budget, $start, $end); $set = $blRepository->getBudgetLimits($budget, $start, $end);
$budgetCollection = new Collection()->push($budget); $budgetCollection = new Collection()->push($budget);
// merge sets based on a key, in case of convert to primary currency // merge sets based on a key, in case of convert to primary currency
$limits = new Collection(); $limits = new Collection();
/** @var BudgetLimit $entry */ /** @var BudgetLimit $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
Log::debug(sprintf('Now at budget limit #%d', $entry->id)); Log::debug(sprintf('Now at budget limit #%d', $entry->id));
$currency = $entry->transactionCurrency; $currency = $entry->transactionCurrency;
if ($this->convertToPrimary) { if ($this->convertToPrimary) {
// the sumExpenses method already handles this. // the sumExpenses method already handles this.
$currency = $this->primaryCurrency; $currency = $this->primaryCurrency;
} }
// clone because these objects change each other. // clone because these objects change each other.
$currentStart = clone $entry->start_date; $currentStart = clone $entry->start_date;
$currentEnd = null === $entry->end_date ? null : clone $entry->end_date; $currentEnd = null === $entry->end_date ? null : clone $entry->end_date;
if (null === $currentEnd) { if (null === $currentEnd) {
$currentEnd = clone $currentStart; $currentEnd = clone $currentStart;
@@ -219,9 +219,9 @@ trait AugumentData
$entry->pc_spent = $spent; $entry->pc_spent = $spent;
// normal amount: // normal amount:
$expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, false); $expenses = $opsRepository->sumExpenses($currentStart, $currentEnd, null, $budgetCollection, $entry->transactionCurrency, false);
$spent = $expenses[$entry->transactionCurrency->id]['sum'] ?? '0'; $spent = $expenses[$entry->transactionCurrency->id]['sum'] ?? '0';
$entry->spent = $spent; $entry->spent = $spent;
$limits->push($entry); $limits->push($entry);
} }
@@ -240,7 +240,7 @@ trait AugumentData
/** @var array $journal */ /** @var array $journal */
foreach ($array as $journal) { foreach ($array as $journal) {
$name = '(no name)'; $name = '(no name)';
if (TransactionTypeEnum::WITHDRAWAL->value === $journal['transaction_type_type']) { if (TransactionTypeEnum::WITHDRAWAL->value === $journal['transaction_type_type']) {
$name = $journal['destination_account_name']; $name = $journal['destination_account_name'];
} }
@@ -263,16 +263,16 @@ trait AugumentData
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$total = $assets->merge($opposing); $total = $assets->merge($opposing);
$collector->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->setAccounts($total); $collector->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value])->setAccounts($total);
$journals = $collector->getExtractedJournals(); $journals = $collector->getExtractedJournals();
$sum = [ $sum = [
'grand_sum' => '0', 'grand_sum' => '0',
'per_currency' => [], 'per_currency' => [],
]; ];
// loop to support multi currency // loop to support multi currency
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
// if not set, set to zero: // if not set, set to zero:
if (!array_key_exists($currencyId, $sum['per_currency'])) { if (!array_key_exists($currencyId, $sum['per_currency'])) {

View File

@@ -59,28 +59,28 @@ trait ChartGeneration
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();
/** @var GeneratorInterface $generator */ /** @var GeneratorInterface $generator */
$generator = app(GeneratorInterface::class); $generator = app(GeneratorInterface::class);
/** @var AccountRepositoryInterface $accountRepos */ /** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class); $accountRepos = app(AccountRepositoryInterface::class);
$primary = app('amount')->getPrimaryCurrency(); $primary = app('amount')->getPrimaryCurrency();
$chartData = []; $chartData = [];
Log::debug(sprintf('Start of accountBalanceChart(list, %s, %s)', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('Start of accountBalanceChart(list, %s, %s)', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
Log::debug(sprintf('Now at account #%d ("%s)', $account->id, $account->name)); Log::debug(sprintf('Now at account #%d ("%s)', $account->id, $account->name));
$currency = $accountRepos->getAccountCurrency($account) ?? $primary; $currency = $accountRepos->getAccountCurrency($account) ?? $primary;
$usePrimary = $convertToPrimary && $primary->id !== $currency->id; $usePrimary = $convertToPrimary && $primary->id !== $currency->id;
$field = $convertToPrimary ? 'pc_balance' : 'balance'; $field = $convertToPrimary ? 'pc_balance' : 'balance';
$currency = $usePrimary ? $primary : $currency; $currency = $usePrimary ? $primary : $currency;
Log::debug(sprintf('Will use field %s', $field)); Log::debug(sprintf('Will use field %s', $field));
$currentSet = [ $currentSet = [
'label' => $account->name, 'label' => $account->name,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'entries' => [], 'entries' => [],
@@ -91,16 +91,16 @@ trait ChartGeneration
$previous = array_values($range)[0]; $previous = array_values($range)[0];
Log::debug(sprintf('Start balance for account #%d ("%s) is', $account->id, $account->name), $previous); Log::debug(sprintf('Start balance for account #%d ("%s) is', $account->id, $account->name), $previous);
while ($currentStart <= $end) { while ($currentStart <= $end) {
$format = $currentStart->format('Y-m-d'); $format = $currentStart->format('Y-m-d');
$label = trim($currentStart->isoFormat((string)trans('config.month_and_day_js', [], $locale))); $label = trim($currentStart->isoFormat((string)trans('config.month_and_day_js', [], $locale)));
$balance = $range[$format] ?? $previous; $balance = $range[$format] ?? $previous;
$previous = $balance; $previous = $balance;
$currentStart->addDay(); $currentStart->addDay();
$currentSet['entries'][$label] = $balance[$field] ?? '0'; $currentSet['entries'][$label] = $balance[$field] ?? '0';
} }
$chartData[] = $currentSet; $chartData[] = $currentSet;
} }
$data = $generator->multiSet($chartData); $data = $generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return $data; return $data;

View File

@@ -32,6 +32,7 @@ use FireflyIII\User;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Laravel\Passport\Passport; use Laravel\Passport\Passport;
use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\RSA;
use function Safe\file_put_contents; use function Safe\file_put_contents;
/** /**
@@ -103,7 +104,7 @@ trait CreateStuff
return; return;
} }
$key = RSA::createKey(4096); $key = RSA::createKey(4096);
Log::alert('NO OAuth keys were found. They have been created.'); Log::alert('NO OAuth keys were found. They have been created.');

View File

@@ -90,19 +90,19 @@ trait DateCalculation
protected function getNextPeriods(Carbon $date, string $range): array protected function getNextPeriods(Carbon $date, string $range): array
{ {
// select thing for next 12 periods: // select thing for next 12 periods:
$loop = []; $loop = [];
/** @var Carbon $current */ /** @var Carbon $current */
$current = app('navigation')->startOfPeriod($date, $range); $current = app('navigation')->startOfPeriod($date, $range);
$current = app('navigation')->endOfPeriod($current, $range); $current = app('navigation')->endOfPeriod($current, $range);
$current->addDay(); $current->addDay();
$count = 0; $count = 0;
while ($count < 12) { while ($count < 12) {
$current = app('navigation')->endOfPeriod($current, $range); $current = app('navigation')->endOfPeriod($current, $range);
$currentStart = app('navigation')->startOfPeriod($current, $range); $currentStart = app('navigation')->startOfPeriod($current, $range);
$loop[] = [ $loop[] = [
'label' => $current->format('Y-m-d'), 'label' => $current->format('Y-m-d'),
'title' => app('navigation')->periodShow($current, $range), 'title' => app('navigation')->periodShow($current, $range),
'start' => clone $currentStart, 'start' => clone $currentStart,
@@ -122,7 +122,7 @@ trait DateCalculation
protected function getPreviousPeriods(Carbon $date, string $range): array protected function getPreviousPeriods(Carbon $date, string $range): array
{ {
// select thing for last 12 periods: // select thing for last 12 periods:
$loop = []; $loop = [];
/** @var Carbon $current */ /** @var Carbon $current */
$current = app('navigation')->startOfPeriod($date, $range); $current = app('navigation')->startOfPeriod($date, $range);

View File

@@ -61,13 +61,13 @@ trait GetConfigurationData
$steps = []; $steps = [];
if (is_array($elements) && count($elements) > 0) { if (is_array($elements) && count($elements) > 0) {
foreach ($elements as $key => $options) { foreach ($elements as $key => $options) {
$currentStep = $options; $currentStep = $options;
// get the text: // get the text:
$currentStep['intro'] = (string)trans('intro.' . $route . '_' . $key); $currentStep['intro'] = (string)trans('intro.'.$route.'_'.$key);
// save in array: // save in array:
$steps[] = $currentStep; $steps[] = $currentStep;
} }
} }
app('log')->debug(sprintf('Total basic steps for %s is %d', $routeKey, count($steps))); app('log')->debug(sprintf('Total basic steps for %s is %d', $routeKey, count($steps)));
@@ -82,22 +82,22 @@ trait GetConfigurationData
*/ */
protected function getDateRangeConfig(): array // get configuration + get preferences. protected function getDateRangeConfig(): array // get configuration + get preferences.
{ {
$viewRange = app('navigation')->getViewRange(false); $viewRange = app('navigation')->getViewRange(false);
Log::debug(sprintf('dateRange: the view range is "%s"', $viewRange)); Log::debug(sprintf('dateRange: the view range is "%s"', $viewRange));
/** @var Carbon $start */ /** @var Carbon $start */
$start = session('start'); $start = session('start');
/** @var Carbon $end */ /** @var Carbon $end */
$end = session('end'); $end = session('end');
/** @var Carbon $first */ /** @var Carbon $first */
$first = session('first'); $first = session('first');
$title = sprintf('%s - %s', $start->isoFormat($this->monthAndDayFormat), $end->isoFormat($this->monthAndDayFormat)); $title = sprintf('%s - %s', $start->isoFormat($this->monthAndDayFormat), $end->isoFormat($this->monthAndDayFormat));
$isCustom = true === session('is_custom_range', false); $isCustom = true === session('is_custom_range', false);
$today = today(config('app.timezone')); $today = today(config('app.timezone'));
$ranges = [ $ranges = [
// first range is the current range: // first range is the current range:
$title => [$start, $end], $title => [$start, $end],
]; ];
@@ -127,10 +127,10 @@ trait GetConfigurationData
// today: // today:
/** @var Carbon $todayStart */ /** @var Carbon $todayStart */
$todayStart = app('navigation')->startOfPeriod($today, $viewRange); $todayStart = app('navigation')->startOfPeriod($today, $viewRange);
/** @var Carbon $todayEnd */ /** @var Carbon $todayEnd */
$todayEnd = app('navigation')->endOfPeriod($todayStart, $viewRange); $todayEnd = app('navigation')->endOfPeriod($todayStart, $viewRange);
if ($todayStart->ne($start) || $todayEnd->ne($end)) { if ($todayStart->ne($start) || $todayEnd->ne($end)) {
$ranges[ucfirst((string)trans('firefly.today'))] = [$todayStart, $todayEnd]; $ranges[ucfirst((string)trans('firefly.today'))] = [$todayStart, $todayEnd];
@@ -186,16 +186,16 @@ trait GetConfigurationData
// user is on page with specific instructions: // user is on page with specific instructions:
if ('' !== $specificPage) { if ('' !== $specificPage) {
$routeKey = str_replace('.', '_', $route); $routeKey = str_replace('.', '_', $route);
$elements = config(sprintf('intro.%s', $routeKey . '_' . $specificPage)); $elements = config(sprintf('intro.%s', $routeKey.'_'.$specificPage));
if (is_array($elements) && count($elements) > 0) { if (is_array($elements) && count($elements) > 0) {
foreach ($elements as $key => $options) { foreach ($elements as $key => $options) {
$currentStep = $options; $currentStep = $options;
// get the text: // get the text:
$currentStep['intro'] = (string)trans('intro.' . $route . '_' . $specificPage . '_' . $key); $currentStep['intro'] = (string)trans('intro.'.$route.'_'.$specificPage.'_'.$key);
// save in array: // save in array:
$steps[] = $currentStep; $steps[] = $currentStep;
} }
} }
} }

View File

@@ -75,14 +75,14 @@ trait ModelInformation
protected function getLiabilityTypes(): array protected function getLiabilityTypes(): array
{ {
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class); $repository = app(AccountRepositoryInterface::class);
// types of liability: // types of liability:
/** @var AccountType $debt */ /** @var AccountType $debt */
$debt = $repository->getAccountTypeByType(AccountTypeEnum::DEBT->value); $debt = $repository->getAccountTypeByType(AccountTypeEnum::DEBT->value);
/** @var AccountType $loan */ /** @var AccountType $loan */
$loan = $repository->getAccountTypeByType(AccountTypeEnum::LOAN->value); $loan = $repository->getAccountTypeByType(AccountTypeEnum::LOAN->value);
/** @var AccountType $mortgage */ /** @var AccountType $mortgage */
$mortgage = $repository->getAccountTypeByType(AccountTypeEnum::MORTGAGE->value); $mortgage = $repository->getAccountTypeByType(AccountTypeEnum::MORTGAGE->value);
@@ -114,8 +114,8 @@ trait ModelInformation
protected function getTriggersForBill(Bill $bill): array // get info and argument protected function getTriggersForBill(Bill $bill): array // get info and argument
{ {
// TODO duplicate code // TODO duplicate code
$operators = config('search.operators'); $operators = config('search.operators');
$triggers = []; $triggers = [];
foreach ($operators as $key => $operator) { foreach ($operators as $key => $operator) {
if ('user_action' !== $key && false === $operator['alias']) { if ('user_action' !== $key && false === $operator['alias']) {
$triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key));
@@ -165,8 +165,8 @@ trait ModelInformation
private function getTriggersForJournal(TransactionJournal $journal): array private function getTriggersForJournal(TransactionJournal $journal): array
{ {
// TODO duplicated code. // TODO duplicated code.
$operators = config('search.operators'); $operators = config('search.operators');
$triggers = []; $triggers = [];
foreach ($operators as $key => $operator) { foreach ($operators as $key => $operator) {
if ('user_action' !== $key && false === $operator['alias']) { if ('user_action' !== $key && false === $operator['alias']) {
$triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key));
@@ -174,18 +174,18 @@ trait ModelInformation
} }
asort($triggers); asort($triggers);
$result = []; $result = [];
$journalTriggers = []; $journalTriggers = [];
$values = []; $values = [];
$index = 0; $index = 0;
// amount, description, category, budget, tags, source, destination, notes, currency type // amount, description, category, budget, tags, source, destination, notes, currency type
// ,type // ,type
/** @var null|Transaction $source */ /** @var null|Transaction $source */
$source = $journal->transactions()->where('amount', '<', 0)->first(); $source = $journal->transactions()->where('amount', '<', 0)->first();
/** @var null|Transaction $destination */ /** @var null|Transaction $destination */
$destination = $journal->transactions()->where('amount', '>', 0)->first(); $destination = $journal->transactions()->where('amount', '>', 0)->first();
if (null === $destination || null === $source) { if (null === $destination || null === $source) {
return $result; return $result;
} }
@@ -220,21 +220,21 @@ trait ModelInformation
++$index; ++$index;
// category (if) // category (if)
$category = $journal->categories()->first(); $category = $journal->categories()->first();
if (null !== $category) { if (null !== $category) {
$journalTriggers[$index] = 'category_is'; $journalTriggers[$index] = 'category_is';
$values[$index] = $category->name; $values[$index] = $category->name;
++$index; ++$index;
} }
// budget (if) // budget (if)
$budget = $journal->budgets()->first(); $budget = $journal->budgets()->first();
if (null !== $budget) { if (null !== $budget) {
$journalTriggers[$index] = 'budget_is'; $journalTriggers[$index] = 'budget_is';
$values[$index] = $budget->name; $values[$index] = $budget->name;
++$index; ++$index;
} }
// tags (if) // tags (if)
$tags = $journal->tags()->get(); $tags = $journal->tags()->get();
/** @var Tag $tag */ /** @var Tag $tag */
foreach ($tags as $tag) { foreach ($tags as $tag) {
@@ -243,7 +243,7 @@ trait ModelInformation
++$index; ++$index;
} }
// notes (if) // notes (if)
$notes = $journal->notes()->first(); $notes = $journal->notes()->first();
if (null !== $notes) { if (null !== $notes) {
$journalTriggers[$index] = 'notes_is'; $journalTriggers[$index] = 'notes_is';
$values[$index] = $notes->text; $values[$index] = $notes->text;

View File

@@ -88,12 +88,12 @@ trait PeriodOverview
$this->accountRepository = app(AccountRepositoryInterface::class); $this->accountRepository = app(AccountRepositoryInterface::class);
$this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::class); $this->periodStatisticRepo = app(PeriodStatisticRepositoryInterface::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];
/** @var array $dates */ /** @var array $dates */
$dates = Navigation::blockPeriods($start, $end, $range); $dates = Navigation::blockPeriods($start, $end, $range);
[$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end); [$start, $end] = $this->getPeriodFromBlocks($dates, $start, $end);
$this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end); $this->statistics = $this->periodStatisticRepo->allInRangeForModel($account, $start, $end);
// TODO needs to be re-arranged: // TODO needs to be re-arranged:
// get all period stats for entire range. // get all period stats for entire range.
@@ -101,7 +101,7 @@ trait PeriodOverview
// create new ones, or use collected. // create new ones, or use collected.
$entries = []; $entries = [];
Log::debug(sprintf('Count of loops: %d', count($dates))); Log::debug(sprintf('Count of loops: %d', count($dates)));
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']); $entries[] = $this->getSingleAccountPeriod($account, $currentDate['period'], $currentDate['start'], $currentDate['end']);
@@ -137,11 +137,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);
@@ -153,32 +153,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']);
@@ -186,17 +186,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);
@@ -212,11 +212,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);
@@ -227,28 +227,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);
@@ -265,38 +265,38 @@ trait PeriodOverview
protected function getNoCategoryPeriodOverview(Carbon $theDate): array protected function getNoCategoryPeriodOverview(Carbon $theDate): array
{ {
Log::debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); 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);
Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); Log::debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d')));
Log::debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); 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]);
@@ -310,13 +310,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),
]; ];
} }
Log::debug('End of loops'); Log::debug('End of loops');
@@ -333,7 +333,7 @@ trait PeriodOverview
'total_transactions' => 0, 'total_transactions' => 0,
]; ];
foreach ($types as $type) { foreach ($types as $type) {
$set = $this->getSingleAccountPeriodByType($account, $start, $end, $type); $set = $this->getSingleAccountPeriodByType($account, $start, $end, $type);
$return['total_transactions'] += $set['count']; $return['total_transactions'] += $set['count'];
unset($set['count']); unset($set['count']);
$return[$type] = $set; $return[$type] = $set;
@@ -353,6 +353,7 @@ trait PeriodOverview
echo sprintf('End: "%s" vs "%s": %s', $statistic->end->toW3cString(), $end->toW3cString(), var_export($statistic->end->eq($end), true)); echo sprintf('End: "%s" vs "%s": %s', $statistic->end->toW3cString(), $end->toW3cString(), var_export($statistic->end->eq($end), true));
var_dump($statistic->end); var_dump($statistic->end);
var_dump($end); var_dump($end);
exit; exit;
} }
@@ -398,21 +399,21 @@ trait PeriodOverview
} }
// each result must be grouped by currency, then saved as period statistic. // each result must be grouped by currency, then saved as period statistic.
Log::debug(sprintf('Going to group %d found journal(s)', count($result))); Log::debug(sprintf('Going to group %d found journal(s)', count($result)));
$grouped = $this->groupByCurrency($result); $grouped = $this->groupByCurrency($result);
$this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped); $this->saveGroupedAsStatistics($account, $start, $end, $type, $grouped);
return $grouped; return $grouped;
} }
$grouped = [ $grouped = [
'count' => 0, 'count' => 0,
]; ];
/** @var PeriodStatistic $statistic */ /** @var PeriodStatistic $statistic */
foreach ($statistics as $statistic) { foreach ($statistics as $statistic) {
$id = (int)$statistic->transaction_currency_id; $id = (int)$statistic->transaction_currency_id;
$currency = Amount::getTransactionCurrencyById($id); $currency = Amount::getTransactionCurrencyById($id);
$grouped[$id] = [ $grouped[$id] = [
'amount' => (string)$statistic->amount, 'amount' => (string)$statistic->amount,
'count' => (int)$statistic->count, 'count' => (int)$statistic->count,
'currency_id' => $currency->id, 'currency_id' => $currency->id,
@@ -434,11 +435,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');
@@ -448,37 +449,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']);
@@ -487,17 +488,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;
@@ -508,12 +509,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');
@@ -523,16 +524,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']);
@@ -550,14 +551,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;
} }
@@ -598,7 +599,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) {
@@ -713,13 +714,13 @@ trait PeriodOverview
exit; exit;
} }
$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';

View File

@@ -52,20 +52,20 @@ trait RenderPartialViews
protected function budgetEntry(array $attributes): string // generate view for report. protected function budgetEntry(array $attributes): string // generate view for report.
{ {
/** @var PopupReportInterface $popupHelper */ /** @var PopupReportInterface $popupHelper */
$popupHelper = app(PopupReportInterface::class); $popupHelper = app(PopupReportInterface::class);
/** @var BudgetRepositoryInterface $budgetRepository */ /** @var BudgetRepositoryInterface $budgetRepository */
$budgetRepository = app(BudgetRepositoryInterface::class); $budgetRepository = app(BudgetRepositoryInterface::class);
$budget = $budgetRepository->find((int)$attributes['budgetId']); $budget = $budgetRepository->find((int)$attributes['budgetId']);
$accountRepos = app(AccountRepositoryInterface::class); $accountRepos = app(AccountRepositoryInterface::class);
$account = $accountRepos->find((int)$attributes['accountId']); $account = $accountRepos->find((int)$attributes['accountId']);
if (null === $budget || null === $account) { if (null === $budget || null === $account) {
throw new FireflyException('Could not render popup.report.balance-amount because budget or account is null.'); throw new FireflyException('Could not render popup.report.balance-amount because budget or account is null.');
} }
$journals = $popupHelper->balanceForBudget($budget, $account, $attributes); $journals = $popupHelper->balanceForBudget($budget, $account, $attributes);
try { try {
$view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render(); $view = view('popup.report.balance-amount', compact('journals', 'budget', 'account'))->render();
@@ -113,14 +113,14 @@ trait RenderPartialViews
$budgetRepository = app(BudgetRepositoryInterface::class); $budgetRepository = app(BudgetRepositoryInterface::class);
/** @var PopupReportInterface $popupHelper */ /** @var PopupReportInterface $popupHelper */
$popupHelper = app(PopupReportInterface::class); $popupHelper = app(PopupReportInterface::class);
$budget = $budgetRepository->find((int)$attributes['budgetId']); $budget = $budgetRepository->find((int)$attributes['budgetId']);
if (null === $budget) { if (null === $budget) {
// transactions without a budget. // transactions without a budget.
$budget = new Budget(); $budget = new Budget();
} }
$journals = $popupHelper->byBudget($budget, $attributes); $journals = $popupHelper->byBudget($budget, $attributes);
try { try {
$view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render(); $view = view('popup.report.budget-spent-amount', compact('journals', 'budget'))->render();
@@ -142,7 +142,7 @@ trait RenderPartialViews
protected function categoryEntry(array $attributes): string // generate view for report. protected function categoryEntry(array $attributes): string // generate view for report.
{ {
/** @var PopupReportInterface $popupHelper */ /** @var PopupReportInterface $popupHelper */
$popupHelper = app(PopupReportInterface::class); $popupHelper = app(PopupReportInterface::class);
/** @var CategoryRepositoryInterface $categoryRepository */ /** @var CategoryRepositoryInterface $categoryRepository */
$categoryRepository = app(CategoryRepositoryInterface::class); $categoryRepository = app(CategoryRepositoryInterface::class);
@@ -237,15 +237,15 @@ trait RenderPartialViews
$accountRepository = app(AccountRepositoryInterface::class); $accountRepository = app(AccountRepositoryInterface::class);
/** @var PopupReportInterface $popupHelper */ /** @var PopupReportInterface $popupHelper */
$popupHelper = app(PopupReportInterface::class); $popupHelper = app(PopupReportInterface::class);
$account = $accountRepository->find((int)$attributes['accountId']); $account = $accountRepository->find((int)$attributes['accountId']);
if (null === $account) { if (null === $account) {
return 'This is an unknown account. Apologies.'; return 'This is an unknown account. Apologies.';
} }
$journals = $popupHelper->byExpenses($account, $attributes); $journals = $popupHelper->byExpenses($account, $attributes);
try { try {
$view = view('popup.report.expense-entry', compact('journals', 'account'))->render(); $view = view('popup.report.expense-entry', compact('journals', 'account'))->render();
@@ -266,8 +266,8 @@ trait RenderPartialViews
*/ */
protected function getCurrentActions(Rule $rule): array // get info from object and present. protected function getCurrentActions(Rule $rule): array // get info from object and present.
{ {
$index = 0; $index = 0;
$actions = []; $actions = [];
// must be repos // must be repos
$currentActions = $rule->ruleActions()->orderBy('order', 'ASC')->get(); $currentActions = $rule->ruleActions()->orderBy('order', 'ASC')->get();
@@ -306,8 +306,8 @@ trait RenderPartialViews
protected function getCurrentTriggers(Rule $rule): array // get info from object and present. protected function getCurrentTriggers(Rule $rule): array // get info from object and present.
{ {
// TODO duplicated code. // TODO duplicated code.
$operators = config('search.operators'); $operators = config('search.operators');
$triggers = []; $triggers = [];
foreach ($operators as $key => $operator) { foreach ($operators as $key => $operator) {
if ('user_action' !== $key && false === $operator['alias']) { if ('user_action' !== $key && false === $operator['alias']) {
$triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key));
@@ -325,7 +325,7 @@ trait RenderPartialViews
$count = ($index + 1); $count = ($index + 1);
try { try {
$rootOperator = OperatorQuerySearch::getRootOperator((string)$entry->trigger_type); $rootOperator = OperatorQuerySearch::getRootOperator((string)$entry->trigger_type);
if (str_starts_with($rootOperator, '-')) { if (str_starts_with($rootOperator, '-')) {
$rootOperator = substr($rootOperator, 1); $rootOperator = substr($rootOperator, 1);
} }
@@ -365,14 +365,14 @@ trait RenderPartialViews
$accountRepository = app(AccountRepositoryInterface::class); $accountRepository = app(AccountRepositoryInterface::class);
/** @var PopupReportInterface $popupHelper */ /** @var PopupReportInterface $popupHelper */
$popupHelper = app(PopupReportInterface::class); $popupHelper = app(PopupReportInterface::class);
$account = $accountRepository->find((int)$attributes['accountId']); $account = $accountRepository->find((int)$attributes['accountId']);
if (null === $account) { if (null === $account) {
return 'This is an unknown category. Apologies.'; return 'This is an unknown category. Apologies.';
} }
$journals = $popupHelper->byIncome($account, $attributes); $journals = $popupHelper->byIncome($account, $attributes);
try { try {
$view = view('popup.report.income-entry', compact('journals', 'account'))->render(); $view = view('popup.report.income-entry', compact('journals', 'account'))->render();

View File

@@ -35,6 +35,7 @@ use Illuminate\Routing\Route;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Route as RouteFacade; use Illuminate\Support\Facades\Route as RouteFacade;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
use function Safe\parse_url; use function Safe\parse_url;
/** /**
@@ -100,13 +101,13 @@ trait RequestInformation
$page = $this->getPageName(); $page = $this->getPageName();
$specificPage = $this->getSpecificPageName(); $specificPage = $this->getSpecificPageName();
// indicator if user has seen the help for this page ( + special page): // indicator if user has seen the help for this page ( + special page):
$key = sprintf('shown_demo_%s%s', $page, $specificPage); $key = sprintf('shown_demo_%s%s', $page, $specificPage);
// is there an intro for this route? // is there an intro for this route?
$intro = config(sprintf('intro.%s', $page)) ?? []; $intro = config(sprintf('intro.%s', $page)) ?? [];
$specialIntro = config(sprintf('intro.%s%s', $page, $specificPage)) ?? []; $specialIntro = config(sprintf('intro.%s%s', $page, $specificPage)) ?? [];
// some routes have a "what" parameter, which indicates a special page: // some routes have a "what" parameter, which indicates a special page:
$shownDemo = true; $shownDemo = true;
// both must be array and either must be > 0 // both must be array and either must be > 0
if (count($intro) > 0 || count($specialIntro) > 0) { if (count($intro) > 0 || count($specialIntro) > 0) {
$shownDemo = app('preferences')->get($key, false)->data; $shownDemo = app('preferences')->get($key, false)->data;
@@ -127,7 +128,7 @@ trait RequestInformation
$start = session('start', today(config('app.timezone'))->startOfMonth()); $start = session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */ /** @var Carbon $end */
$end = session('end', today(config('app.timezone'))->endOfMonth()); $end = session('end', today(config('app.timezone'))->endOfMonth());
if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) { if ($start->greaterThanOrEqualTo($date) && $end->greaterThanOrEqualTo($date)) {
return true; return true;
} }
@@ -145,20 +146,20 @@ trait RequestInformation
final protected function parseAttributes(array $attributes): array // parse input + return result final protected function parseAttributes(array $attributes): array // parse input + return result
{ {
$attributes['location'] ??= ''; $attributes['location'] ??= '';
$attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', new Route('get', '', [])); $attributes['accounts'] = AccountList::routeBinder($attributes['accounts'] ?? '', new Route('get', '', []));
$date = Carbon::createFromFormat('Ymd', $attributes['startDate']); $date = Carbon::createFromFormat('Ymd', $attributes['startDate']);
if (!$date instanceof Carbon) { if (!$date instanceof Carbon) {
$date = today(config('app.timezone')); $date = today(config('app.timezone'));
} }
$date->startOfMonth(); $date->startOfMonth();
$attributes['startDate'] = $date; $attributes['startDate'] = $date;
$date2 = Carbon::createFromFormat('Ymd', $attributes['endDate']); $date2 = Carbon::createFromFormat('Ymd', $attributes['endDate']);
if (!$date2 instanceof Carbon) { if (!$date2 instanceof Carbon) {
$date2 = today(config('app.timezone')); $date2 = today(config('app.timezone'));
} }
$date2->endOfDay(); $date2->endOfDay();
$attributes['endDate'] = $date2; $attributes['endDate'] = $date2;
return $attributes; return $attributes;
} }

View File

@@ -74,8 +74,8 @@ trait RuleManagement
protected function getPreviousTriggers(Request $request): array protected function getPreviousTriggers(Request $request): array
{ {
// TODO duplicated code. // TODO duplicated code.
$operators = config('search.operators'); $operators = config('search.operators');
$triggers = []; $triggers = [];
foreach ($operators as $key => $operator) { foreach ($operators as $key => $operator) {
if ('user_action' !== $key && false === $operator['alias']) { if ('user_action' !== $key && false === $operator['alias']) {
$triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key)); $triggers[$key] = (string)trans(sprintf('firefly.rule_trigger_%s_choice', $key));
@@ -129,7 +129,7 @@ trait RuleManagement
} }
asort($triggers); asort($triggers);
$index = 0; $index = 0;
foreach ($submittedOperators as $operator) { foreach ($submittedOperators as $operator) {
$rootOperator = OperatorQuerySearch::getRootOperator($operator['type']); $rootOperator = OperatorQuerySearch::getRootOperator($operator['type']);
$needsContext = (bool)config(sprintf('search.operators.%s.needs_context', $rootOperator)); $needsContext = (bool)config(sprintf('search.operators.%s.needs_context', $rootOperator));

View File

@@ -39,14 +39,15 @@ trait TransactionCalculation
*/ */
protected function getExpensesForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array protected function getExpensesForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array
{ {
$total = $accounts->merge($opposing); $total = $accounts->merge($opposing);
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($total) $collector->setAccounts($total)
->setRange($start, $end) ->setRange($start, $end)
->withAccountInformation() ->withAccountInformation()
->setTypes([TransactionTypeEnum::WITHDRAWAL->value]); ->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -60,7 +61,8 @@ trait TransactionCalculation
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value]) $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value])
->setTags($tags)->withAccountInformation(); ->setTags($tags)->withAccountInformation()
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -73,7 +75,8 @@ trait TransactionCalculation
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value]) $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value])
->setBudgets($budgets)->withAccountInformation(); ->setBudgets($budgets)->withAccountInformation()
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -90,7 +93,8 @@ trait TransactionCalculation
->setRange($start, $end) ->setRange($start, $end)
->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value]) ->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::TRANSFER->value])
->setCategories($categories) ->setCategories($categories)
->withAccountInformation(); ->withAccountInformation()
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -103,7 +107,8 @@ trait TransactionCalculation
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value]) $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value])
->setCategories($categories)->withAccountInformation(); ->setCategories($categories)->withAccountInformation()
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -113,7 +118,7 @@ trait TransactionCalculation
*/ */
protected function getIncomeForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array protected function getIncomeForOpposing(Collection $accounts, Collection $opposing, Carbon $start, Carbon $end): array
{ {
$total = $accounts->merge($opposing); $total = $accounts->merge($opposing);
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
@@ -130,7 +135,8 @@ trait TransactionCalculation
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value]) $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::TRANSFER->value])
->setTags($tags)->withAccountInformation(); ->setTags($tags)->withAccountInformation()
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }

View File

@@ -69,7 +69,7 @@ trait UserNavigation
final protected function isEditableGroup(TransactionGroup $group): bool final protected function isEditableGroup(TransactionGroup $group): bool
{ {
/** @var null|TransactionJournal $journal */ /** @var null|TransactionJournal $journal */
$journal = $group->transactionJournals()->first(); $journal = $group->transactionJournals()->first();
if (null === $journal) { if (null === $journal) {
return false; return false;
} }
@@ -96,10 +96,10 @@ trait UserNavigation
return redirect(route('index')); return redirect(route('index'));
} }
$journal = $transaction->transactionJournal; $journal = $transaction->transactionJournal;
/** @var null|Transaction $other */ /** @var null|Transaction $other */
$other = $journal->transactions()->where('id', '!=', $transaction->id)->first(); $other = $journal->transactions()->where('id', '!=', $transaction->id)->first();
if (null === $other) { if (null === $other) {
app('log')->error(sprintf('Account #%d has no valid journals. Dont know where it belongs.', $account->id)); app('log')->error(sprintf('Account #%d has no valid journals. Dont know where it belongs.', $account->id));
session()->flash('error', trans('firefly.cant_find_redirect_account')); session()->flash('error', trans('firefly.cant_find_redirect_account'));
@@ -119,7 +119,7 @@ trait UserNavigation
final protected function redirectGroupToAccount(TransactionGroup $group) final protected function redirectGroupToAccount(TransactionGroup $group)
{ {
/** @var null|TransactionJournal $journal */ /** @var null|TransactionJournal $journal */
$journal = $group->transactionJournals()->first(); $journal = $group->transactionJournals()->first();
if (null === $journal) { if (null === $journal) {
app('log')->error(sprintf('No journals in group #%d', $group->id)); app('log')->error(sprintf('No journals in group #%d', $group->id));

View File

@@ -112,7 +112,7 @@ class AccountEnrichment implements EnrichmentInterface
} }
#[Override] #[Override]
public function enrichSingle(array | Model $model): Account | array public function enrichSingle(array|Model $model): Account|array
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);
@@ -168,9 +168,9 @@ class AccountEnrichment implements EnrichmentInterface
private function appendCollectedData(): void private function appendCollectedData(): void
{ {
$this->collection = $this->collection->map(function (Account $item) { $this->collection = $this->collection->map(function (Account $item) {
$id = (int)$item->id; $id = (int)$item->id;
$item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null; $item->full_account_type = $this->accountTypes[(int)$item->account_type_id] ?? null;
$meta = [ $meta = [
'currency' => null, 'currency' => null,
'location' => [ 'location' => [
'latitude' => null, 'latitude' => null,
@@ -217,30 +217,30 @@ class AccountEnrichment implements EnrichmentInterface
// add balances // add balances
// get currencies: // get currencies:
$currency = $this->primaryCurrency; // assume primary currency $currency = $this->primaryCurrency; // assume primary currency
if (null !== $meta['currency']) { if (null !== $meta['currency']) {
$currency = $meta['currency']; $currency = $meta['currency'];
} }
// get the current balance: // get the current balance:
$date = $this->getDate(); $date = $this->getDate();
// $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary); // $finalBalance = Steam::finalAccountBalance($item, $date, $this->primaryCurrency, $this->convertToPrimary);
$finalBalance = $this->balances[$id]; $finalBalance = $this->balances[$id];
$balanceDifference = $this->getBalanceDifference($id, $currency); $balanceDifference = $this->getBalanceDifference($id, $currency);
Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance); Log::debug(sprintf('Call finalAccountBalance(%s) with date/time "%s"', var_export($this->convertToPrimary, true), $date->toIso8601String()), $finalBalance);
// collect current balances: // collect current balances:
$currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places); $currentBalance = Steam::bcround($finalBalance[$currency->code] ?? '0', $currency->decimal_places);
$openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places); $openingBalance = Steam::bcround($meta['opening_balance_amount'] ?? '0', $currency->decimal_places);
$virtualBalance = Steam::bcround($item->virtual_balance ?? '0', $currency->decimal_places); $virtualBalance = Steam::bcround($item->virtual_balance ?? '0', $currency->decimal_places);
$debtAmount = $meta['current_debt'] ?? null; $debtAmount = $meta['current_debt'] ?? null;
// set some pc_ default values to NULL: // set some pc_ default values to NULL:
$pcCurrentBalance = null; $pcCurrentBalance = null;
$pcOpeningBalance = null; $pcOpeningBalance = null;
$pcVirtualBalance = null; $pcVirtualBalance = null;
$pcDebtAmount = null; $pcDebtAmount = null;
$pcBalanceDifference = null; $pcBalanceDifference = null;
// convert to primary currency if needed: // convert to primary currency if needed:
if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) { if ($this->convertToPrimary && $currency->id !== $this->primaryCurrency->id) {
@@ -279,7 +279,7 @@ class AccountEnrichment implements EnrichmentInterface
'pc_balance_difference' => $pcBalanceDifference, 'pc_balance_difference' => $pcBalanceDifference,
]; ];
// end add balances // end add balances
$item->meta = $meta; $item->meta = $meta;
return $item; return $item;
}); });
@@ -313,23 +313,25 @@ class AccountEnrichment implements EnrichmentInterface
private function collectLocations(): void private function collectLocations(): void
{ {
$locations = Location::query()->whereIn('locatable_id', $this->ids) $locations = Location::query()->whereIn('locatable_id', $this->ids)
->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray(); ->where('locatable_type', Account::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray()
;
foreach ($locations as $location) { foreach ($locations as $location) {
$this->locations[(int)$location['locatable_id']] $this->locations[(int)$location['locatable_id']]
= [ = [
'latitude' => (float)$location['latitude'], 'latitude' => (float)$location['latitude'],
'longitude' => (float)$location['longitude'], 'longitude' => (float)$location['longitude'],
'zoom_level' => (int)$location['zoom_level'], 'zoom_level' => (int)$location['zoom_level'],
]; ];
} }
Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations)));
} }
private function collectMetaData(): void private function collectMetaData(): void
{ {
$set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt']) $set = AccountMeta::whereIn('name', ['is_multi_currency', 'include_net_worth', 'currency_id', 'account_role', 'account_number', 'BIC', 'liability_direction', 'interest', 'interest_period', 'current_debt'])
->whereIn('account_id', $this->ids) ->whereIn('account_id', $this->ids)
->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray(); ->get(['account_meta.id', 'account_meta.account_id', 'account_meta.name', 'account_meta.data'])->toArray()
;
/** @var array $entry */ /** @var array $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
@@ -355,9 +357,10 @@ class AccountEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->ids) $notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', Account::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text']; $this->notes[(int)$note['noteable_id']] = (string)$note['text'];
} }
@@ -366,12 +369,13 @@ class AccountEnrichment implements EnrichmentInterface
private function collectObjectGroups(): void private function collectObjectGroups(): void
{ {
$set = DB::table('object_groupables') $set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->ids) ->whereIn('object_groupable_id', $this->ids)
->where('object_groupable_type', Account::class) ->where('object_groupable_type', Account::class)
->get(['object_groupable_id', 'object_group_id']); ->get(['object_groupable_id', 'object_group_id'])
;
$ids = array_unique($set->pluck('object_group_id')->toArray()); $ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) { foreach ($set as $entry) {
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;
@@ -395,19 +399,20 @@ class AccountEnrichment implements EnrichmentInterface
->setUserGroup($this->userGroup) ->setUserGroup($this->userGroup)
->setAccounts($this->collection) ->setAccounts($this->collection)
->withAccountInformation() ->withAccountInformation()
->setTypes([TransactionTypeEnum::OPENING_BALANCE->value]); ->setTypes([TransactionTypeEnum::OPENING_BALANCE->value])
$journals = $collector->getExtractedJournals(); ;
$journals = $collector->getExtractedJournals();
foreach ($journals as $journal) { foreach ($journals as $journal) {
$this->openingBalances[(int)$journal['source_account_id']] $this->openingBalances[(int)$journal['source_account_id']]
= [ = [
'amount' => Steam::negative($journal['amount']), 'amount' => Steam::negative($journal['amount']),
'date' => $journal['date'], 'date' => $journal['date'],
]; ];
$this->openingBalances[(int)$journal['destination_account_id']] $this->openingBalances[(int)$journal['destination_account_id']]
= [ = [
'amount' => Steam::positive($journal['amount']), 'amount' => Steam::positive($journal['amount']),
'date' => $journal['date'], 'date' => $journal['date'],
]; ];
} }
} }
@@ -431,8 +436,8 @@ class AccountEnrichment implements EnrichmentInterface
if (0 === count($startBalance) || 0 === count($endBalance)) { if (0 === count($startBalance) || 0 === count($endBalance)) {
return null; return null;
} }
$start = $startBalance[$currency->code] ?? '0'; $start = $startBalance[$currency->code] ?? '0';
$end = $endBalance[$currency->code] ?? '0'; $end = $endBalance[$currency->code] ?? '0';
return bcsub($end, $start); return bcsub($end, $start);
} }
@@ -453,7 +458,7 @@ class AccountEnrichment implements EnrichmentInterface
case 'current_balance': case 'current_balance':
case 'pc_current_balance': case 'pc_current_balance':
$this->collection = $this->collection->sortBy(static fn(Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]); $this->collection = $this->collection->sortBy(static fn (Account $account) => $account->meta['balances'][$parameter[0]] ?? '0', SORT_NUMERIC, 'desc' === $parameter[1]);
break; break;
} }

View File

@@ -79,7 +79,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
} }
#[Override] #[Override]
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);

View File

@@ -70,7 +70,7 @@ class BudgetEnrichment implements EnrichmentInterface
return $this->collection; return $this->collection;
} }
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);
@@ -103,8 +103,8 @@ class BudgetEnrichment implements EnrichmentInterface
private function appendCollectedData(): void private function appendCollectedData(): void
{ {
$this->collection = $this->collection->map(function (Budget $item) { $this->collection = $this->collection->map(function (Budget $item) {
$id = (int)$item->id; $id = (int)$item->id;
$meta = [ $meta = [
'object_group_id' => null, 'object_group_id' => null,
'object_group_order' => null, 'object_group_order' => null,
'object_group_title' => null, 'object_group_title' => null,
@@ -156,7 +156,7 @@ class BudgetEnrichment implements EnrichmentInterface
$opsRepository->setUserGroup($this->userGroup); $opsRepository->setUserGroup($this->userGroup);
// $spent = $this->beautify(); // $spent = $this->beautify();
// $set = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($budget)) // $set = $this->opsRepository->sumExpenses($start, $end, null, new Collection()->push($budget))
$expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null); $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection, null);
foreach ($this->collection as $item) { foreach ($this->collection as $item) {
$id = (int)$item->id; $id = (int)$item->id;
$this->spent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item, false)); $this->spent[$id] = array_values($opsRepository->sumCollectedExpensesByBudget($expenses, $item, false));
@@ -177,9 +177,10 @@ class BudgetEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->ids) $notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', Budget::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', Budget::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text']; $this->notes[(int)$note['noteable_id']] = (string)$note['text'];
} }
@@ -188,12 +189,13 @@ class BudgetEnrichment implements EnrichmentInterface
private function collectObjectGroups(): void private function collectObjectGroups(): void
{ {
$set = DB::table('object_groupables') $set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->ids) ->whereIn('object_groupable_id', $this->ids)
->where('object_groupable_type', Budget::class) ->where('object_groupable_type', Budget::class)
->get(['object_groupable_id', 'object_group_id']); ->get(['object_groupable_id', 'object_group_id'])
;
$ids = array_unique($set->pluck('object_group_id')->toArray()); $ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) { foreach ($set as $entry) {
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;

View File

@@ -73,7 +73,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
return $this->collection; return $this->collection;
} }
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);
@@ -115,12 +115,12 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private function collectBudgets(): void private function collectBudgets(): void
{ {
$budgetIds = $this->collection->pluck('budget_id')->unique()->toArray(); $budgetIds = $this->collection->pluck('budget_id')->unique()->toArray();
$budgets = Budget::whereIn('id', $budgetIds)->get(); $budgets = Budget::whereIn('id', $budgetIds)->get();
$repository = app(OperationsRepository::class); $repository = app(OperationsRepository::class);
$repository->setUser($this->user); $repository->setUser($this->user);
$expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null); $expenses = $repository->collectExpenses($this->start, $this->end, null, $budgets, null);
/** @var BudgetLimit $budgetLimit */ /** @var BudgetLimit $budgetLimit */
foreach ($this->collection as $budgetLimit) { foreach ($this->collection as $budgetLimit) {
@@ -151,8 +151,8 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private function collectIds(): void private function collectIds(): void
{ {
$this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth(); $this->start = $this->collection->min('start_date') ?? Carbon::now()->startOfMonth();
$this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth(); $this->end = $this->collection->max('end_date') ?? Carbon::now()->endOfMonth();
/** @var BudgetLimit $limit */ /** @var BudgetLimit $limit */
foreach ($this->collection as $limit) { foreach ($this->collection as $limit) {
@@ -169,9 +169,10 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->ids) $notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', BudgetLimit::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text']; $this->notes[(int)$note['noteable_id']] = (string)$note['text'];
} }
@@ -180,7 +181,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private function filterToBudget(array $expenses, int $budget): array private function filterToBudget(array $expenses, int $budget): array
{ {
$result = array_filter($expenses, fn(array $item) => (int)$item['budget_id'] === $budget); $result = array_filter($expenses, fn (array $item) => (int)$item['budget_id'] === $budget);
Log::debug(sprintf('filterToBudget for budget #%d, from %d to %d items', $budget, count($expenses), count($result))); Log::debug(sprintf('filterToBudget for budget #%d, from %d to %d items', $budget, count($expenses), count($result)));
return $result; return $result;
@@ -188,13 +189,13 @@ class BudgetLimitEnrichment implements EnrichmentInterface
private function stringifyIds(): void private function stringifyIds(): void
{ {
$this->expenses = array_map(fn($first) => array_map(function ($second) { $this->expenses = array_map(fn ($first) => array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0); $second['currency_id'] = (string)($second['currency_id'] ?? 0);
return $second; return $second;
}, $first), $this->expenses); }, $first), $this->expenses);
$this->pcExpenses = array_map(fn($first) => array_map(function ($second) { $this->pcExpenses = array_map(fn ($first) => array_map(function ($second) {
$second['currency_id'] = (string)($second['currency_id'] ?? 0); $second['currency_id'] = (string)($second['currency_id'] ?? 0);
return $second; return $second;

View File

@@ -62,7 +62,7 @@ class CategoryEnrichment implements EnrichmentInterface
return $collection; return $collection;
} }
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);
@@ -123,9 +123,10 @@ class CategoryEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->ids) $notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', Category::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', Category::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text']; $this->notes[(int)$note['noteable_id']] = (string)$note['text'];
} }
@@ -139,9 +140,9 @@ class CategoryEnrichment implements EnrichmentInterface
$opsRepository = app(OperationsRepositoryInterface::class); $opsRepository = app(OperationsRepositoryInterface::class);
$opsRepository->setUser($this->user); $opsRepository->setUser($this->user);
$opsRepository->setUserGroup($this->userGroup); $opsRepository->setUserGroup($this->userGroup);
$expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection); $expenses = $opsRepository->collectExpenses($this->start, $this->end, null, $this->collection);
$income = $opsRepository->collectIncome($this->start, $this->end, null, $this->collection); $income = $opsRepository->collectIncome($this->start, $this->end, null, $this->collection);
$transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection); $transfers = $opsRepository->collectTransfers($this->start, $this->end, null, $this->collection);
foreach ($this->collection as $item) { foreach ($this->collection as $item) {
$id = (int)$item->id; $id = (int)$item->id;
$this->spent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative', false)); $this->spent[$id] = array_values($opsRepository->sumCollectedTransactionsByCategory($expenses, $item, 'negative', false));

View File

@@ -33,7 +33,7 @@ interface EnrichmentInterface
{ {
public function enrich(Collection $collection): Collection; public function enrich(Collection $collection): Collection;
public function enrichSingle(array | Model $model): array | Model; public function enrichSingle(array|Model $model): array|Model;
public function setUser(User $user): void; public function setUser(User $user): void;

View File

@@ -43,13 +43,13 @@ use Illuminate\Support\Facades\Log;
class PiggyBankEnrichment implements EnrichmentInterface class PiggyBankEnrichment implements EnrichmentInterface
{ {
private array $accountIds = []; // @phpstan-ignore-line private array $accountIds = []; // @phpstan-ignore-line
private array $accounts = []; // @phpstan-ignore-line private array $accounts = []; // @phpstan-ignore-line
private array $amounts = []; private array $amounts = [];
private Collection $collection; private Collection $collection;
private array $currencies = []; private array $currencies = [];
private array $currencyIds = []; private array $currencyIds = [];
private array $ids = []; private array $ids = [];
// private array $accountCurrencies = []; // private array $accountCurrencies = [];
private array $mappedObjects = []; private array $mappedObjects = [];
private array $notes = []; private array $notes = [];
@@ -77,7 +77,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
return $this->collection; return $this->collection;
} }
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);
@@ -100,14 +100,14 @@ class PiggyBankEnrichment implements EnrichmentInterface
private function appendCollectedData(): void private function appendCollectedData(): void
{ {
$this->collection = $this->collection->map(function (PiggyBank $item) { $this->collection = $this->collection->map(function (PiggyBank $item) {
$id = (int)$item->id; $id = (int)$item->id;
$currencyId = (int)$item->transaction_currency_id; $currencyId = (int)$item->transaction_currency_id;
$currency = $this->currencies[$currencyId] ?? $this->primaryCurrency; $currency = $this->currencies[$currencyId] ?? $this->primaryCurrency;
$targetAmount = null; $targetAmount = null;
if (0 !== bccomp($item->target_amount, '0')) { if (0 !== bccomp($item->target_amount, '0')) {
$targetAmount = $item->target_amount; $targetAmount = $item->target_amount;
} }
$meta = [ $meta = [
'notes' => $this->notes[$id] ?? null, 'notes' => $this->notes[$id] ?? null,
'currency' => $this->currencies[$currencyId] ?? null, 'currency' => $this->currencies[$currencyId] ?? null,
// 'auto_budget' => $this->autoBudgets[$id] ?? null, // 'auto_budget' => $this->autoBudgets[$id] ?? null,
@@ -136,17 +136,17 @@ class PiggyBankEnrichment implements EnrichmentInterface
} }
// add current amount(s). // add current amount(s).
foreach ($this->amounts[$id] as $accountId => $row) { foreach ($this->amounts[$id] as $accountId => $row) {
$meta['accounts'][] = [ $meta['accounts'][] = [
'account_id' => (string)$accountId, 'account_id' => (string)$accountId,
'name' => $this->accounts[$accountId]['name'] ?? '', 'name' => $this->accounts[$accountId]['name'] ?? '',
'current_amount' => Steam::bcround($row['current_amount'], $currency->decimal_places), 'current_amount' => Steam::bcround($row['current_amount'], $currency->decimal_places),
'pc_current_amount' => Steam::bcround($row['pc_current_amount'], $this->primaryCurrency->decimal_places), 'pc_current_amount' => Steam::bcround($row['pc_current_amount'], $this->primaryCurrency->decimal_places),
]; ];
$meta['current_amount'] = bcadd($meta['current_amount'], $row['current_amount']); $meta['current_amount'] = bcadd($meta['current_amount'], $row['current_amount']);
// only add pc_current_amount when the pc_current_amount is set // only add pc_current_amount when the pc_current_amount is set
$meta['pc_current_amount'] = null === $row['pc_current_amount'] ? null : bcadd($meta['pc_current_amount'], $row['pc_current_amount']); $meta['pc_current_amount'] = null === $row['pc_current_amount'] ? null : bcadd($meta['pc_current_amount'], $row['pc_current_amount']);
} }
$meta['current_amount'] = Steam::bcround($meta['current_amount'], $currency->decimal_places); $meta['current_amount'] = Steam::bcround($meta['current_amount'], $currency->decimal_places);
// only round this number when pc_current_amount is set. // only round this number when pc_current_amount is set.
$meta['pc_current_amount'] = null === $meta['pc_current_amount'] ? null : Steam::bcround($meta['pc_current_amount'], $this->primaryCurrency->decimal_places); $meta['pc_current_amount'] = null === $meta['pc_current_amount'] ? null : Steam::bcround($meta['pc_current_amount'], $this->primaryCurrency->decimal_places);
@@ -160,7 +160,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
$meta['save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['target_amount'], $meta['current_amount']), $currency->decimal_places); $meta['save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['target_amount'], $meta['current_amount']), $currency->decimal_places);
$meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places); $meta['pc_save_per_month'] = Steam::bcround($this->getSuggestedMonthlyAmount($item->start_date, $item->target_date, $meta['pc_target_amount'], $meta['pc_current_amount']), $currency->decimal_places);
$item->meta = $meta; $item->meta = $meta;
return $item; return $item;
}); });
@@ -176,7 +176,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
$this->ids[] = $id; $this->ids[] = $id;
$this->currencyIds[$id] = (int)$piggy->transaction_currency_id; $this->currencyIds[$id] = (int)$piggy->transaction_currency_id;
} }
$this->ids = array_unique($this->ids); $this->ids = array_unique($this->ids);
// collect currencies. // collect currencies.
$currencies = TransactionCurrency::whereIn('id', $this->currencyIds)->get(); $currencies = TransactionCurrency::whereIn('id', $this->currencyIds)->get();
@@ -185,10 +185,10 @@ class PiggyBankEnrichment implements EnrichmentInterface
} }
// collect accounts // collect accounts
$set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->ids)->get(['piggy_bank_id', 'account_id', 'current_amount', 'native_current_amount']); $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->ids)->get(['piggy_bank_id', 'account_id', 'current_amount', 'native_current_amount']);
foreach ($set as $item) { foreach ($set as $item) {
$id = (int)$item->piggy_bank_id; $id = (int)$item->piggy_bank_id;
$accountId = (int)$item->account_id; $accountId = (int)$item->account_id;
$this->amounts[$id] ??= []; $this->amounts[$id] ??= [];
if (!array_key_exists($id, $this->accountIds)) { if (!array_key_exists($id, $this->accountIds)) {
$this->accountIds[$id] = (int)$item->account_id; $this->accountIds[$id] = (int)$item->account_id;
@@ -206,7 +206,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
} }
// get account currency preference for ALL. // get account currency preference for ALL.
$set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get();
/** @var AccountMeta $item */ /** @var AccountMeta $item */
foreach ($set as $item) { foreach ($set as $item) {
@@ -219,7 +219,7 @@ class PiggyBankEnrichment implements EnrichmentInterface
} }
// get account info. // get account info.
$set = Account::whereIn('id', array_values($this->accountIds))->get(); $set = Account::whereIn('id', array_values($this->accountIds))->get();
/** @var Account $item */ /** @var Account $item */
foreach ($set as $item) { foreach ($set as $item) {
@@ -234,9 +234,10 @@ class PiggyBankEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->ids) $notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', PiggyBank::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', PiggyBank::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text']; $this->notes[(int)$note['noteable_id']] = (string)$note['text'];
} }
@@ -245,12 +246,13 @@ class PiggyBankEnrichment implements EnrichmentInterface
private function collectObjectGroups(): void private function collectObjectGroups(): void
{ {
$set = DB::table('object_groupables') $set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->ids) ->whereIn('object_groupable_id', $this->ids)
->where('object_groupable_type', PiggyBank::class) ->where('object_groupable_type', PiggyBank::class)
->get(['object_groupable_id', 'object_group_id']); ->get(['object_groupable_id', 'object_group_id'])
;
$ids = array_unique($set->pluck('object_group_id')->toArray()); $ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) { foreach ($set as $entry) {
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;

View File

@@ -66,7 +66,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
return $this->collection; return $this->collection;
} }
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);
@@ -89,10 +89,10 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
private function appendCollectedData(): void private function appendCollectedData(): void
{ {
$this->collection = $this->collection->map(function (PiggyBankEvent $item) { $this->collection = $this->collection->map(function (PiggyBankEvent $item) {
$id = (int)$item->id; $id = (int)$item->id;
$piggyId = (int)$item->piggy_bank_id; $piggyId = (int)$item->piggy_bank_id;
$journalId = (int)$item->transaction_journal_id; $journalId = (int)$item->transaction_journal_id;
$currency = null; $currency = null;
if (array_key_exists($piggyId, $this->accountIds)) { if (array_key_exists($piggyId, $this->accountIds)) {
$accountId = $this->accountIds[$piggyId]; $accountId = $this->accountIds[$piggyId];
if (array_key_exists($accountId, $this->accountCurrencies)) { if (array_key_exists($accountId, $this->accountCurrencies)) {
@@ -120,7 +120,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
} }
$this->ids = array_unique($this->ids); $this->ids = array_unique($this->ids);
// collect groups with journal info. // collect groups with journal info.
$set = TransactionJournal::whereIn('id', $this->journalIds)->get(['id', 'transaction_group_id']); $set = TransactionJournal::whereIn('id', $this->journalIds)->get(['id', 'transaction_group_id']);
/** @var TransactionJournal $item */ /** @var TransactionJournal $item */
foreach ($set as $item) { foreach ($set as $item) {
@@ -128,7 +128,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
} }
// collect account info. // collect account info.
$set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggyBankIds)->get(['piggy_bank_id', 'account_id']); $set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggyBankIds)->get(['piggy_bank_id', 'account_id']);
foreach ($set as $item) { foreach ($set as $item) {
$id = (int)$item->piggy_bank_id; $id = (int)$item->piggy_bank_id;
if (!array_key_exists($id, $this->accountIds)) { if (!array_key_exists($id, $this->accountIds)) {
@@ -137,12 +137,12 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
} }
// get account currency preference for ALL. // get account currency preference for ALL.
$set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get(); $set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get();
/** @var AccountMeta $item */ /** @var AccountMeta $item */
foreach ($set as $item) { foreach ($set as $item) {
$accountId = (int)$item->account_id; $accountId = (int)$item->account_id;
$currencyId = (int)$item->data; $currencyId = (int)$item->data;
if (!array_key_exists($currencyId, $this->currencies)) { if (!array_key_exists($currencyId, $this->currencies)) {
$this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId); $this->currencies[$currencyId] = Amount::getTransactionCurrencyById($currencyId);
} }

View File

@@ -51,11 +51,12 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use function Safe\json_decode; use function Safe\json_decode;
class RecurringEnrichment implements EnrichmentInterface class RecurringEnrichment implements EnrichmentInterface
{ {
private array $accounts = []; private array $accounts = [];
private Collection $collection; private Collection $collection;
// private array $transactionTypeIds = []; // private array $transactionTypeIds = [];
// private array $transactionTypes = []; // private array $transactionTypes = [];
@@ -97,7 +98,7 @@ class RecurringEnrichment implements EnrichmentInterface
return $this->collection; return $this->collection;
} }
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);
@@ -131,7 +132,7 @@ class RecurringEnrichment implements EnrichmentInterface
return (string)trans('firefly.recurring_monthly', ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip - 1], $this->language); return (string)trans('firefly.recurring_monthly', ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip - 1], $this->language);
} }
if ('ndom' === $repetition->repetition_type) { if ('ndom' === $repetition->repetition_type) {
$parts = explode(',', $repetition->repetition_moment); $parts = explode(',', $repetition->repetition_moment);
// first part is number of week, second is weekday. // first part is number of week, second is weekday.
$dayOfWeek = trans(sprintf('config.dow_%s', $parts[1]), [], $this->language); $dayOfWeek = trans(sprintf('config.dow_%s', $parts[1]), [], $this->language);
if ($repetition->repetition_skip > 0) { if ($repetition->repetition_skip > 0) {
@@ -148,7 +149,7 @@ class RecurringEnrichment implements EnrichmentInterface
} }
// $diffInYears = (int)$today->diffInYears($repDate, true); // $diffInYears = (int)$today->diffInYears($repDate, true);
// $repDate->addYears($diffInYears); // technically not necessary. // $repDate->addYears($diffInYears); // technically not necessary.
$string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js')); $string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js'));
return (string)trans('firefly.recurring_yearly', ['date' => $string], $this->language); return (string)trans('firefly.recurring_yearly', ['date' => $string], $this->language);
} }
@@ -171,8 +172,8 @@ class RecurringEnrichment implements EnrichmentInterface
private function appendCollectedData(): void private function appendCollectedData(): void
{ {
$this->collection = $this->collection->map(function (Recurrence $item) { $this->collection = $this->collection->map(function (Recurrence $item) {
$id = (int)$item->id; $id = (int)$item->id;
$meta = [ $meta = [
'notes' => $this->notes[$id] ?? null, 'notes' => $this->notes[$id] ?? null,
'repetitions' => array_values($this->repetitions[$id] ?? []), 'repetitions' => array_values($this->repetitions[$id] ?? []),
'transactions' => $this->processTransactions(array_values($this->transactions[$id] ?? [])), 'transactions' => $this->processTransactions(array_values($this->transactions[$id] ?? [])),
@@ -285,7 +286,7 @@ class RecurringEnrichment implements EnrichmentInterface
{ {
/** @var Recurrence $recurrence */ /** @var Recurrence $recurrence */
foreach ($this->collection as $recurrence) { foreach ($this->collection as $recurrence) {
$id = (int)$recurrence->id; $id = (int)$recurrence->id;
// $typeId = (int)$recurrence->transaction_type_id; // $typeId = (int)$recurrence->transaction_type_id;
$this->ids[] = $id; $this->ids[] = $id;
// $this->transactionTypeIds[$id] = $typeId; // $this->transactionTypeIds[$id] = $typeId;
@@ -303,9 +304,10 @@ class RecurringEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->ids) $notes = Note::query()->whereIn('noteable_id', $this->ids)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', Recurrence::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', Recurrence::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text']; $this->notes[(int)$note['noteable_id']] = (string)$note['text'];
} }
@@ -335,20 +337,20 @@ class RecurringEnrichment implements EnrichmentInterface
Log::debug('Start of enrichment: collectRepetitions()'); Log::debug('Start of enrichment: collectRepetitions()');
$repository = app(RecurringRepositoryInterface::class); $repository = app(RecurringRepositoryInterface::class);
$repository->setUserGroup($this->userGroup); $repository->setUserGroup($this->userGroup);
$set = RecurrenceRepetition::whereIn('recurrence_id', $this->ids)->get(); $set = RecurrenceRepetition::whereIn('recurrence_id', $this->ids)->get();
/** @var RecurrenceRepetition $repetition */ /** @var RecurrenceRepetition $repetition */
foreach ($set as $repetition) { foreach ($set as $repetition) {
$recurrence = $this->collection->filter(fn(Recurrence $item) => (int)$item->id === (int)$repetition->recurrence_id)->first(); $recurrence = $this->collection->filter(fn (Recurrence $item) => (int)$item->id === (int)$repetition->recurrence_id)->first();
$fromDate = clone($recurrence->latest_date ?? $recurrence->first_date); $fromDate = clone ($recurrence->latest_date ?? $recurrence->first_date);
$id = (int)$repetition->recurrence_id; $id = (int)$repetition->recurrence_id;
$repId = (int)$repetition->id; $repId = (int)$repetition->id;
$this->repetitions[$id] ??= []; $this->repetitions[$id] ??= [];
// get the (future) occurrences for this specific type of repetition: // get the (future) occurrences for this specific type of repetition:
$amount = 'daily' === $repetition->repetition_type ? 9 : 5; $amount = 'daily' === $repetition->repetition_type ? 9 : 5;
$set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount); $set = $repository->getXOccurrencesSince($repetition, $fromDate, now(config('app.timezone')), $amount);
$occurrences = []; $occurrences = [];
/** @var Carbon $carbon */ /** @var Carbon $carbon */
foreach ($set as $carbon) { foreach ($set as $carbon) {
@@ -371,8 +373,8 @@ class RecurringEnrichment implements EnrichmentInterface
private function collectTransactionMetaData(): void private function collectTransactionMetaData(): void
{ {
$ids = array_keys($this->transactions); $ids = array_keys($this->transactions);
$meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get(); $meta = RecurrenceTransactionMeta::whereNull('deleted_at')->whereIn('rt_id', $ids)->get();
// other meta-data to be collected: // other meta-data to be collected:
$billIds = []; $billIds = [];
$piggyBankIds = []; $piggyBankIds = [];
@@ -384,8 +386,8 @@ class RecurringEnrichment implements EnrichmentInterface
$transactionId = (int)$entry->rt_id; $transactionId = (int)$entry->rt_id;
// this should refer to another array, were rtIds can be used to find the recurrence. // this should refer to another array, were rtIds can be used to find the recurrence.
$recurrenceId = $this->recurrenceIds[$transactionId] ?? 0; $recurrenceId = $this->recurrenceIds[$transactionId] ?? 0;
$name = (string)($entry->name ?? ''); $name = (string)($entry->name ?? '');
if (0 === $recurrenceId) { if (0 === $recurrenceId) {
Log::error(sprintf('Could not find recurrence ID for recurrence transaction ID %d', $transactionId)); Log::error(sprintf('Could not find recurrence ID for recurrence transaction ID %d', $transactionId));
@@ -485,14 +487,14 @@ class RecurringEnrichment implements EnrichmentInterface
/** @var RecurrenceTransaction $transaction */ /** @var RecurrenceTransaction $transaction */
foreach ($set as $transaction) { foreach ($set as $transaction) {
$id = (int)$transaction->recurrence_id; $id = (int)$transaction->recurrence_id;
$transactionId = (int)$transaction->id; $transactionId = (int)$transaction->id;
$this->recurrenceIds[$transactionId] = $id; $this->recurrenceIds[$transactionId] = $id;
$this->transactions[$id] ??= []; $this->transactions[$id] ??= [];
$amount = $transaction->amount; $amount = $transaction->amount;
$foreignAmount = $transaction->foreign_amount; $foreignAmount = $transaction->foreign_amount;
$this->transactions[$id][$transactionId] = [ $this->transactions[$id][$transactionId] = [
'id' => (string)$transactionId, 'id' => (string)$transactionId,
// 'recurrence_id' => $id, // 'recurrence_id' => $id,
'transaction_currency_id' => (int)$transaction->transaction_currency_id, 'transaction_currency_id' => (int)$transaction->transaction_currency_id,
@@ -529,8 +531,8 @@ class RecurringEnrichment implements EnrichmentInterface
private function getLanguage(): void private function getLanguage(): void
{ {
/** @var Preference $preference */ /** @var Preference $preference */
$preference = Preferences::getForUser($this->user, 'language', config('firefly.default_language', 'en_US')); $preference = Preferences::getForUser($this->user, 'language', config('firefly.default_language', 'en_US'));
$language = $preference->data; $language = $preference->data;
if (is_array($language)) { if (is_array($language)) {
$language = 'en_US'; $language = 'en_US';
} }
@@ -543,9 +545,9 @@ class RecurringEnrichment implements EnrichmentInterface
$return = []; $return = [];
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
foreach ($transactions as $transaction) { foreach ($transactions as $transaction) {
$currencyId = $transaction['transaction_currency_id']; $currencyId = $transaction['transaction_currency_id'];
$pcAmount = null; $pcAmount = null;
$pcForeignAmount = null; $pcForeignAmount = null;
// set the same amount in the primary currency, if both are the same anyway. // set the same amount in the primary currency, if both are the same anyway.
if (true === $this->convertToPrimary && $currencyId === (int)$this->primaryCurrency->id) { if (true === $this->convertToPrimary && $currencyId === (int)$this->primaryCurrency->id) {
$pcAmount = $transaction['amount']; $pcAmount = $transaction['amount'];
@@ -561,26 +563,26 @@ class RecurringEnrichment implements EnrichmentInterface
} }
} }
$transaction['pc_amount'] = $pcAmount; $transaction['pc_amount'] = $pcAmount;
$transaction['pc_foreign_amount'] = $pcForeignAmount; $transaction['pc_foreign_amount'] = $pcForeignAmount;
$sourceId = $transaction['source_id']; $sourceId = $transaction['source_id'];
$transaction['source_name'] = $this->accounts[$sourceId]->name; $transaction['source_name'] = $this->accounts[$sourceId]->name;
$transaction['source_iban'] = $this->accounts[$sourceId]->iban; $transaction['source_iban'] = $this->accounts[$sourceId]->iban;
$transaction['source_type'] = $this->accounts[$sourceId]->accountType->type; $transaction['source_type'] = $this->accounts[$sourceId]->accountType->type;
$transaction['source_id'] = (string)$transaction['source_id']; $transaction['source_id'] = (string)$transaction['source_id'];
$destId = $transaction['destination_id']; $destId = $transaction['destination_id'];
$transaction['destination_name'] = $this->accounts[$destId]->name; $transaction['destination_name'] = $this->accounts[$destId]->name;
$transaction['destination_iban'] = $this->accounts[$destId]->iban; $transaction['destination_iban'] = $this->accounts[$destId]->iban;
$transaction['destination_type'] = $this->accounts[$destId]->accountType->type; $transaction['destination_type'] = $this->accounts[$destId]->accountType->type;
$transaction['destination_id'] = (string)$transaction['destination_id']; $transaction['destination_id'] = (string)$transaction['destination_id'];
$transaction['currency_id'] = (string)$currencyId; $transaction['currency_id'] = (string)$currencyId;
$transaction['currency_name'] = $this->currencies[$currencyId]->name; $transaction['currency_name'] = $this->currencies[$currencyId]->name;
$transaction['currency_code'] = $this->currencies[$currencyId]->code; $transaction['currency_code'] = $this->currencies[$currencyId]->code;
$transaction['currency_symbol'] = $this->currencies[$currencyId]->symbol; $transaction['currency_symbol'] = $this->currencies[$currencyId]->symbol;
$transaction['currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; $transaction['currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places;
$transaction['primary_currency_id'] = (string)$this->primaryCurrency->id; $transaction['primary_currency_id'] = (string)$this->primaryCurrency->id;
$transaction['primary_currency_name'] = $this->primaryCurrency->name; $transaction['primary_currency_name'] = $this->primaryCurrency->name;
@@ -602,7 +604,7 @@ class RecurringEnrichment implements EnrichmentInterface
$transaction['foreign_currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places; $transaction['foreign_currency_decimal_places'] = $this->currencies[$currencyId]->decimal_places;
} }
unset($transaction['transaction_currency_id']); unset($transaction['transaction_currency_id']);
$return[] = $transaction; $return[] = $transaction;
} }
return $return; return $return;

View File

@@ -86,11 +86,11 @@ class SubscriptionEnrichment implements EnrichmentInterface
$paidDates = $this->paidDates; $paidDates = $this->paidDates;
$payDates = $this->payDates; $payDates = $this->payDates;
$this->collection = $this->collection->map(function (Bill $item) use ($notes, $objectGroups, $paidDates, $payDates) { $this->collection = $this->collection->map(function (Bill $item) use ($notes, $objectGroups, $paidDates, $payDates) {
$id = (int)$item->id; $id = (int)$item->id;
$currency = $item->transactionCurrency; $currency = $item->transactionCurrency;
$nem = $this->getNextExpectedMatch($payDates[$id] ?? []); $nem = $this->getNextExpectedMatch($payDates[$id] ?? []);
$meta = [ $meta = [
'notes' => null, 'notes' => null,
'object_group_id' => null, 'object_group_id' => null,
'object_group_title' => null, 'object_group_title' => null,
@@ -101,7 +101,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
'nem' => $nem, 'nem' => $nem,
'nem_diff' => $this->getNextExpectedMatchDiff($nem, $payDates[$id] ?? []), 'nem_diff' => $this->getNextExpectedMatchDiff($nem, $payDates[$id] ?? []),
]; ];
$amounts = [ $amounts = [
'amount_min' => Steam::bcround($item->amount_min, $currency->decimal_places), 'amount_min' => Steam::bcround($item->amount_min, $currency->decimal_places),
'amount_max' => Steam::bcround($item->amount_max, $currency->decimal_places), 'amount_max' => Steam::bcround($item->amount_max, $currency->decimal_places),
'average' => Steam::bcround(bcdiv(bcadd($item->amount_min, $item->amount_max), '2'), $currency->decimal_places), 'average' => Steam::bcround(bcdiv(bcadd($item->amount_min, $item->amount_max), '2'), $currency->decimal_places),
@@ -142,7 +142,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
return $collection; return $collection;
} }
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);
@@ -177,13 +177,13 @@ class SubscriptionEnrichment implements EnrichmentInterface
*/ */
protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon protected function lastPaidDate(Bill $subscription, Collection $dates, Carbon $default): Carbon
{ {
$filtered = $dates->filter(fn(TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); $filtered = $dates->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id);
Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id)); Log::debug(sprintf('Filtered down from %d to %d entries for bill #%d.', $dates->count(), $filtered->count(), $subscription->id));
if (0 === $filtered->count()) { if (0 === $filtered->count()) {
return $default; return $default;
} }
$latest = $filtered->first()->date; $latest = $filtered->first()->date;
/** @var TransactionJournal $journal */ /** @var TransactionJournal $journal */
foreach ($filtered as $journal) { foreach ($filtered as $journal) {
@@ -198,9 +198,10 @@ class SubscriptionEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->subscriptionIds) $notes = Note::query()->whereIn('noteable_id', $this->subscriptionIds)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', Bill::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text']; $this->notes[(int)$note['noteable_id']] = (string)$note['text'];
} }
@@ -209,12 +210,13 @@ class SubscriptionEnrichment implements EnrichmentInterface
private function collectObjectGroups(): void private function collectObjectGroups(): void
{ {
$set = DB::table('object_groupables') $set = DB::table('object_groupables')
->whereIn('object_groupable_id', $this->subscriptionIds) ->whereIn('object_groupable_id', $this->subscriptionIds)
->where('object_groupable_type', Bill::class) ->where('object_groupable_type', Bill::class)
->get(['object_groupable_id', 'object_group_id']); ->get(['object_groupable_id', 'object_group_id'])
;
$ids = array_unique($set->pluck('object_group_id')->toArray()); $ids = array_unique($set->pluck('object_group_id')->toArray());
foreach ($set as $entry) { foreach ($set as $entry) {
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id; $this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;
@@ -242,13 +244,13 @@ class SubscriptionEnrichment implements EnrichmentInterface
// 2023-07-18 this particular date is used to search for the last paid date. // 2023-07-18 this particular date is used to search for the last paid date.
// 2023-07-18 the cloned $searchDate is used to grab the correct transactions. // 2023-07-18 the cloned $searchDate is used to grab the correct transactions.
/** @var Carbon $start */ /** @var Carbon $start */
$start = clone $this->start; $start = clone $this->start;
$searchStart = clone $start; $searchStart = clone $start;
$start->subDay(); $start->subDay();
/** @var Carbon $end */ /** @var Carbon $end */
$end = clone $this->end; $end = clone $this->end;
$searchEnd = clone $end; $searchEnd = clone $end;
// move the search dates to the start of the day. // move the search dates to the start of the day.
$searchStart->startOfDay(); $searchStart->startOfDay();
@@ -257,13 +259,13 @@ class SubscriptionEnrichment implements EnrichmentInterface
Log::debug(sprintf('Search parameters are: start: %s, end: %s', $searchStart->format('Y-m-d H:i:s'), $searchEnd->format('Y-m-d H:i:s'))); Log::debug(sprintf('Search parameters are: start: %s, end: %s', $searchStart->format('Y-m-d H:i:s'), $searchEnd->format('Y-m-d H:i:s')));
// Get from database when bills were paid. // Get from database when bills were paid.
$set = $this->user->transactionJournals() $set = $this->user->transactionJournals()
->whereIn('bill_id', $this->subscriptionIds) ->whereIn('bill_id', $this->subscriptionIds)
->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id') ->leftJoin('transaction_currencies AS currency', 'currency.id', '=', 'transactions.transaction_currency_id')
->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id') ->leftJoin('transaction_currencies AS foreign_currency', 'foreign_currency.id', '=', 'transactions.foreign_currency_id')
->where('transactions.amount', '>', 0) ->where('transactions.amount', '>', 0)
->before($searchEnd)->after($searchStart)->get( ->before($searchEnd)->after($searchStart)->get(
[ [
'transaction_journals.id', 'transaction_journals.id',
'transaction_journals.date', 'transaction_journals.date',
@@ -280,24 +282,25 @@ class SubscriptionEnrichment implements EnrichmentInterface
'transactions.amount', 'transactions.amount',
'transactions.foreign_amount', 'transactions.foreign_amount',
] ]
); )
;
Log::debug(sprintf('Count %d entries in set', $set->count())); Log::debug(sprintf('Count %d entries in set', $set->count()));
// for each bill, do a loop. // for each bill, do a loop.
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
/** @var Bill $subscription */ /** @var Bill $subscription */
foreach ($this->collection as $subscription) { foreach ($this->collection as $subscription) {
// Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date. // Grab from array the most recent payment. If none exist, fall back to the start date and pretend *that* was the last paid date.
Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $start->format('Y-m-d'))); Log::debug(sprintf('Grab last paid date from function, return %s if it comes up with nothing.', $start->format('Y-m-d')));
$lastPaidDate = $this->lastPaidDate($subscription, $set, $start); $lastPaidDate = $this->lastPaidDate($subscription, $set, $start);
Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d'))); Log::debug(sprintf('Result of lastPaidDate is %s', $lastPaidDate->format('Y-m-d')));
// At this point the "next match" is exactly after the last time the bill was paid. // At this point the "next match" is exactly after the last time the bill was paid.
$result = []; $result = [];
$filtered = $set->filter(fn(TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id); $filtered = $set->filter(fn (TransactionJournal $journal) => (int)$journal->bill_id === (int)$subscription->id);
foreach ($filtered as $entry) { foreach ($filtered as $entry) {
$array = [ $array = [
'transaction_group_id' => (string)$entry->transaction_group_id, 'transaction_group_id' => (string)$entry->transaction_group_id,
'transaction_journal_id' => (string)$entry->id, 'transaction_journal_id' => (string)$entry->id,
'date' => $entry->date->toAtomString(), 'date' => $entry->date->toAtomString(),
@@ -360,12 +363,12 @@ class SubscriptionEnrichment implements EnrichmentInterface
/** @var Bill $subscription */ /** @var Bill $subscription */
foreach ($this->collection as $subscription) { foreach ($this->collection as $subscription) {
$id = (int)$subscription->id; $id = (int)$subscription->id;
$lastPaidDate = $this->getLastPaidDate($this->paidDates[$id] ?? []); $lastPaidDate = $this->getLastPaidDate($this->paidDates[$id] ?? []);
$payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate); $payDates = $this->calculator->getPayDates($this->start, $this->end, $subscription->date, $subscription->repeat_freq, $subscription->skip, $lastPaidDate);
$payDatesFormatted = []; $payDatesFormatted = [];
foreach ($payDates as $string) { foreach ($payDates as $string) {
$date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone')); $date = Carbon::createFromFormat('!Y-m-d', $string, config('app.timezone'));
if (!$date instanceof Carbon) { if (!$date instanceof Carbon) {
$date = today(config('app.timezone')); $date = today(config('app.timezone'));
} }

View File

@@ -83,7 +83,7 @@ class TransactionGroupEnrichment implements EnrichmentInterface
} }
#[Override] #[Override]
public function enrichSingle(array | Model $model): array | TransactionGroup public function enrichSingle(array|Model $model): array|TransactionGroup
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
if (is_array($model)) { if (is_array($model)) {
@@ -109,28 +109,28 @@ class TransactionGroupEnrichment implements EnrichmentInterface
private function appendCollectedData(): void private function appendCollectedData(): void
{ {
$notes = $this->notes; $notes = $this->notes;
$tags = $this->tags; $tags = $this->tags;
$metaData = $this->metaData; $metaData = $this->metaData;
$locations = $this->locations; $locations = $this->locations;
$attachmentCount = $this->attachmentCount; $attachmentCount = $this->attachmentCount;
$primaryCurrency = $this->primaryCurrency; $primaryCurrency = $this->primaryCurrency;
$this->collection = $this->collection->map(function (array $item) use ($primaryCurrency, $notes, $tags, $metaData, $locations, $attachmentCount) { $this->collection = $this->collection->map(function (array $item) use ($primaryCurrency, $notes, $tags, $metaData, $locations, $attachmentCount) {
foreach ($item['transactions'] as $index => $transaction) { foreach ($item['transactions'] as $index => $transaction) {
$journalId = (int)$transaction['transaction_journal_id']; $journalId = (int)$transaction['transaction_journal_id'];
// attach notes if they exist: // attach notes if they exist:
$item['transactions'][$index]['notes'] = array_key_exists($journalId, $notes) ? $notes[$journalId] : null; $item['transactions'][$index]['notes'] = array_key_exists($journalId, $notes) ? $notes[$journalId] : null;
// attach tags if they exist: // attach tags if they exist:
$item['transactions'][$index]['tags'] = array_key_exists($journalId, $tags) ? $tags[$journalId] : []; $item['transactions'][$index]['tags'] = array_key_exists($journalId, $tags) ? $tags[$journalId] : [];
// attachment count // attachment count
$item['transactions'][$index]['attachment_count'] = array_key_exists($journalId, $attachmentCount) ? $attachmentCount[$journalId] : 0; $item['transactions'][$index]['attachment_count'] = array_key_exists($journalId, $attachmentCount) ? $attachmentCount[$journalId] : 0;
// default location data // default location data
$item['transactions'][$index]['location'] = [ $item['transactions'][$index]['location'] = [
'latitude' => null, 'latitude' => null,
'longitude' => null, 'longitude' => null,
'zoom_level' => null, 'zoom_level' => null,
@@ -146,8 +146,8 @@ class TransactionGroupEnrichment implements EnrichmentInterface
]; ];
// append meta data // append meta data
$item['transactions'][$index]['meta'] = []; $item['transactions'][$index]['meta'] = [];
$item['transactions'][$index]['meta_date'] = []; $item['transactions'][$index]['meta_date'] = [];
if (array_key_exists($journalId, $metaData)) { if (array_key_exists($journalId, $metaData)) {
// loop al meta data: // loop al meta data:
foreach ($metaData[$journalId] as $name => $value) { foreach ($metaData[$journalId] as $name => $value) {
@@ -175,11 +175,12 @@ class TransactionGroupEnrichment implements EnrichmentInterface
// select count(id) as nr_of_attachments, attachable_id from attachments // select count(id) as nr_of_attachments, attachable_id from attachments
// group by attachable_id // group by attachable_id
$attachments = Attachment::query() $attachments = Attachment::query()
->whereIn('attachable_id', $this->journalIds) ->whereIn('attachable_id', $this->journalIds)
->where('attachable_type', TransactionJournal::class) ->where('attachable_type', TransactionJournal::class)
->groupBy('attachable_id') ->groupBy('attachable_id')
->get(['attachable_id', DB::raw('COUNT(id) as nr_of_attachments')]) ->get(['attachable_id', DB::raw('COUNT(id) as nr_of_attachments')])
->toArray(); ->toArray()
;
foreach ($attachments as $row) { foreach ($attachments as $row) {
$this->attachmentCount[(int)$row['attachable_id']] = (int)$row['nr_of_attachments']; $this->attachmentCount[(int)$row['attachable_id']] = (int)$row['nr_of_attachments'];
} }
@@ -199,14 +200,15 @@ class TransactionGroupEnrichment implements EnrichmentInterface
private function collectLocations(): void private function collectLocations(): void
{ {
$locations = Location::query()->whereIn('locatable_id', $this->journalIds) $locations = Location::query()->whereIn('locatable_id', $this->journalIds)
->where('locatable_type', TransactionJournal::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray(); ->where('locatable_type', TransactionJournal::class)->get(['locations.locatable_id', 'locations.latitude', 'locations.longitude', 'locations.zoom_level'])->toArray()
;
foreach ($locations as $location) { foreach ($locations as $location) {
$this->locations[(int)$location['locatable_id']] $this->locations[(int)$location['locatable_id']]
= [ = [
'latitude' => (float)$location['latitude'], 'latitude' => (float)$location['latitude'],
'longitude' => (float)$location['longitude'], 'longitude' => (float)$location['longitude'],
'zoom_level' => (int)$location['zoom_level'], 'zoom_level' => (int)$location['zoom_level'],
]; ];
} }
Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations))); Log::debug(sprintf('Enrich with %d locations(s)', count($this->locations)));
} }
@@ -215,8 +217,8 @@ class TransactionGroupEnrichment implements EnrichmentInterface
{ {
$set = TransactionJournalMeta::whereIn('transaction_journal_id', $this->journalIds)->get(['transaction_journal_id', 'name', 'data'])->toArray(); $set = TransactionJournalMeta::whereIn('transaction_journal_id', $this->journalIds)->get(['transaction_journal_id', 'name', 'data'])->toArray();
foreach ($set as $entry) { foreach ($set as $entry) {
$name = $entry['name']; $name = $entry['name'];
$data = (string)$entry['data']; $data = (string)$entry['data'];
if ('' === $data) { if ('' === $data) {
continue; continue;
} }
@@ -234,9 +236,10 @@ class TransactionGroupEnrichment implements EnrichmentInterface
private function collectNotes(): void private function collectNotes(): void
{ {
$notes = Note::query()->whereIn('noteable_id', $this->journalIds) $notes = Note::query()->whereIn('noteable_id', $this->journalIds)
->whereNotNull('notes.text') ->whereNotNull('notes.text')
->where('notes.text', '!=', '') ->where('notes.text', '!=', '')
->where('noteable_type', TransactionJournal::class)->get(['notes.noteable_id', 'notes.text'])->toArray(); ->where('noteable_type', TransactionJournal::class)->get(['notes.noteable_id', 'notes.text'])->toArray()
;
foreach ($notes as $note) { foreach ($notes as $note) {
$this->notes[(int)$note['noteable_id']] = (string)$note['text']; $this->notes[(int)$note['noteable_id']] = (string)$note['text'];
} }
@@ -246,11 +249,12 @@ class TransactionGroupEnrichment implements EnrichmentInterface
private function collectTags(): void private function collectTags(): void
{ {
$set = Tag::leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id') $set = Tag::leftJoin('tag_transaction_journal', 'tags.id', '=', 'tag_transaction_journal.tag_id')
->whereIn('tag_transaction_journal.transaction_journal_id', $this->journalIds) ->whereIn('tag_transaction_journal.transaction_journal_id', $this->journalIds)
->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag'])->toArray(); ->get(['tag_transaction_journal.transaction_journal_id', 'tags.tag'])->toArray()
;
foreach ($set as $item) { foreach ($set as $item) {
$journalId = $item['transaction_journal_id']; $journalId = $item['transaction_journal_id'];
$this->tags[$journalId] ??= []; $this->tags[$journalId] ??= [];
$this->tags[$journalId][] = $item['tag']; $this->tags[$journalId][] = $item['tag'];
} }
} }

View File

@@ -66,7 +66,7 @@ class WebhookEnrichment implements EnrichmentInterface
return $this->collection; return $this->collection;
} }
public function enrichSingle(array | Model $model): array | Model public function enrichSingle(array|Model $model): array|Model
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$collection = new Collection()->push($model); $collection = new Collection()->push($model);

View File

@@ -65,9 +65,9 @@ class AccountBalanceCalculator
public static function recalculateForJournal(TransactionJournal $transactionJournal): void public static function recalculateForJournal(TransactionJournal $transactionJournal): void
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
$object = new self(); $object = new self();
$set = []; $set = [];
foreach ($transactionJournal->transactions as $transaction) { foreach ($transactionJournal->transactions as $transaction) {
$set[$transaction->account_id] = $transaction->account; $set[$transaction->account_id] = $transaction->account;
} }
@@ -81,17 +81,18 @@ class AccountBalanceCalculator
return '0'; return '0';
} }
Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d'))); Log::debug(sprintf('getLatestBalance: notBefore date is "%s", calculating', $notBefore->format('Y-m-d')));
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereNull('transactions.deleted_at') ->whereNull('transactions.deleted_at')
->where('transaction_journals.transaction_currency_id', $currencyId) ->where('transaction_journals.transaction_currency_id', $currencyId)
->whereNull('transaction_journals.deleted_at') ->whereNull('transaction_journals.deleted_at')
// this order is the same as GroupCollector // this order is the same as GroupCollector
->orderBy('transaction_journals.date', 'DESC') ->orderBy('transaction_journals.date', 'DESC')
->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.order', 'ASC')
->orderBy('transaction_journals.id', 'DESC') ->orderBy('transaction_journals.id', 'DESC')
->orderBy('transaction_journals.description', 'DESC') ->orderBy('transaction_journals.description', 'DESC')
->orderBy('transactions.amount', 'DESC') ->orderBy('transactions.amount', 'DESC')
->where('transactions.account_id', $accountId); ->where('transactions.account_id', $accountId)
;
$notBefore->startOfDay(); $notBefore->startOfDay();
$query->where('transaction_journals.date', '<', $notBefore); $query->where('transaction_journals.date', '<', $notBefore);
@@ -112,14 +113,15 @@ class AccountBalanceCalculator
$balances = []; $balances = [];
$count = 0; $count = 0;
$query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->whereNull('transactions.deleted_at') ->whereNull('transactions.deleted_at')
->whereNull('transaction_journals.deleted_at') ->whereNull('transaction_journals.deleted_at')
// this order is the same as GroupCollector, but in the exact reverse. // this order is the same as GroupCollector, but in the exact reverse.
->orderBy('transaction_journals.date', 'asc') ->orderBy('transaction_journals.date', 'asc')
->orderBy('transaction_journals.order', 'desc') ->orderBy('transaction_journals.order', 'desc')
->orderBy('transaction_journals.id', 'asc') ->orderBy('transaction_journals.id', 'asc')
->orderBy('transaction_journals.description', 'asc') ->orderBy('transaction_journals.description', 'asc')
->orderBy('transactions.amount', 'asc'); ->orderBy('transactions.amount', 'asc')
;
if ($accounts->count() > 0) { if ($accounts->count() > 0) {
$query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()); $query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray());
} }
@@ -128,7 +130,7 @@ class AccountBalanceCalculator
$query->where('transaction_journals.date', '>=', $notBefore); $query->where('transaction_journals.date', '>=', $notBefore);
} }
$set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']); $set = $query->get(['transactions.id', 'transactions.balance_dirty', 'transactions.transaction_currency_id', 'transaction_journals.date', 'transactions.account_id', 'transactions.amount']);
Log::debug(sprintf('Counted %d transaction(s)', $set->count())); Log::debug(sprintf('Counted %d transaction(s)', $set->count()));
// the balance value is an array. // the balance value is an array.
@@ -141,8 +143,8 @@ class AccountBalanceCalculator
$balances[$entry->account_id][$entry->transaction_currency_id] ??= [$this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore), null]; $balances[$entry->account_id][$entry->transaction_currency_id] ??= [$this->getLatestBalance($entry->account_id, $entry->transaction_currency_id, $notBefore), null];
// before and after are easy: // before and after are easy:
$before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; $before = $balances[$entry->account_id][$entry->transaction_currency_id][0];
$after = bcadd($before, (string)$entry->amount); $after = bcadd($before, (string)$entry->amount);
if (true === $entry->balance_dirty || $accounts->count() > 0) { if (true === $entry->balance_dirty || $accounts->count() > 0) {
// update the transaction: // update the transaction:
$entry->balance_before = $before; $entry->balance_before = $before;

View File

@@ -49,15 +49,15 @@ class BillDateCalculator
Log::debug(sprintf('Dates must be between %s and %s.', $earliest->format('Y-m-d'), $latest->format('Y-m-d'))); Log::debug(sprintf('Dates must be between %s and %s.', $earliest->format('Y-m-d'), $latest->format('Y-m-d')));
Log::debug(sprintf('Bill started on %s, period is "%s", skip is %d, last paid = "%s".', $billStart->format('Y-m-d'), $period, $skip, $lastPaid?->format('Y-m-d'))); Log::debug(sprintf('Bill started on %s, period is "%s", skip is %d, last paid = "%s".', $billStart->format('Y-m-d'), $period, $skip, $lastPaid?->format('Y-m-d')));
$daysUntilEOM = app('navigation')->daysUntilEndOfMonth($billStart); $daysUntilEOM = app('navigation')->daysUntilEndOfMonth($billStart);
Log::debug(sprintf('For bill start, days until end of month is %d', $daysUntilEOM)); Log::debug(sprintf('For bill start, days until end of month is %d', $daysUntilEOM));
$set = new Collection(); $set = new Collection();
$currentStart = clone $earliest; $currentStart = clone $earliest;
// 2023-06-23 subDay to fix 7655 // 2023-06-23 subDay to fix 7655
$currentStart->subDay(); $currentStart->subDay();
$loop = 0; $loop = 0;
Log::debug('Start of loop'); Log::debug('Start of loop');
while ($currentStart <= $latest) { while ($currentStart <= $latest) {
@@ -107,7 +107,7 @@ class BillDateCalculator
// for the next loop, go to end of period, THEN add day. // for the next loop, go to end of period, THEN add day.
Log::debug('Add one day to nextExpectedMatch/currentStart.'); Log::debug('Add one day to nextExpectedMatch/currentStart.');
$nextExpectedMatch->addDay(); $nextExpectedMatch->addDay();
$currentStart = clone $nextExpectedMatch; $currentStart = clone $nextExpectedMatch;
++$loop; ++$loop;
if ($loop > 31) { if ($loop > 31) {
@@ -117,8 +117,8 @@ class BillDateCalculator
} }
} }
Log::debug('end of loop'); Log::debug('end of loop');
$simple = $set->map( // @phpstan-ignore-line $simple = $set->map( // @phpstan-ignore-line
static fn(Carbon $date) => $date->format('Y-m-d') static fn (Carbon $date) => $date->format('Y-m-d')
); );
Log::debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray()); Log::debug(sprintf('Found %d pay dates', $set->count()), $simple->toArray());
@@ -140,7 +140,7 @@ class BillDateCalculator
return $billStartDate; return $billStartDate;
} }
$steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate); $steps = app('navigation')->diffInPeriods($period, $skip, $earliest, $billStartDate);
if ($steps === $this->diffInMonths) { if ($steps === $this->diffInMonths) {
Log::debug(sprintf('Steps is %d, which is the same as diffInMonths (%d), so we add another 1.', $steps, $this->diffInMonths)); Log::debug(sprintf('Steps is %d, which is the same as diffInMonths (%d), so we add another 1.', $steps, $this->diffInMonths));
++$steps; ++$steps;

View File

@@ -39,7 +39,7 @@ trait ReturnsIntegerIdTrait
protected function id(): Attribute protected function id(): Attribute
{ {
return Attribute::make( return Attribute::make(
get: static fn($value) => (int)$value, get: static fn ($value) => (int)$value,
); );
} }
} }

View File

@@ -37,14 +37,14 @@ trait ReturnsIntegerUserIdTrait
protected function userGroupId(): Attribute protected function userGroupId(): Attribute
{ {
return Attribute::make( return Attribute::make(
get: static fn($value) => (int)$value, get: static fn ($value) => (int)$value,
); );
} }
protected function userId(): Attribute protected function userId(): Attribute
{ {
return Attribute::make( return Attribute::make(
get: static fn($value) => (int)$value, get: static fn ($value) => (int)$value,
); );
} }
} }

View File

@@ -77,10 +77,10 @@ class Navigation
if (!array_key_exists($repeatFreq, $functionMap)) { if (!array_key_exists($repeatFreq, $functionMap)) {
Log::error(sprintf( Log::error(sprintf(
'The periodicity %s is unknown. Choose one of available periodicity: %s', 'The periodicity %s is unknown. Choose one of available periodicity: %s',
$repeatFreq, $repeatFreq,
implode(', ', array_keys($functionMap)) implode(', ', array_keys($functionMap))
)); ));
return $theDate; return $theDate;
} }
@@ -93,7 +93,7 @@ class Navigation
if ($end < $start) { if ($end < $start) {
[$start, $end] = [$end, $start]; [$start, $end] = [$end, $start];
} }
$periods = []; $periods = [];
// first, 13 periods of [range] // first, 13 periods of [range]
$loopCount = 0; $loopCount = 0;
$loopDate = clone $end; $loopDate = clone $end;
@@ -151,13 +151,13 @@ class Navigation
public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int public function diffInPeriods(string $period, int $skip, Carbon $beginning, Carbon $end): int
{ {
Log::debug(sprintf( Log::debug(sprintf(
'diffInPeriods: %s (skip: %d), between %s and %s.', 'diffInPeriods: %s (skip: %d), between %s and %s.',
$period, $period,
$skip, $skip,
$beginning->format('Y-m-d'), $beginning->format('Y-m-d'),
$end->format('Y-m-d') $end->format('Y-m-d')
)); ));
$map = [ $map = [
'daily' => 'diffInDays', 'daily' => 'diffInDays',
'weekly' => 'diffInWeeks', 'weekly' => 'diffInWeeks',
'monthly' => 'diffInMonths', 'monthly' => 'diffInMonths',
@@ -170,7 +170,7 @@ class Navigation
return 1; return 1;
} }
$func = $map[$period]; $func = $map[$period];
// first do the diff // first do the diff
$floatDiff = $beginning->{$func}($end, true); // @phpstan-ignore-line $floatDiff = $beginning->{$func}($end, true); // @phpstan-ignore-line
@@ -185,7 +185,7 @@ class Navigation
} }
// then do ceil() // then do ceil()
$diff = ceil($floatDiff); $diff = ceil($floatDiff);
Log::debug(sprintf('Diff is %f periods (%d rounded up)', $floatDiff, $diff)); Log::debug(sprintf('Diff is %f periods (%d rounded up)', $floatDiff, $diff));
@@ -193,11 +193,11 @@ class Navigation
$parameter = $skip + 1; $parameter = $skip + 1;
$diff = ceil($diff / $parameter) * $parameter; $diff = ceil($diff / $parameter) * $parameter;
Log::debug(sprintf( Log::debug(sprintf(
'diffInPeriods: skip is %d, so param is %d, and diff becomes %d', 'diffInPeriods: skip is %d, so param is %d, and diff becomes %d',
$skip, $skip,
$parameter, $parameter,
$diff $diff
)); ));
} }
return (int)$diff; return (int)$diff;
@@ -205,7 +205,7 @@ class Navigation
public function endOfPeriod(Carbon $end, string $repeatFreq): Carbon public function endOfPeriod(Carbon $end, string $repeatFreq): Carbon
{ {
$currentEnd = clone $end; $currentEnd = clone $end;
// Log::debug(sprintf('Now in endOfPeriod("%s", "%s").', $currentEnd->toIso8601String(), $repeatFreq)); // Log::debug(sprintf('Now in endOfPeriod("%s", "%s").', $currentEnd->toIso8601String(), $repeatFreq));
$functionMap = [ $functionMap = [
@@ -239,7 +239,7 @@ class Navigation
Log::debug('Session data available.'); Log::debug('Session data available.');
/** @var Carbon $tStart */ /** @var Carbon $tStart */
$tStart = session('start', today(config('app.timezone'))->startOfMonth()); $tStart = session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $tEnd */ /** @var Carbon $tEnd */
$tEnd = session('end', today(config('app.timezone'))->endOfMonth()); $tEnd = session('end', today(config('app.timezone'))->endOfMonth());
@@ -259,7 +259,7 @@ class Navigation
return $end->endOfMonth(); return $end->endOfMonth();
} }
$result = match ($repeatFreq) { $result = match ($repeatFreq) {
'last7' => $currentEnd->addDays(7)->startOfDay(), 'last7' => $currentEnd->addDays(7)->startOfDay(),
'last30' => $currentEnd->addDays(30)->startOfDay(), 'last30' => $currentEnd->addDays(30)->startOfDay(),
'last90' => $currentEnd->addDays(90)->startOfDay(), 'last90' => $currentEnd->addDays(90)->startOfDay(),
@@ -279,7 +279,7 @@ class Navigation
return $end; return $end;
} }
$function = $functionMap[$repeatFreq]; $function = $functionMap[$repeatFreq];
if (array_key_exists($repeatFreq, $modifierMap)) { if (array_key_exists($repeatFreq, $modifierMap)) {
$currentEnd->{$function}($modifierMap[$repeatFreq])->milli(0); // @phpstan-ignore-line $currentEnd->{$function}($modifierMap[$repeatFreq])->milli(0); // @phpstan-ignore-line
@@ -319,7 +319,7 @@ class Navigation
'yearly' => 'endOfYear', 'yearly' => 'endOfYear',
]; ];
$currentEnd = clone $theCurrentEnd; $currentEnd = clone $theCurrentEnd;
if (array_key_exists($repeatFreq, $functionMap)) { if (array_key_exists($repeatFreq, $functionMap)) {
$function = $functionMap[$repeatFreq]; $function = $functionMap[$repeatFreq];
@@ -362,7 +362,7 @@ class Navigation
*/ */
public function listOfPeriods(Carbon $start, Carbon $end): array public function listOfPeriods(Carbon $start, Carbon $end): array
{ {
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
// define period to increment // define period to increment
$increment = 'addDay'; $increment = 'addDay';
$format = $this->preferredCarbonFormat($start, $end); $format = $this->preferredCarbonFormat($start, $end);
@@ -379,8 +379,8 @@ class Navigation
$increment = 'addYear'; $increment = 'addYear';
$displayFormat = (string)trans('config.year_js'); $displayFormat = (string)trans('config.year_js');
} }
$begin = clone $start; $begin = clone $start;
$entries = []; $entries = [];
while ($begin < $end) { while ($begin < $end) {
$formatted = $begin->format($format); $formatted = $begin->format($format);
$displayed = $begin->isoFormat($displayFormat); $displayed = $begin->isoFormat($displayFormat);
@@ -439,6 +439,7 @@ class Navigation
// special formatter for quarter of year // special formatter for quarter of year
Log::error(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); Log::error(sprintf('No date formats for frequency "%s"!', $repeatFrequency));
throw new FireflyException(sprintf('No date formats for frequency "%s"!', $repeatFrequency)); throw new FireflyException(sprintf('No date formats for frequency "%s"!', $repeatFrequency));
return $date->format('Y-m-d'); return $date->format('Y-m-d');
@@ -557,9 +558,9 @@ class Navigation
public function startOfPeriod(Carbon $theDate, string $repeatFreq): Carbon public function startOfPeriod(Carbon $theDate, string $repeatFreq): Carbon
{ {
$date = clone $theDate; $date = clone $theDate;
// Log::debug(sprintf('Now in startOfPeriod("%s", "%s")', $date->toIso8601String(), $repeatFreq)); // Log::debug(sprintf('Now in startOfPeriod("%s", "%s")', $date->toIso8601String(), $repeatFreq));
$functionMap = [ $functionMap = [
'1D' => 'startOfDay', '1D' => 'startOfDay',
'daily' => 'startOfDay', 'daily' => 'startOfDay',
'1W' => 'startOfWeek', '1W' => 'startOfWeek',
@@ -606,7 +607,7 @@ class Navigation
return $date; return $date;
} }
$result = match ($repeatFreq) { $result = match ($repeatFreq) {
'last7' => $date->subDays(7)->startOfDay(), 'last7' => $date->subDays(7)->startOfDay(),
'last30' => $date->subDays(30)->startOfDay(), 'last30' => $date->subDays(30)->startOfDay(),
'last90' => $date->subDays(90)->startOfDay(), 'last90' => $date->subDays(90)->startOfDay(),
@@ -638,7 +639,7 @@ class Navigation
public function subtractPeriod(Carbon $theDate, string $repeatFreq, ?int $subtract = null): Carbon public function subtractPeriod(Carbon $theDate, string $repeatFreq, ?int $subtract = null): Carbon
{ {
$subtract ??= 1; $subtract ??= 1;
$date = clone $theDate; $date = clone $theDate;
// 1D 1W 1M 3M 6M 1Y // 1D 1W 1M 3M 6M 1Y
$functionMap = [ $functionMap = [
'1D' => 'subDays', '1D' => 'subDays',
@@ -677,7 +678,7 @@ class Navigation
// this is then subtracted from $theDate (* $subtract). // this is then subtracted from $theDate (* $subtract).
if ('custom' === $repeatFreq) { if ('custom' === $repeatFreq) {
/** @var Carbon $tStart */ /** @var Carbon $tStart */
$tStart = session('start', today(config('app.timezone'))->startOfMonth()); $tStart = session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $tEnd */ /** @var Carbon $tEnd */
$tEnd = session('end', today(config('app.timezone'))->endOfMonth()); $tEnd = session('end', today(config('app.timezone'))->endOfMonth());
@@ -771,7 +772,7 @@ class Navigation
return $fiscalHelper->endOfFiscalYear($end); return $fiscalHelper->endOfFiscalYear($end);
} }
$list = [ $list = [
'last7', 'last7',
'last30', 'last30',
'last90', 'last90',

View File

@@ -41,10 +41,10 @@ trait RecalculatesAvailableBudgetsTrait
{ {
private function calculateAmount(AvailableBudget $availableBudget): void private function calculateAmount(AvailableBudget $availableBudget): void
{ {
$repository = app(BudgetLimitRepositoryInterface::class); $repository = app(BudgetLimitRepositoryInterface::class);
$repository->setUser($availableBudget->user); $repository->setUser($availableBudget->user);
$newAmount = '0'; $newAmount = '0';
$abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY()); $abPeriod = Period::make($availableBudget->start_date, $availableBudget->end_date, Precision::DAY());
Log::debug( Log::debug(
sprintf( sprintf(
'Now at AB #%d, ("%s" to "%s")', 'Now at AB #%d, ("%s" to "%s")',
@@ -54,7 +54,7 @@ trait RecalculatesAvailableBudgetsTrait
) )
); );
// have to recalculate everything just in case. // have to recalculate everything just in case.
$set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date); $set = $repository->getAllBudgetLimitsByCurrency($availableBudget->transactionCurrency, $availableBudget->start_date, $availableBudget->end_date);
Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count())); Log::debug(sprintf('Found %d interesting budget limit(s).', $set->count()));
/** @var BudgetLimit $budgetLimit */ /** @var BudgetLimit $budgetLimit */
@@ -69,8 +69,8 @@ trait RecalculatesAvailableBudgetsTrait
); );
// overlap in days: // overlap in days:
$limitPeriod = Period::make( $limitPeriod = Period::make(
$budgetLimit->start_date, $budgetLimit->start_date,
$budgetLimit->end_date, $budgetLimit->end_date,
precision : Precision::DAY(), precision : Precision::DAY(),
boundaries: Boundaries::EXCLUDE_NONE() boundaries: Boundaries::EXCLUDE_NONE()
); );
@@ -111,8 +111,8 @@ trait RecalculatesAvailableBudgetsTrait
return '0'; return '0';
} }
$limitPeriod = Period::make( $limitPeriod = Period::make(
$budgetLimit->start_date, $budgetLimit->start_date,
$budgetLimit->end_date, $budgetLimit->end_date,
precision : Precision::DAY(), precision : Precision::DAY(),
boundaries: Boundaries::EXCLUDE_NONE() boundaries: Boundaries::EXCLUDE_NONE()
); );
@@ -130,7 +130,7 @@ trait RecalculatesAvailableBudgetsTrait
Log::debug(sprintf('Now in updateAvailableBudget(limit #%d)', $budgetLimit->id)); Log::debug(sprintf('Now in updateAvailableBudget(limit #%d)', $budgetLimit->id));
/** @var null|Budget $budget */ /** @var null|Budget $budget */
$budget = Budget::find($budgetLimit->budget_id); $budget = Budget::find($budgetLimit->budget_id);
if (null === $budget) { if (null === $budget) {
Log::warning('Budget is null, probably deleted, find deleted version.'); Log::warning('Budget is null, probably deleted, find deleted version.');
@@ -145,7 +145,7 @@ trait RecalculatesAvailableBudgetsTrait
} }
/** @var null|User $user */ /** @var null|User $user */
$user = $budget->user; $user = $budget->user;
// sanity check. It happens when the budget has been deleted so the original user is unknown. // sanity check. It happens when the budget has been deleted so the original user is unknown.
if (null === $user) { if (null === $user) {
@@ -161,7 +161,7 @@ trait RecalculatesAvailableBudgetsTrait
// all have to be created or updated. // all have to be created or updated.
try { try {
$viewRange = app('preferences')->getForUser($user, 'viewRange', '1M')->data; $viewRange = app('preferences')->getForUser($user, 'viewRange', '1M')->data;
} catch (ContainerExceptionInterface | NotFoundExceptionInterface $e) { } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
$viewRange = '1M'; $viewRange = '1M';
} }
@@ -169,20 +169,20 @@ trait RecalculatesAvailableBudgetsTrait
if (null === $viewRange || is_array($viewRange)) { if (null === $viewRange || is_array($viewRange)) {
$viewRange = '1M'; $viewRange = '1M';
} }
$viewRange = (string)$viewRange; $viewRange = (string)$viewRange;
$start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange); $start = app('navigation')->startOfPeriod($budgetLimit->start_date, $viewRange);
$end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange); $end = app('navigation')->startOfPeriod($budgetLimit->end_date, $viewRange);
$end = app('navigation')->endOfPeriod($end, $viewRange); $end = app('navigation')->endOfPeriod($end, $viewRange);
// limit period in total is: // limit period in total is:
$limitPeriod = Period::make($start, $end, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE()); $limitPeriod = Period::make($start, $end, precision: Precision::DAY(), boundaries: Boundaries::EXCLUDE_NONE());
Log::debug(sprintf('Limit period is from %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d'))); Log::debug(sprintf('Limit period is from %s to %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
// from the start until the end of the budget limit, need to loop! // from the start until the end of the budget limit, need to loop!
$current = clone $start; $current = clone $start;
while ($current <= $end) { while ($current <= $end) {
$currentEnd = app('navigation')->endOfPeriod($current, $viewRange); $currentEnd = app('navigation')->endOfPeriod($current, $viewRange);
// create or find AB for this particular period, and set the amount accordingly. // create or find AB for this particular period, and set the amount accordingly.
/** @var null|AvailableBudget $availableBudget */ /** @var null|AvailableBudget $availableBudget */
@@ -227,7 +227,7 @@ trait RecalculatesAvailableBudgetsTrait
} }
// prep for next loop // prep for next loop
$current = app('navigation')->addPeriod($current, $viewRange, 0); $current = app('navigation')->addPeriod($current, $viewRange, 0);
} }
} }
} }

View File

@@ -29,6 +29,7 @@ use Carbon\CarbonInterface;
use Carbon\Exceptions\InvalidFormatException; use Carbon\Exceptions\InvalidFormatException;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use function Safe\preg_match; use function Safe\preg_match;
/** /**
@@ -78,15 +79,15 @@ class ParseDateString
public function parseDate(string $date): Carbon public function parseDate(string $date): Carbon
{ {
Log::debug(sprintf('parseDate("%s")', $date)); Log::debug(sprintf('parseDate("%s")', $date));
$date = strtolower($date); $date = strtolower($date);
// parse keywords: // parse keywords:
if (in_array($date, $this->keywords, true)) { if (in_array($date, $this->keywords, true)) {
return $this->parseKeyword($date); return $this->parseKeyword($date);
} }
// if regex for YYYY-MM-DD: // if regex for YYYY-MM-DD:
$pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/'; $pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])$/';
$result = preg_match($pattern, $date); $result = preg_match($pattern, $date);
if (0 !== $result) { if (0 !== $result) {
return $this->parseDefaultDate($date); return $this->parseDefaultDate($date);
} }
@@ -355,11 +356,11 @@ class ParseDateString
foreach ($parts as $part) { foreach ($parts as $part) {
Log::debug(sprintf('Now parsing part "%s"', $part)); Log::debug(sprintf('Now parsing part "%s"', $part));
$part = trim($part); $part = trim($part);
// verify if correct // verify if correct
$pattern = '/[+-]\d+[wqmdy]/'; $pattern = '/[+-]\d+[wqmdy]/';
$result = preg_match($pattern, $part); $result = preg_match($pattern, $part);
if (0 === $result) { if (0 === $result) {
Log::error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); Log::error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part));
@@ -373,7 +374,7 @@ class ParseDateString
continue; continue;
} }
$func = $functions[$direction][$period]; $func = $functions[$direction][$period];
Log::debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d'))); Log::debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d')));
$today->{$func}($number); // @phpstan-ignore-line $today->{$func}($number); // @phpstan-ignore-line
Log::debug(sprintf('Resulting date is %s', $today->format('Y-m-d'))); Log::debug(sprintf('Resulting date is %s', $today->format('Y-m-d')));

View File

@@ -48,12 +48,13 @@ class Preferences
} }
return Preference::where('user_id', $user->id) return Preference::where('user_id', $user->id)
->where('name', '!=', 'currencyPreference') ->where('name', '!=', 'currencyPreference')
->where(function (Builder $q) use ($user): void { ->where(function (Builder $q) use ($user): void {
$q->whereNull('user_group_id'); $q->whereNull('user_group_id');
$q->orWhere('user_group_id', $user->user_group_id); $q->orWhere('user_group_id', $user->user_group_id);
}) })
->get(); ->get()
;
} }
public function beginsWith(User $user, string $search): Collection public function beginsWith(User $user, string $search): Collection
@@ -89,7 +90,7 @@ class Preferences
Cache::put($key, '', 5); Cache::put($key, '', 5);
} }
public function get(string $name, array | bool | int | string | null $default = null): ?Preference public function get(string $name, array|bool|int|string|null $default = null): ?Preference
{ {
/** @var null|User $user */ /** @var null|User $user */
$user = auth()->user(); $user = auth()->user();
@@ -107,12 +108,13 @@ class Preferences
{ {
$result = []; $result = [];
$preferences = Preference::where('user_id', $user->id) $preferences = Preference::where('user_id', $user->id)
->where(function (Builder $q) use ($user): void { ->where(function (Builder $q) use ($user): void {
$q->whereNull('user_group_id'); $q->whereNull('user_group_id');
$q->orWhere('user_group_id', $user->user_group_id); $q->orWhere('user_group_id', $user->user_group_id);
}) })
->whereIn('name', $list) ->whereIn('name', $list)
->get(['id', 'name', 'data']); ->get(['id', 'name', 'data'])
;
/** @var Preference $preference */ /** @var Preference $preference */
foreach ($preferences as $preference) { foreach ($preferences as $preference) {
@@ -154,7 +156,7 @@ class Preferences
return $result; return $result;
} }
public function getEncryptedForUser(User $user, string $name, array | bool | int | string | null $default = null): ?Preference public function getEncryptedForUser(User $user, string $name, array|bool|int|string|null $default = null): ?Preference
{ {
$result = $this->getForUser($user, $name, $default); $result = $this->getForUser($user, $name, $default);
if ('' === $result->data) { if ('' === $result->data) {
@@ -179,7 +181,7 @@ class Preferences
return $result; return $result;
} }
public function getForUser(User $user, string $name, array | bool | int | string | null $default = null): ?Preference public function getForUser(User $user, string $name, array|bool|int|string|null $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. // don't care about user group ID, except for some specific preferences.
@@ -190,7 +192,7 @@ class Preferences
$query->where('user_group_id', $userGroupId); $query->where('user_group_id', $userGroupId);
} }
$preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']);
if (null !== $preference && null === $preference->data) { if (null !== $preference && null === $preference->data) {
$preference->delete(); $preference->delete();
@@ -214,7 +216,7 @@ class Preferences
return $this->setForUser($user, $name, $default); return $this->setForUser($user, $name, $default);
} }
public function getFresh(string $name, array | bool | int | string | null $default = null): ?Preference public function getFresh(string $name, array|bool|int|string|null $default = null): ?Preference
{ {
/** @var null|User $user */ /** @var null|User $user */
$user = auth()->user(); $user = auth()->user();
@@ -233,8 +235,8 @@ 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;
@@ -248,7 +250,7 @@ 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;
@@ -262,7 +264,7 @@ class Preferences
Session::forget('first'); Session::forget('first');
} }
public function set(string $name, array | bool | int | string | null $value): Preference public function set(string $name, array|bool|int|string|null $value): Preference
{ {
/** @var null|User $user */ /** @var null|User $user */
$user = auth()->user(); $user = auth()->user();
@@ -291,21 +293,21 @@ class Preferences
return $this->set($name, $encrypted); return $this->set($name, $encrypted);
} }
public function setForUser(User $user, string $name, array | bool | int | string | null $value): Preference public function setForUser(User $user, string $name, array|bool|int|string|null $value): Preference
{ {
$fullName = sprintf('preference%s%s', $user->id, $name); $fullName = sprintf('preference%s%s', $user->id, $name);
$userGroupId = $this->getUserGroupId($user, $name); $userGroupId = $this->getUserGroupId($user, $name);
$userGroupId = 0 === (int)$userGroupId ? null : (int)$userGroupId; $userGroupId = 0 === (int)$userGroupId ? null : (int)$userGroupId;
Cache::forget($fullName); Cache::forget($fullName);
$query = Preference::where('user_id', $user->id)->where('name', $name); $query = Preference::where('user_id', $user->id)->where('name', $name);
if (null !== $userGroupId) { if (null !== $userGroupId) {
Log::debug('Include user group ID in query'); Log::debug('Include user group ID in query');
$query->where('user_group_id', $userGroupId); $query->where('user_group_id', $userGroupId);
} }
$preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']); $preference = $query->first(['id', 'user_id', 'user_group_id', 'name', 'data', 'updated_at', 'created_at']);
if (null !== $preference && null === $value) { if (null !== $preference && null === $value) {
$preference->delete(); $preference->delete();

View File

@@ -76,7 +76,7 @@ class BudgetReportGenerator
/** @var Account $account */ /** @var Account $account */
foreach ($this->accounts as $account) { foreach ($this->accounts as $account) {
$accountId = $account->id; $accountId = $account->id;
$this->report[$accountId] ??= [ $this->report[$accountId] ??= [
'name' => $account->name, 'name' => $account->name,
'id' => $account->id, 'id' => $account->id,
@@ -170,16 +170,16 @@ class BudgetReportGenerator
'budget_limits' => [], 'budget_limits' => [],
]; ];
$noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts); $noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts);
foreach ($noBudget as $noBudgetEntry) { foreach ($noBudget as $noBudgetEntry) {
// currency information: // currency information:
$nbCurrencyId = (int)($noBudgetEntry['currency_id'] ?? $this->currency->id); $nbCurrencyId = (int)($noBudgetEntry['currency_id'] ?? $this->currency->id);
$nbCurrencyCode = $noBudgetEntry['currency_code'] ?? $this->currency->code; $nbCurrencyCode = $noBudgetEntry['currency_code'] ?? $this->currency->code;
$nbCurrencyName = $noBudgetEntry['currency_name'] ?? $this->currency->name; $nbCurrencyName = $noBudgetEntry['currency_name'] ?? $this->currency->name;
$nbCurrencySymbol = $noBudgetEntry['currency_symbol'] ?? $this->currency->symbol; $nbCurrencySymbol = $noBudgetEntry['currency_symbol'] ?? $this->currency->symbol;
$nbCurrencyDp = $noBudgetEntry['currency_decimal_places'] ?? $this->currency->decimal_places; $nbCurrencyDp = $noBudgetEntry['currency_decimal_places'] ?? $this->currency->decimal_places;
$this->report['budgets'][0]['budget_limits'][] = [ $this->report['budgets'][0]['budget_limits'][] = [
'budget_limit_id' => null, 'budget_limit_id' => null,
'start_date' => $this->start, 'start_date' => $this->start,
'end_date' => $this->end, 'end_date' => $this->end,
@@ -195,7 +195,7 @@ class BudgetReportGenerator
'currency_symbol' => $nbCurrencySymbol, 'currency_symbol' => $nbCurrencySymbol,
'currency_decimal_places' => $nbCurrencyDp, 'currency_decimal_places' => $nbCurrencyDp,
]; ];
$this->report['sums'][$nbCurrencyId]['spent'] = bcadd($this->report['sums'][$nbCurrencyId]['spent'] ?? '0', (string)$noBudgetEntry['sum']); $this->report['sums'][$nbCurrencyId]['spent'] = bcadd($this->report['sums'][$nbCurrencyId]['spent'] ?? '0', (string)$noBudgetEntry['sum']);
// append currency info because it may be missing: // append currency info because it may be missing:
$this->report['sums'][$nbCurrencyId]['currency_id'] = $nbCurrencyId; $this->report['sums'][$nbCurrencyId]['currency_id'] = $nbCurrencyId;
$this->report['sums'][$nbCurrencyId]['currency_code'] = $nbCurrencyCode; $this->report['sums'][$nbCurrencyId]['currency_code'] = $nbCurrencyCode;
@@ -218,15 +218,15 @@ class BudgetReportGenerator
// make percentages based on total amount. // make percentages based on total amount.
foreach ($this->report['budgets'] as $budgetId => $data) { foreach ($this->report['budgets'] as $budgetId => $data) {
foreach ($data['budget_limits'] as $limitId => $entry) { foreach ($data['budget_limits'] as $limitId => $entry) {
$budgetId = (int)$budgetId; $budgetId = (int)$budgetId;
$limitId = (int)$limitId; $limitId = (int)$limitId;
$currencyId = (int)$entry['currency_id']; $currencyId = (int)$entry['currency_id'];
$spent = $entry['spent']; $spent = $entry['spent'];
$totalSpent = $this->report['sums'][$currencyId]['spent'] ?? '0'; $totalSpent = $this->report['sums'][$currencyId]['spent'] ?? '0';
$spentPct = '0'; $spentPct = '0';
$budgeted = $entry['budgeted']; $budgeted = $entry['budgeted'];
$totalBudgeted = $this->report['sums'][$currencyId]['budgeted'] ?? '0'; $totalBudgeted = $this->report['sums'][$currencyId]['budgeted'] ?? '0';
$budgetedPct = '0'; $budgetedPct = '0';
if (0 !== bccomp((string)$spent, '0') && 0 !== bccomp($totalSpent, '0')) { if (0 !== bccomp((string)$spent, '0') && 0 !== bccomp($totalSpent, '0')) {
$spentPct = round((float)bcmul(bcdiv((string)$spent, $totalSpent), '100')); $spentPct = round((float)bcmul(bcdiv((string)$spent, $totalSpent), '100'));
@@ -234,7 +234,7 @@ class BudgetReportGenerator
if (0 !== bccomp((string)$budgeted, '0') && 0 !== bccomp($totalBudgeted, '0')) { if (0 !== bccomp((string)$budgeted, '0') && 0 !== bccomp($totalBudgeted, '0')) {
$budgetedPct = round((float)bcmul(bcdiv((string)$budgeted, $totalBudgeted), '100')); $budgetedPct = round((float)bcmul(bcdiv((string)$budgeted, $totalBudgeted), '100'));
} }
$this->report['sums'][$currencyId]['budgeted'] ??= '0'; $this->report['sums'][$currencyId]['budgeted'] ??= '0';
$this->report['budgets'][$budgetId]['budget_limits'][$limitId]['spent_pct'] = $spentPct; $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['spent_pct'] = $spentPct;
$this->report['budgets'][$budgetId]['budget_limits'][$limitId]['budgeted_pct'] = $budgetedPct; $this->report['budgets'][$budgetId]['budget_limits'][$limitId]['budgeted_pct'] = $budgetedPct;
} }
@@ -246,7 +246,7 @@ class BudgetReportGenerator
*/ */
private function processBudget(Budget $budget): void private function processBudget(Budget $budget): void
{ {
$budgetId = $budget->id; $budgetId = $budget->id;
$this->report['budgets'][$budgetId] ??= [ $this->report['budgets'][$budgetId] ??= [
'budget_id' => $budgetId, 'budget_id' => $budgetId,
'budget_name' => $budget->name, 'budget_name' => $budget->name,
@@ -255,7 +255,7 @@ class BudgetReportGenerator
]; ];
// get all budget limits for budget in period: // get all budget limits for budget in period:
$limits = $this->blRepository->getBudgetLimits($budget, $this->start, $this->end); $limits = $this->blRepository->getBudgetLimits($budget, $this->start, $this->end);
/** @var BudgetLimit $limit */ /** @var BudgetLimit $limit */
foreach ($limits as $limit) { foreach ($limits as $limit) {
@@ -275,18 +275,18 @@ class BudgetReportGenerator
$this->report[$sourceAccountId]['currencies'][$currencyId] $this->report[$sourceAccountId]['currencies'][$currencyId]
??= [ ??= [
'currency_id' => $expenses['currency_id'], 'currency_id' => $expenses['currency_id'],
'currency_symbol' => $expenses['currency_symbol'], 'currency_symbol' => $expenses['currency_symbol'],
'currency_name' => $expenses['currency_name'], 'currency_name' => $expenses['currency_name'],
'currency_decimal_places' => $expenses['currency_decimal_places'], 'currency_decimal_places' => $expenses['currency_decimal_places'],
'budgets' => [], 'budgets' => [],
]; ];
$this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId]
??= '0'; ??= '0';
$this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId] $this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId]
= bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], (string)$journal['amount']); = bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], (string)$journal['amount']);
} }
} }
@@ -305,14 +305,14 @@ class BudgetReportGenerator
*/ */
private function processLimit(Budget $budget, BudgetLimit $limit): void private function processLimit(Budget $budget, BudgetLimit $limit): void
{ {
$budgetId = $budget->id; $budgetId = $budget->id;
$limitId = $limit->id; $limitId = $limit->id;
$limitCurrency = $limit->transactionCurrency ?? $this->currency; $limitCurrency = $limit->transactionCurrency ?? $this->currency;
$currencyId = $limitCurrency->id; $currencyId = $limitCurrency->id;
$expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection()->push($budget)); $expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection()->push($budget));
$spent = $expenses[$currencyId]['sum'] ?? '0'; $spent = $expenses[$currencyId]['sum'] ?? '0';
$left = -1 === bccomp(bcadd($limit->amount, $spent), '0') ? '0' : bcadd($limit->amount, $spent); $left = -1 === bccomp(bcadd($limit->amount, $spent), '0') ? '0' : bcadd($limit->amount, $spent);
$overspent = 1 === bccomp(bcmul($spent, '-1'), $limit->amount) ? bcadd($spent, $limit->amount) : '0'; $overspent = 1 === bccomp(bcmul($spent, '-1'), $limit->amount) ? bcadd($spent, $limit->amount) : '0';
$this->report['budgets'][$budgetId]['budget_limits'][$limitId] ??= [ $this->report['budgets'][$budgetId]['budget_limits'][$limitId] ??= [
'budget_limit_id' => $limitId, 'budget_limit_id' => $limitId,
@@ -333,17 +333,17 @@ class BudgetReportGenerator
// make sum information: // make sum information:
$this->report['sums'][$currencyId] $this->report['sums'][$currencyId]
??= [ ??= [
'budgeted' => '0', 'budgeted' => '0',
'spent' => '0', 'spent' => '0',
'left' => '0', 'left' => '0',
'overspent' => '0', 'overspent' => '0',
'currency_id' => $currencyId, 'currency_id' => $currencyId,
'currency_code' => $limitCurrency->code, 'currency_code' => $limitCurrency->code,
'currency_name' => $limitCurrency->name, 'currency_name' => $limitCurrency->name,
'currency_symbol' => $limitCurrency->symbol, 'currency_symbol' => $limitCurrency->symbol,
'currency_decimal_places' => $limitCurrency->decimal_places, 'currency_decimal_places' => $limitCurrency->decimal_places,
]; ];
$this->report['sums'][$currencyId]['budgeted'] = bcadd((string)$this->report['sums'][$currencyId]['budgeted'], $limit->amount); $this->report['sums'][$currencyId]['budgeted'] = bcadd((string)$this->report['sums'][$currencyId]['budgeted'], $limit->amount);
$this->report['sums'][$currencyId]['spent'] = bcadd((string)$this->report['sums'][$currencyId]['spent'], $spent); $this->report['sums'][$currencyId]['spent'] = bcadd((string)$this->report['sums'][$currencyId]['spent'], $spent);
$this->report['sums'][$currencyId]['left'] = bcadd((string)$this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent)); $this->report['sums'][$currencyId]['left'] = bcadd((string)$this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent));

View File

@@ -62,17 +62,17 @@ class CategoryReportGenerator
*/ */
public function operations(): void public function operations(): void
{ {
$earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts); $earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts);
$spentWith = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts); $spentWith = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts);
// also transferred out and transferred into these accounts in this category: // also transferred out and transferred into these accounts in this category:
$transferredIn = $this->opsRepository->listTransferredIn($this->start, $this->end, $this->accounts); $transferredIn = $this->opsRepository->listTransferredIn($this->start, $this->end, $this->accounts);
$transferredOut = $this->opsRepository->listTransferredOut($this->start, $this->end, $this->accounts); $transferredOut = $this->opsRepository->listTransferredOut($this->start, $this->end, $this->accounts);
$earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts); $earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts);
$spentWithout = $this->noCatRepository->listExpenses($this->start, $this->end, $this->accounts); $spentWithout = $this->noCatRepository->listExpenses($this->start, $this->end, $this->accounts);
$this->report = [ $this->report = [
'categories' => [], 'categories' => [],
'sums' => [], 'sums' => [],
]; ];
@@ -106,7 +106,7 @@ class CategoryReportGenerator
private function processCategoryRow(int $currencyId, array $currencyRow, int $categoryId, array $categoryRow): void private function processCategoryRow(int $currencyId, array $currencyRow, int $categoryId, array $categoryRow): void
{ {
$key = sprintf('%s-%s', $currencyId, $categoryId); $key = sprintf('%s-%s', $currencyId, $categoryId);
$this->report['categories'][$key] ??= [ $this->report['categories'][$key] ??= [
'id' => $categoryId, 'id' => $categoryId,
'title' => $categoryRow['name'], 'title' => $categoryRow['name'],
@@ -122,9 +122,9 @@ class CategoryReportGenerator
// loop journals: // loop journals:
foreach ($categoryRow['transaction_journals'] as $journal) { foreach ($categoryRow['transaction_journals'] as $journal) {
// sum of sums // sum of sums
$this->report['sums'][$currencyId]['sum'] = bcadd((string)$this->report['sums'][$currencyId]['sum'], (string)$journal['amount']); $this->report['sums'][$currencyId]['sum'] = bcadd((string)$this->report['sums'][$currencyId]['sum'], (string)$journal['amount']);
// sum of spent: // sum of spent:
$this->report['sums'][$currencyId]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd( $this->report['sums'][$currencyId]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd(
(string)$this->report['sums'][$currencyId]['spent'], (string)$this->report['sums'][$currencyId]['spent'],
(string)$journal['amount'] (string)$journal['amount']
) : $this->report['sums'][$currencyId]['spent']; ) : $this->report['sums'][$currencyId]['spent'];
@@ -135,14 +135,14 @@ class CategoryReportGenerator
) : $this->report['sums'][$currencyId]['earned']; ) : $this->report['sums'][$currencyId]['earned'];
// sum of category // sum of category
$this->report['categories'][$key]['sum'] = bcadd((string)$this->report['categories'][$key]['sum'], (string)$journal['amount']); $this->report['categories'][$key]['sum'] = bcadd((string)$this->report['categories'][$key]['sum'], (string)$journal['amount']);
// total spent in category // total spent in category
$this->report['categories'][$key]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd( $this->report['categories'][$key]['spent'] = -1 === bccomp((string)$journal['amount'], '0') ? bcadd(
(string)$this->report['categories'][$key]['spent'], (string)$this->report['categories'][$key]['spent'],
(string)$journal['amount'] (string)$journal['amount']
) : $this->report['categories'][$key]['spent']; ) : $this->report['categories'][$key]['spent'];
// total earned in category // total earned in category
$this->report['categories'][$key]['earned'] = 1 === bccomp((string)$journal['amount'], '0') ? bcadd( $this->report['categories'][$key]['earned'] = 1 === bccomp((string)$journal['amount'], '0') ? bcadd(
(string)$this->report['categories'][$key]['earned'], (string)$this->report['categories'][$key]['earned'],
(string)$journal['amount'] (string)$journal['amount']
) : $this->report['categories'][$key]['earned']; ) : $this->report['categories'][$key]['earned'];

View File

@@ -48,14 +48,14 @@ class TransactionSummarizer
Log::debug(sprintf('Now in groupByCurrencyId([%d journals], "%s", %s)', count($journals), $method, var_export($includeForeign, true))); Log::debug(sprintf('Now in groupByCurrencyId([%d journals], "%s", %s)', count($journals), $method, var_export($includeForeign, true)));
$array = []; $array = [];
foreach ($journals as $journal) { foreach ($journals as $journal) {
$field = 'amount'; $field = 'amount';
// grab default currency information. // grab default currency information.
$currencyId = (int)$journal['currency_id']; $currencyId = (int)$journal['currency_id'];
$currencyName = $journal['currency_name']; $currencyName = $journal['currency_name'];
$currencySymbol = $journal['currency_symbol']; $currencySymbol = $journal['currency_symbol'];
$currencyCode = $journal['currency_code']; $currencyCode = $journal['currency_code'];
$currencyDecimalPlaces = $journal['currency_decimal_places']; $currencyDecimalPlaces = $journal['currency_decimal_places'];
// prepare foreign currency info: // prepare foreign currency info:
$foreignCurrencyId = 0; $foreignCurrencyId = 0;
@@ -102,7 +102,7 @@ class TransactionSummarizer
} }
// first process normal amount // first process normal amount
$amount = (string)($journal[$field] ?? '0'); $amount = (string)($journal[$field] ?? '0');
$array[$currencyId] ??= [ $array[$currencyId] ??= [
'sum' => '0', 'sum' => '0',
'currency_id' => $currencyId, 'currency_id' => $currencyId,
@@ -121,7 +121,7 @@ class TransactionSummarizer
// then process foreign amount, if it exists. // then process foreign amount, if it exists.
if (0 !== $foreignCurrencyId && true === $includeForeign) { if (0 !== $foreignCurrencyId && true === $includeForeign) {
$amount = (string)($journal['foreign_amount'] ?? '0'); $amount = (string)($journal['foreign_amount'] ?? '0');
$array[$foreignCurrencyId] ??= [ $array[$foreignCurrencyId] ??= [
'sum' => '0', 'sum' => '0',
'currency_id' => $foreignCurrencyId, 'currency_id' => $foreignCurrencyId,
@@ -179,7 +179,7 @@ class TransactionSummarizer
if ($convertToPrimary && $journal['currency_id'] !== $primary->id && $primary->id === $journal['foreign_currency_id']) { if ($convertToPrimary && $journal['currency_id'] !== $primary->id && $primary->id === $journal['foreign_currency_id']) {
$field = 'foreign_amount'; $field = 'foreign_amount';
} }
$key = sprintf('%s-%s', $journal[$idKey], $currencyId); $key = sprintf('%s-%s', $journal[$idKey], $currencyId);
// sum it all up or create a new array. // sum it all up or create a new array.
$array[$key] ??= [ $array[$key] ??= [
'id' => $journal[$idKey], 'id' => $journal[$idKey],
@@ -193,7 +193,7 @@ class TransactionSummarizer
]; ];
// add the data from the $field to the array. // add the data from the $field to the array.
$array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string)($journal[$field] ?? '0'))); // @phpstan-ignore-line $array[$key]['sum'] = bcadd($array[$key]['sum'], Steam::{$method}((string)($journal[$field] ?? '0'))); // @phpstan-ignore-line
Log::debug(sprintf('Field for transaction #%d is "%s" (%s). Sum: %s', $journal['transaction_group_id'], $currencyCode, $field, $array[$key]['sum'])); Log::debug(sprintf('Field for transaction #%d is "%s" (%s). Sum: %s', $journal['transaction_group_id'], $currencyCode, $field, $array[$key]['sum']));
// also do foreign amount, but only when convertToPrimary is false (otherwise we have it already) // also do foreign amount, but only when convertToPrimary is false (otherwise we have it already)
@@ -201,7 +201,7 @@ class TransactionSummarizer
if ((!$convertToPrimary || $journal['foreign_currency_id'] !== $primary->id) && 0 !== (int)$journal['foreign_currency_id']) { if ((!$convertToPrimary || $journal['foreign_currency_id'] !== $primary->id) && 0 !== (int)$journal['foreign_currency_id']) {
Log::debug(sprintf('Use foreign amount from transaction #%d: %s %s. Sum: %s', $journal['transaction_group_id'], $currencyCode, $journal['foreign_amount'], $array[$key]['sum'])); Log::debug(sprintf('Use foreign amount from transaction #%d: %s %s. Sum: %s', $journal['transaction_group_id'], $currencyCode, $journal['foreign_amount'], $array[$key]['sum']));
$key = sprintf('%s-%s', $journal[$idKey], $journal['foreign_currency_id']); $key = sprintf('%s-%s', $journal[$idKey], $journal['foreign_currency_id']);
$array[$key] ??= [ $array[$key] ??= [
'id' => $journal[$idKey], 'id' => $journal[$idKey],
'name' => $journal[$nameKey], 'name' => $journal[$nameKey],
'sum' => '0', 'sum' => '0',

View File

@@ -82,8 +82,8 @@ trait CalculateRangeOccurrences
*/ */
protected function getNdomInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array protected function getNdomInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array
{ {
$return = []; $return = [];
$attempts = 0; $attempts = 0;
$start->startOfMonth(); $start->startOfMonth();
// this feels a bit like a cop out but why reinvent the wheel? // this feels a bit like a cop out but why reinvent the wheel?
$counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth']; $counters = [1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth'];
@@ -108,12 +108,12 @@ trait CalculateRangeOccurrences
*/ */
protected function getWeeklyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array protected function getWeeklyInRange(Carbon $start, Carbon $end, int $skipMod, string $moment): array
{ {
$return = []; $return = [];
$attempts = 0; $attempts = 0;
app('log')->debug('Rep is weekly.'); app('log')->debug('Rep is weekly.');
// monday = 1 // monday = 1
// sunday = 7 // sunday = 7
$dayOfWeek = (int)$moment; $dayOfWeek = (int)$moment;
app('log')->debug(sprintf('DoW in repetition is %d, in mutator is %d', $dayOfWeek, $start->dayOfWeekIso)); app('log')->debug(sprintf('DoW in repetition is %d, in mutator is %d', $dayOfWeek, $start->dayOfWeekIso));
if ($start->dayOfWeekIso > $dayOfWeek) { if ($start->dayOfWeekIso > $dayOfWeek) {
// day has already passed this week, add one week: // day has already passed this week, add one week:
@@ -154,8 +154,8 @@ trait CalculateRangeOccurrences
} }
// is $date between $start and $end? // is $date between $start and $end?
$obj = clone $date; $obj = clone $date;
$count = 0; $count = 0;
while ($obj <= $end && $obj >= $start && $count < 10) { while ($obj <= $end && $obj >= $start && $count < 10) {
if (0 === $attempts % $skipMod) { if (0 === $attempts % $skipMod) {
$return[] = clone $obj; $return[] = clone $obj;

View File

@@ -89,10 +89,10 @@ trait CalculateXOccurrences
*/ */
protected function getXNDomOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array protected function getXNDomOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array
{ {
$return = []; $return = [];
$total = 0; $total = 0;
$attempts = 0; $attempts = 0;
$mutator = clone $date; $mutator = clone $date;
$mutator->addDay(); // always assume today has passed. $mutator->addDay(); // always assume today has passed.
$mutator->startOfMonth(); $mutator->startOfMonth();
// this feels a bit like a cop out but why reinvent the wheel? // this feels a bit like a cop out but why reinvent the wheel?
@@ -120,14 +120,14 @@ trait CalculateXOccurrences
*/ */
protected function getXWeeklyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array protected function getXWeeklyOccurrences(Carbon $date, int $count, int $skipMod, string $moment): array
{ {
$return = []; $return = [];
$total = 0; $total = 0;
$attempts = 0; $attempts = 0;
$mutator = clone $date; $mutator = clone $date;
// monday = 1 // monday = 1
// sunday = 7 // sunday = 7
$mutator->addDay(); // always assume today has passed. $mutator->addDay(); // always assume today has passed.
$dayOfWeek = (int)$moment; $dayOfWeek = (int)$moment;
if ($mutator->dayOfWeekIso > $dayOfWeek) { if ($mutator->dayOfWeekIso > $dayOfWeek) {
// day has already passed this week, add one week: // day has already passed this week, add one week:
$mutator->addWeek(); $mutator->addWeek();
@@ -164,7 +164,7 @@ trait CalculateXOccurrences
if ($mutator > $date) { if ($mutator > $date) {
$date->addYear(); $date->addYear();
} }
$obj = clone $date; $obj = clone $date;
while ($total < $count) { while ($total < $count) {
if (0 === $attempts % $skipMod) { if (0 === $attempts % $skipMod) {
$return[] = clone $obj; $return[] = clone $obj;

View File

@@ -87,7 +87,7 @@ trait CalculateXOccurrencesSince
++$total; ++$total;
} }
++$attempts; ++$attempts;
$mutator = $mutator->endOfMonth()->addDay(); $mutator = $mutator->endOfMonth()->addDay();
} }
Log::debug('Collected enough occurrences.'); Log::debug('Collected enough occurrences.');
@@ -103,10 +103,10 @@ trait CalculateXOccurrencesSince
protected function getXNDomOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array protected function getXNDomOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array
{ {
Log::debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$return = []; $return = [];
$total = 0; $total = 0;
$attempts = 0; $attempts = 0;
$mutator = clone $date; $mutator = clone $date;
$mutator->addDay(); // always assume today has passed. $mutator->addDay(); // always assume today has passed.
$mutator->startOfMonth(); $mutator->startOfMonth();
// this feels a bit like a cop out but why reinvent the wheel? // this feels a bit like a cop out but why reinvent the wheel?
@@ -137,15 +137,15 @@ trait CalculateXOccurrencesSince
protected function getXWeeklyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array protected function getXWeeklyOccurrencesSince(Carbon $date, Carbon $afterDate, int $count, int $skipMod, string $moment): array
{ {
Log::debug(sprintf('Now in %s', __METHOD__)); Log::debug(sprintf('Now in %s', __METHOD__));
$return = []; $return = [];
$total = 0; $total = 0;
$attempts = 0; $attempts = 0;
$mutator = clone $date; $mutator = clone $date;
// monday = 1 // monday = 1
// sunday = 7 // sunday = 7
// Removed assumption today has passed, see issue https://github.com/firefly-iii/firefly-iii/issues/4798 // Removed assumption today has passed, see issue https://github.com/firefly-iii/firefly-iii/issues/4798
// $mutator->addDay(); // always assume today has passed. // $mutator->addDay(); // always assume today has passed.
$dayOfWeek = (int)$moment; $dayOfWeek = (int)$moment;
if ($mutator->dayOfWeekIso > $dayOfWeek) { if ($mutator->dayOfWeekIso > $dayOfWeek) {
// day has already passed this week, add one week: // day has already passed this week, add one week:
$mutator->addWeek(); $mutator->addWeek();
@@ -189,7 +189,7 @@ trait CalculateXOccurrencesSince
$date->addYear(); $date->addYear();
Log::debug(sprintf('Date is now %s', $date->format('Y-m-d'))); Log::debug(sprintf('Date is now %s', $date->format('Y-m-d')));
} }
$obj = clone $date; $obj = clone $date;
while ($total < $count) { while ($total < $count) {
Log::debug(sprintf('total (%d) < count (%d) so go.', $total, $count)); Log::debug(sprintf('total (%d) < count (%d) so go.', $total, $count));
Log::debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod)); Log::debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod));

View File

@@ -46,7 +46,7 @@ trait FiltersWeekends
return $dates; return $dates;
} }
$return = []; $return = [];
/** @var Carbon $date */ /** @var Carbon $date */
foreach ($dates as $date) { foreach ($dates as $date) {
@@ -60,7 +60,7 @@ trait FiltersWeekends
// is weekend and must set back to Friday? // is weekend and must set back to Friday?
if (RecurrenceRepetitionWeekend::WEEKEND_TO_FRIDAY->value === $repetition->weekend) { if (RecurrenceRepetitionWeekend::WEEKEND_TO_FRIDAY->value === $repetition->weekend) {
$clone = clone $date; $clone = clone $date;
$clone->addDays(5 - $date->dayOfWeekIso); $clone->addDays(5 - $date->dayOfWeekIso);
Log::debug( Log::debug(
sprintf('Date is %s, and this is in the weekend, so corrected to %s (Friday).', $date->format('D d M Y'), $clone->format('D d M Y')) sprintf('Date is %s, and this is in the weekend, so corrected to %s (Friday).', $date->format('D d M Y'), $clone->format('D d M Y'))
@@ -72,7 +72,7 @@ trait FiltersWeekends
// postpone to Monday? // postpone to Monday?
if (RecurrenceRepetitionWeekend::WEEKEND_TO_MONDAY->value === $repetition->weekend) { if (RecurrenceRepetitionWeekend::WEEKEND_TO_MONDAY->value === $repetition->weekend) {
$clone = clone $date; $clone = clone $date;
$clone->addDays(8 - $date->dayOfWeekIso); $clone->addDays(8 - $date->dayOfWeekIso);
Log::debug( Log::debug(
sprintf('Date is %s, and this is in the weekend, so corrected to %s (Monday).', $date->format('D d M Y'), $clone->format('D d M Y')) sprintf('Date is %s, and this is in the weekend, so corrected to %s (Monday).', $date->format('D d M Y'), $clone->format('D d M Y'))

View File

@@ -37,7 +37,7 @@ interface UserGroupInterface
public function getUserGroup(): ?UserGroup; public function getUserGroup(): ?UserGroup;
public function setUser(Authenticatable | User | null $user): void; public function setUser(Authenticatable|User|null $user): void;
public function setUserGroup(UserGroup $userGroup): void; public function setUserGroup(UserGroup $userGroup): void;

View File

@@ -61,10 +61,10 @@ trait UserGroupTrait
/** /**
* @throws FireflyException * @throws FireflyException
*/ */
public function setUser(Authenticatable | User | null $user): void public function setUser(Authenticatable|User|null $user): void
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;
if (null === $user->userGroup) { if (null === $user->userGroup) {
throw new FireflyException(sprintf('User #%d ("%s") has no user group.', $user->id, $user->email)); throw new FireflyException(sprintf('User #%d ("%s") has no user group.', $user->id, $user->email));
} }
@@ -99,14 +99,15 @@ trait UserGroupTrait
public function setUserGroupById(int $userGroupId): void public function setUserGroupById(int $userGroupId): void
{ {
$memberships = GroupMembership::where('user_id', $this->user->id) $memberships = GroupMembership::where('user_id', $this->user->id)
->where('user_group_id', $userGroupId) ->where('user_group_id', $userGroupId)
->count(); ->count()
;
if (0 === $memberships) { if (0 === $memberships) {
throw new FireflyException(sprintf('User #%d has no access to administration #%d', $this->user->id, $userGroupId)); throw new FireflyException(sprintf('User #%d has no access to administration #%d', $this->user->id, $userGroupId));
} }
/** @var null|UserGroup $userGroup */ /** @var null|UserGroup $userGroup */
$userGroup = UserGroup::find($userGroupId); $userGroup = UserGroup::find($userGroupId);
if (null === $userGroup) { if (null === $userGroup) {
throw new FireflyException(sprintf('Cannot find administration for user #%d', $this->user->id)); throw new FireflyException(sprintf('Cannot find administration for user #%d', $this->user->id));
} }

View File

@@ -96,12 +96,12 @@ trait AppendsLocationData
$data['latitude'] = null; $data['latitude'] = null;
$data['zoom_level'] = null; $data['zoom_level'] = null;
$longitudeKey = $this->getLocationKey($prefix, 'longitude'); $longitudeKey = $this->getLocationKey($prefix, 'longitude');
$latitudeKey = $this->getLocationKey($prefix, 'latitude'); $latitudeKey = $this->getLocationKey($prefix, 'latitude');
$zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level');
$isValidPOST = $this->isValidPost($prefix); $isValidPOST = $this->isValidPost($prefix);
$isValidPUT = $this->isValidPUT($prefix); $isValidPUT = $this->isValidPUT($prefix);
$isValidEmptyPUT = $this->isValidEmptyPUT($prefix); $isValidEmptyPUT = $this->isValidEmptyPUT($prefix);
// for a POST (store), all fields must be present and not NULL. // for a POST (store), all fields must be present and not NULL.
if ($isValidPOST) { if ($isValidPOST) {
@@ -153,9 +153,9 @@ trait AppendsLocationData
$zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level'); $zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level');
return ( return (
null === $this->get($longitudeKey) null === $this->get($longitudeKey)
&& null === $this->get($latitudeKey) && null === $this->get($latitudeKey)
&& null === $this->get($zoomLevelKey)) && null === $this->get($zoomLevelKey))
&& ( && (
'PUT' === $this->method() 'PUT' === $this->method()
|| ('POST' === $this->method() && $this->routeIs('*.update')) || ('POST' === $this->method() && $this->routeIs('*.update'))

View File

@@ -40,7 +40,7 @@ trait ChecksLogin
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); app('log')->debug(sprintf('Now in %s', __METHOD__));
// Only allow logged-in users // Only allow logged-in users
$check = auth()->check(); $check = auth()->check();
if (!$check) { if (!$check) {
return false; return false;
} }
@@ -79,7 +79,7 @@ trait ChecksLogin
public function getUserGroup(): ?UserGroup public function getUserGroup(): ?UserGroup
{ {
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
app('log')->debug('Now in getUserGroup()'); app('log')->debug('Now in getUserGroup()');
/** @var null|UserGroup $userGroup */ /** @var null|UserGroup $userGroup */
@@ -91,7 +91,7 @@ trait ChecksLogin
app('log')->debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id)); app('log')->debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id));
$userGroupId = (int)$user->user_group_id; $userGroupId = (int)$user->user_group_id;
} }
$userGroup = UserGroup::find($userGroupId); $userGroup = UserGroup::find($userGroupId);
if (null === $userGroup) { if (null === $userGroup) {
app('log')->error(sprintf('Request class has user_group_id (#%d), but group does not exist.', $userGroupId)); app('log')->error(sprintf('Request class has user_group_id (#%d), but group does not exist.', $userGroupId));

View File

@@ -31,6 +31,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Facades\Steam; use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use function Safe\preg_replace; use function Safe\preg_replace;
/** /**
@@ -147,15 +148,15 @@ trait ConvertsDataTypes
public function convertSortParameters(string $field, string $class): array public function convertSortParameters(string $field, string $class): array
{ {
// assume this all works, because the validator would have caught any errors. // assume this all works, because the validator would have caught any errors.
$parameter = (string)request()->query->get($field); $parameter = (string)request()->query->get($field);
if ('' === $parameter) { if ('' === $parameter) {
return []; return [];
} }
$parts = explode(',', $parameter); $parts = explode(',', $parameter);
$sortParameters = []; $sortParameters = [];
foreach ($parts as $part) { foreach ($parts as $part) {
$part = trim($part); $part = trim($part);
$direction = 'asc'; $direction = 'asc';
if ('-' === $part[0]) { if ('-' === $part[0]) {
$part = substr($part, 1); $part = substr($part, 1);
$direction = 'desc'; $direction = 'desc';
@@ -459,7 +460,7 @@ trait ConvertsDataTypes
if (!is_array($entry)) { if (!is_array($entry)) {
continue; continue;
} }
$amount = null; $amount = null;
if (array_key_exists('current_amount', $entry)) { if (array_key_exists('current_amount', $entry)) {
$amount = $this->clearString((string)($entry['current_amount'] ?? '0')); $amount = $this->clearString((string)($entry['current_amount'] ?? '0'));
if (null === $entry['current_amount']) { if (null === $entry['current_amount']) {

View File

@@ -40,9 +40,9 @@ trait ValidatesWebhooks
if (count($validator->failed()) > 0) { if (count($validator->failed()) > 0) {
return; return;
} }
$data = $validator->getData(); $data = $validator->getData();
$triggers = $data['triggers'] ?? []; $triggers = $data['triggers'] ?? [];
$responses = $data['responses'] ?? []; $responses = $data['responses'] ?? [];
if (0 === count($triggers) || 0 === count($responses)) { if (0 === count($triggers) || 0 === count($responses)) {
Log::debug('No trigger or response, return.'); Log::debug('No trigger or response, return.');

View File

@@ -28,6 +28,7 @@ use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use function Safe\json_encode; use function Safe\json_encode;
/** /**
@@ -36,16 +37,16 @@ use function Safe\json_encode;
class AccountSearch implements GenericSearchInterface class AccountSearch implements GenericSearchInterface
{ {
/** @var string */ /** @var string */
public const string SEARCH_ALL = 'all'; public const string SEARCH_ALL = 'all';
/** @var string */ /** @var string */
public const string SEARCH_IBAN = 'iban'; public const string SEARCH_IBAN = 'iban';
/** @var string */ /** @var string */
public const string SEARCH_ID = 'id'; public const string SEARCH_ID = 'id';
/** @var string */ /** @var string */
public const string SEARCH_NAME = 'name'; public const string SEARCH_NAME = 'name';
/** @var string */ /** @var string */
public const string SEARCH_NUMBER = 'number'; public const string SEARCH_NUMBER = 'number';
@@ -62,9 +63,10 @@ class AccountSearch implements GenericSearchInterface
public function search(): Collection public function search(): Collection
{ {
$searchQuery = $this->user->accounts() $searchQuery = $this->user->accounts()
->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id') ->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id')
->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id') ->leftJoin('account_meta', 'accounts.id', '=', 'account_meta.account_id')
->whereIn('account_types.type', $this->types); ->whereIn('account_types.type', $this->types)
;
$like = sprintf('%%%s%%', $this->query); $like = sprintf('%%%s%%', $this->query);
$originalQuery = $this->query; $originalQuery = $this->query;
@@ -135,7 +137,7 @@ class AccountSearch implements GenericSearchInterface
$this->types = $types; $this->types = $types;
} }
public function setUser(Authenticatable | User | null $user): void public function setUser(Authenticatable|User|null $user): void
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -117,7 +117,7 @@ class OperatorQuerySearch implements SearchInterface
$operator = substr($operator, 1); $operator = substr($operator, 1);
} }
$config = config(sprintf('search.operators.%s', $operator)); $config = config(sprintf('search.operators.%s', $operator));
if (null === $config) { if (null === $config) {
throw new FireflyException(sprintf('No configuration for search operator "%s"', $operator)); throw new FireflyException(sprintf('No configuration for search operator "%s"', $operator));
} }
@@ -186,7 +186,7 @@ class OperatorQuerySearch implements SearchInterface
try { try {
$parsedQuery = $parser->parse($query); $parsedQuery = $parser->parse($query);
} catch (LogicException | TypeError $e) { } catch (LogicException|TypeError $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
Log::error(sprintf('Could not parse search: "%s".', $query)); Log::error(sprintf('Could not parse search: "%s".', $query));
@@ -278,7 +278,7 @@ class OperatorQuerySearch implements SearchInterface
$value = $node->getValue(); $value = $node->getValue();
$prohibited = $node->isProhibited($flipProhibitedFlag); $prohibited = $node->isProhibited($flipProhibitedFlag);
$context = config(sprintf('search.operators.%s.needs_context', $operator)); $context = config(sprintf('search.operators.%s.needs_context', $operator));
// is an operator that needs no context, and value is false, then prohibited = true. // is an operator that needs no context, and value is false, then prohibited = true.
if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context && !$prohibited) { if ('false' === $value && in_array($operator, $this->validOperators, true) && false === $context && !$prohibited) {
@@ -292,14 +292,14 @@ class OperatorQuerySearch implements SearchInterface
} }
// must be valid operator: // must be valid operator:
$inArray = in_array($operator, $this->validOperators, true); $inArray = in_array($operator, $this->validOperators, true);
if ($inArray) { if ($inArray) {
if ($this->updateCollector($operator, $value, $prohibited)) { if ($this->updateCollector($operator, $value, $prohibited)) {
$this->operators->push([ $this->operators->push([
'type' => self::getRootOperator($operator), 'type' => self::getRootOperator($operator),
'value' => $value, 'value' => $value,
'prohibited' => $prohibited, 'prohibited' => $prohibited,
]); ]);
Log::debug(sprintf('Added operator type "%s"', $operator)); Log::debug(sprintf('Added operator type "%s"', $operator));
} }
} }
@@ -355,7 +355,7 @@ class OperatorQuerySearch implements SearchInterface
private function handleStringNode(StringNode $node, bool $flipProhibitedFlag): void private function handleStringNode(StringNode $node, bool $flipProhibitedFlag): void
{ {
$string = $node->getValue(); $string = $node->getValue();
$prohibited = $node->isProhibited($flipProhibitedFlag); $prohibited = $node->isProhibited($flipProhibitedFlag);
@@ -477,7 +477,7 @@ class OperatorQuerySearch implements SearchInterface
} }
} }
// string position (default): starts with: // string position (default): starts with:
$stringMethod = 'str_starts_with'; $stringMethod = 'str_starts_with';
// string position: ends with: // string position: ends with:
if (StringPosition::ENDS === $stringPosition) { if (StringPosition::ENDS === $stringPosition) {
@@ -491,7 +491,7 @@ class OperatorQuerySearch implements SearchInterface
} }
// get accounts: // get accounts:
$accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337); $accounts = $this->accountRepository->searchAccount($value, $searchTypes, 1337);
if (0 === $accounts->count() && false === $prohibited) { if (0 === $accounts->count() && false === $prohibited) {
Log::warning('Found zero accounts, search for non existing account, NO results will be returned.'); Log::warning('Found zero accounts, search for non existing account, NO results will be returned.');
$this->collector->findNothing(); $this->collector->findNothing();
@@ -504,8 +504,8 @@ class OperatorQuerySearch implements SearchInterface
return; return;
} }
Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count()));
$filtered = $accounts->filter( $filtered = $accounts->filter(
static fn(Account $account) => $stringMethod(strtolower($account->name), strtolower($value)) static fn (Account $account) => $stringMethod(strtolower($account->name), strtolower($value))
); );
if (0 === $filtered->count()) { if (0 === $filtered->count()) {
@@ -557,7 +557,7 @@ class OperatorQuerySearch implements SearchInterface
} }
// string position (default): starts with: // string position (default): starts with:
$stringMethod = 'str_starts_with'; $stringMethod = 'str_starts_with';
// string position: ends with: // string position: ends with:
if (StringPosition::ENDS === $stringPosition) { if (StringPosition::ENDS === $stringPosition) {
@@ -571,7 +571,7 @@ class OperatorQuerySearch implements SearchInterface
} }
// search for accounts: // search for accounts:
$accounts = $this->accountRepository->searchAccountNr($value, $searchTypes, 1337); $accounts = $this->accountRepository->searchAccountNr($value, $searchTypes, 1337);
if (0 === $accounts->count()) { if (0 === $accounts->count()) {
Log::debug('Found zero accounts, search for invalid account.'); Log::debug('Found zero accounts, search for invalid account.');
Log::warning('Call to findNothing() from searchAccountNr().'); Log::warning('Call to findNothing() from searchAccountNr().');
@@ -582,7 +582,7 @@ class OperatorQuerySearch implements SearchInterface
// if found, do filter // if found, do filter
Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count())); Log::debug(sprintf('Found %d accounts, will filter.', $accounts->count()));
$filtered = $accounts->filter( $filtered = $accounts->filter(
static function (Account $account) use ($value, $stringMethod) { static function (Account $account) use ($value, $stringMethod) {
// either IBAN or account number // either IBAN or account number
$ibanMatch = $stringMethod(strtolower((string)$account->iban), strtolower($value)); $ibanMatch = $stringMethod(strtolower((string)$account->iban), strtolower($value));
@@ -1250,15 +1250,15 @@ class OperatorQuerySearch implements SearchInterface
throw new FireflyException(sprintf('Unsupported search operator: "%s"', $operator)); throw new FireflyException(sprintf('Unsupported search operator: "%s"', $operator));
// some search operators are ignored, basically: // some search operators are ignored, basically:
case 'user_action': case 'user_action':
Log::info(sprintf('Ignore search operator "%s"', $operator)); Log::info(sprintf('Ignore search operator "%s"', $operator));
return false; return false;
// //
// all account related searches: // all account related searches:
// //
case 'account_is': case 'account_is':
$this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS); $this->searchAccount($value, SearchDirection::BOTH, StringPosition::IS);
@@ -1420,7 +1420,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'source_account_id': case 'source_account_id':
$account = $this->accountRepository->find((int)$value); $account = $this->accountRepository->find((int)$value);
if (null !== $account) { if (null !== $account) {
$this->collector->setSourceAccounts(new Collection()->push($account)); $this->collector->setSourceAccounts(new Collection()->push($account));
} }
@@ -1433,7 +1433,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-source_account_id': case '-source_account_id':
$account = $this->accountRepository->find((int)$value); $account = $this->accountRepository->find((int)$value);
if (null !== $account) { if (null !== $account) {
$this->collector->excludeSourceAccounts(new Collection()->push($account)); $this->collector->excludeSourceAccounts(new Collection()->push($account));
} }
@@ -1446,25 +1446,25 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'journal_id': case 'journal_id':
$parts = explode(',', $value); $parts = explode(',', $value);
$this->collector->setJournalIds($parts); $this->collector->setJournalIds($parts);
break; break;
case '-journal_id': case '-journal_id':
$parts = explode(',', $value); $parts = explode(',', $value);
$this->collector->excludeJournalIds($parts); $this->collector->excludeJournalIds($parts);
break; break;
case 'id': case 'id':
$parts = explode(',', $value); $parts = explode(',', $value);
$this->collector->setIds($parts); $this->collector->setIds($parts);
break; break;
case '-id': case '-id':
$parts = explode(',', $value); $parts = explode(',', $value);
$this->collector->excludeIds($parts); $this->collector->excludeIds($parts);
break; break;
@@ -1550,7 +1550,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'destination_account_id': case 'destination_account_id':
$account = $this->accountRepository->find((int)$value); $account = $this->accountRepository->find((int)$value);
if (null !== $account) { if (null !== $account) {
$this->collector->setDestinationAccounts(new Collection()->push($account)); $this->collector->setDestinationAccounts(new Collection()->push($account));
} }
@@ -1562,7 +1562,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-destination_account_id': case '-destination_account_id':
$account = $this->accountRepository->find((int)$value); $account = $this->accountRepository->find((int)$value);
if (null !== $account) { if (null !== $account) {
$this->collector->excludeDestinationAccounts(new Collection()->push($account)); $this->collector->excludeDestinationAccounts(new Collection()->push($account));
} }
@@ -1575,12 +1575,12 @@ class OperatorQuerySearch implements SearchInterface
case 'account_id': case 'account_id':
Log::debug(sprintf('Now in "account_id" with value "%s"', $value)); Log::debug(sprintf('Now in "account_id" with value "%s"', $value));
$parts = explode(',', $value); $parts = explode(',', $value);
$collection = new Collection(); $collection = new Collection();
foreach ($parts as $accountId) { foreach ($parts as $accountId) {
$accountId = (int)$accountId; $accountId = (int)$accountId;
Log::debug(sprintf('Searching for account with ID #%d', $accountId)); Log::debug(sprintf('Searching for account with ID #%d', $accountId));
$account = $this->accountRepository->find($accountId); $account = $this->accountRepository->find($accountId);
if (null !== $account) { if (null !== $account) {
Log::debug(sprintf('Found account with ID #%d ("%s")', $accountId, $account->name)); Log::debug(sprintf('Found account with ID #%d ("%s")', $accountId, $account->name));
$collection->push($account); $collection->push($account);
@@ -1601,8 +1601,8 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-account_id': case '-account_id':
$parts = explode(',', $value); $parts = explode(',', $value);
$collection = new Collection(); $collection = new Collection();
foreach ($parts as $accountId) { foreach ($parts as $accountId) {
$account = $this->accountRepository->find((int)$accountId); $account = $this->accountRepository->find((int)$accountId);
if (null !== $account) { if (null !== $account) {
@@ -1619,48 +1619,48 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// cash account // cash account
// //
case 'source_is_cash': case 'source_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->setSourceAccounts(new Collection()->push($account)); $this->collector->setSourceAccounts(new Collection()->push($account));
break; break;
case '-source_is_cash': case '-source_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->excludeSourceAccounts(new Collection()->push($account)); $this->collector->excludeSourceAccounts(new Collection()->push($account));
break; break;
case 'destination_is_cash': case 'destination_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->setDestinationAccounts(new Collection()->push($account)); $this->collector->setDestinationAccounts(new Collection()->push($account));
break; break;
case '-destination_is_cash': case '-destination_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->excludeDestinationAccounts(new Collection()->push($account)); $this->collector->excludeDestinationAccounts(new Collection()->push($account));
break; break;
case 'account_is_cash': case 'account_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->setAccounts(new Collection()->push($account)); $this->collector->setAccounts(new Collection()->push($account));
break; break;
case '-account_is_cash': case '-account_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->excludeAccounts(new Collection()->push($account)); $this->collector->excludeAccounts(new Collection()->push($account));
break; break;
// //
// description // description
// //
case 'description_starts': case 'description_starts':
$this->collector->descriptionStarts([$value]); $this->collector->descriptionStarts([$value]);
@@ -1682,7 +1682,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'description_contains': case 'description_contains':
$this->words[] = $value; $this->words[] = $value;
return false; return false;
@@ -1701,11 +1701,11 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// currency // currency
// //
case 'currency_is': case 'currency_is':
$currency = $this->findCurrency($value); $currency = $this->findCurrency($value);
if ($currency instanceof TransactionCurrency) { if ($currency instanceof TransactionCurrency) {
$this->collector->setCurrency($currency); $this->collector->setCurrency($currency);
} }
@@ -1717,7 +1717,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-currency_is': case '-currency_is':
$currency = $this->findCurrency($value); $currency = $this->findCurrency($value);
if ($currency instanceof TransactionCurrency) { if ($currency instanceof TransactionCurrency) {
$this->collector->excludeCurrency($currency); $this->collector->excludeCurrency($currency);
} }
@@ -1729,7 +1729,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'foreign_currency_is': case 'foreign_currency_is':
$currency = $this->findCurrency($value); $currency = $this->findCurrency($value);
if ($currency instanceof TransactionCurrency) { if ($currency instanceof TransactionCurrency) {
$this->collector->setForeignCurrency($currency); $this->collector->setForeignCurrency($currency);
} }
@@ -1741,7 +1741,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-foreign_currency_is': case '-foreign_currency_is':
$currency = $this->findCurrency($value); $currency = $this->findCurrency($value);
if ($currency instanceof TransactionCurrency) { if ($currency instanceof TransactionCurrency) {
$this->collector->excludeForeignCurrency($currency); $this->collector->excludeForeignCurrency($currency);
} }
@@ -1752,9 +1752,9 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// attachments // attachments
// //
case 'has_attachments': case 'has_attachments':
case '-has_no_attachments': case '-has_no_attachments':
Log::debug('Set collector to filter on attachments.'); Log::debug('Set collector to filter on attachments.');
@@ -1769,8 +1769,8 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// categories // categories
case '-has_any_category': case '-has_any_category':
case 'has_no_category': case 'has_no_category':
$this->collector->withoutCategory(); $this->collector->withoutCategory();
@@ -1784,7 +1784,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'category_is': case 'category_is':
$category = $this->categoryRepository->findByName($value); $category = $this->categoryRepository->findByName($value);
if (null !== $category) { if (null !== $category) {
$this->collector->setCategory($category); $this->collector->setCategory($category);
@@ -1796,7 +1796,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-category_is': case '-category_is':
$category = $this->categoryRepository->findByName($value); $category = $this->categoryRepository->findByName($value);
if (null !== $category) { if (null !== $category) {
$this->collector->excludeCategory($category); $this->collector->excludeCategory($category);
@@ -1806,7 +1806,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'category_ends': case 'category_ends':
$result = $this->categoryRepository->categoryEndsWith($value, 1337); $result = $this->categoryRepository->categoryEndsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->setCategories($result); $this->collector->setCategories($result);
} }
@@ -1818,7 +1818,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-category_ends': case '-category_ends':
$result = $this->categoryRepository->categoryEndsWith($value, 1337); $result = $this->categoryRepository->categoryEndsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->excludeCategories($result); $this->collector->excludeCategories($result);
} }
@@ -1830,7 +1830,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'category_starts': case 'category_starts':
$result = $this->categoryRepository->categoryStartsWith($value, 1337); $result = $this->categoryRepository->categoryStartsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->setCategories($result); $this->collector->setCategories($result);
} }
@@ -1842,7 +1842,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-category_starts': case '-category_starts':
$result = $this->categoryRepository->categoryStartsWith($value, 1337); $result = $this->categoryRepository->categoryStartsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->excludeCategories($result); $this->collector->excludeCategories($result);
} }
@@ -1854,7 +1854,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'category_contains': case 'category_contains':
$result = $this->categoryRepository->searchCategory($value, 1337); $result = $this->categoryRepository->searchCategory($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->setCategories($result); $this->collector->setCategories($result);
} }
@@ -1866,7 +1866,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-category_contains': case '-category_contains':
$result = $this->categoryRepository->searchCategory($value, 1337); $result = $this->categoryRepository->searchCategory($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->excludeCategories($result); $this->collector->excludeCategories($result);
} }
@@ -1877,9 +1877,9 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// budgets // budgets
// //
case '-has_any_budget': case '-has_any_budget':
case 'has_no_budget': case 'has_no_budget':
$this->collector->withoutBudget(); $this->collector->withoutBudget();
@@ -1893,7 +1893,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'budget_contains': case 'budget_contains':
$result = $this->budgetRepository->searchBudget($value, 1337); $result = $this->budgetRepository->searchBudget($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->setBudgets($result); $this->collector->setBudgets($result);
} }
@@ -1905,7 +1905,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-budget_contains': case '-budget_contains':
$result = $this->budgetRepository->searchBudget($value, 1337); $result = $this->budgetRepository->searchBudget($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->excludeBudgets($result); $this->collector->excludeBudgets($result);
} }
@@ -1917,7 +1917,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'budget_is': case 'budget_is':
$budget = $this->budgetRepository->findByName($value); $budget = $this->budgetRepository->findByName($value);
if (null !== $budget) { if (null !== $budget) {
$this->collector->setBudget($budget); $this->collector->setBudget($budget);
@@ -1929,7 +1929,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-budget_is': case '-budget_is':
$budget = $this->budgetRepository->findByName($value); $budget = $this->budgetRepository->findByName($value);
if (null !== $budget) { if (null !== $budget) {
$this->collector->excludeBudget($budget); $this->collector->excludeBudget($budget);
@@ -1941,7 +1941,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'budget_ends': case 'budget_ends':
$result = $this->budgetRepository->budgetEndsWith($value, 1337); $result = $this->budgetRepository->budgetEndsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->setBudgets($result); $this->collector->setBudgets($result);
} }
@@ -1953,7 +1953,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-budget_ends': case '-budget_ends':
$result = $this->budgetRepository->budgetEndsWith($value, 1337); $result = $this->budgetRepository->budgetEndsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->excludeBudgets($result); $this->collector->excludeBudgets($result);
} }
@@ -1965,7 +1965,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'budget_starts': case 'budget_starts':
$result = $this->budgetRepository->budgetStartsWith($value, 1337); $result = $this->budgetRepository->budgetStartsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->setBudgets($result); $this->collector->setBudgets($result);
} }
@@ -1977,7 +1977,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-budget_starts': case '-budget_starts':
$result = $this->budgetRepository->budgetStartsWith($value, 1337); $result = $this->budgetRepository->budgetStartsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->excludeBudgets($result); $this->collector->excludeBudgets($result);
} }
@@ -1988,9 +1988,9 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// bill // bill
// //
case '-has_any_bill': case '-has_any_bill':
case 'has_no_bill': case 'has_no_bill':
$this->collector->withoutBill(); $this->collector->withoutBill();
@@ -2004,7 +2004,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'bill_contains': case 'bill_contains':
$result = $this->billRepository->searchBill($value, 1337); $result = $this->billRepository->searchBill($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->setBills($result); $this->collector->setBills($result);
@@ -2016,7 +2016,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-bill_contains': case '-bill_contains':
$result = $this->billRepository->searchBill($value, 1337); $result = $this->billRepository->searchBill($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->excludeBills($result); $this->collector->excludeBills($result);
@@ -2028,7 +2028,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'bill_is': case 'bill_is':
$bill = $this->billRepository->findByName($value); $bill = $this->billRepository->findByName($value);
if (null !== $bill) { if (null !== $bill) {
$this->collector->setBill($bill); $this->collector->setBill($bill);
@@ -2040,7 +2040,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-bill_is': case '-bill_is':
$bill = $this->billRepository->findByName($value); $bill = $this->billRepository->findByName($value);
if (null !== $bill) { if (null !== $bill) {
$this->collector->excludeBills(new Collection()->push($bill)); $this->collector->excludeBills(new Collection()->push($bill));
@@ -2052,7 +2052,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'bill_ends': case 'bill_ends':
$result = $this->billRepository->billEndsWith($value, 1337); $result = $this->billRepository->billEndsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->setBills($result); $this->collector->setBills($result);
} }
@@ -2064,7 +2064,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-bill_ends': case '-bill_ends':
$result = $this->billRepository->billEndsWith($value, 1337); $result = $this->billRepository->billEndsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->excludeBills($result); $this->collector->excludeBills($result);
} }
@@ -2076,7 +2076,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'bill_starts': case 'bill_starts':
$result = $this->billRepository->billStartsWith($value, 1337); $result = $this->billRepository->billStartsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->setBills($result); $this->collector->setBills($result);
} }
@@ -2088,7 +2088,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-bill_starts': case '-bill_starts':
$result = $this->billRepository->billStartsWith($value, 1337); $result = $this->billRepository->billStartsWith($value, 1337);
if ($result->count() > 0) { if ($result->count() > 0) {
$this->collector->excludeBills($result); $this->collector->excludeBills($result);
} }
@@ -2099,9 +2099,9 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// tags // tags
// //
case '-has_any_tag': case '-has_any_tag':
case 'has_no_tag': case 'has_no_tag':
$this->collector->withoutTags(); $this->collector->withoutTags();
@@ -2116,7 +2116,7 @@ class OperatorQuerySearch implements SearchInterface
case '-tag_is_not': case '-tag_is_not':
case 'tag_is': case 'tag_is':
$result = $this->tagRepository->findByTag($value); $result = $this->tagRepository->findByTag($value);
if (null !== $result) { if (null !== $result) {
$this->includeTags[] = $result->id; $this->includeTags[] = $result->id;
$this->includeTags = array_unique($this->includeTags); $this->includeTags = array_unique($this->includeTags);
@@ -2131,7 +2131,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'tag_contains': case 'tag_contains':
$tags = $this->tagRepository->searchTag($value); $tags = $this->tagRepository->searchTag($value);
if (0 === $tags->count()) { if (0 === $tags->count()) {
Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
Log::warning(sprintf('Call to findNothing() from %s.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator));
@@ -2146,7 +2146,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'tag_starts': case 'tag_starts':
$tags = $this->tagRepository->tagStartsWith($value); $tags = $this->tagRepository->tagStartsWith($value);
if (0 === $tags->count()) { if (0 === $tags->count()) {
Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
Log::warning(sprintf('Call to findNothing() from %s.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator));
@@ -2161,7 +2161,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-tag_starts': case '-tag_starts':
$tags = $this->tagRepository->tagStartsWith($value); $tags = $this->tagRepository->tagStartsWith($value);
if (0 === $tags->count()) { if (0 === $tags->count()) {
Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
Log::warning(sprintf('Call to findNothing() from %s.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator));
@@ -2175,7 +2175,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'tag_ends': case 'tag_ends':
$tags = $this->tagRepository->tagEndsWith($value); $tags = $this->tagRepository->tagEndsWith($value);
if (0 === $tags->count()) { if (0 === $tags->count()) {
Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
Log::warning(sprintf('Call to findNothing() from %s.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator));
@@ -2189,7 +2189,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-tag_ends': case '-tag_ends':
$tags = $this->tagRepository->tagEndsWith($value); $tags = $this->tagRepository->tagEndsWith($value);
if (0 === $tags->count()) { if (0 === $tags->count()) {
Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
Log::warning(sprintf('Call to findNothing() from %s.', $operator)); Log::warning(sprintf('Call to findNothing() from %s.', $operator));
@@ -2203,7 +2203,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-tag_contains': case '-tag_contains':
$tags = $this->tagRepository->searchTag($value)->keyBy('id'); $tags = $this->tagRepository->searchTag($value)->keyBy('id');
if (0 === $tags->count()) { if (0 === $tags->count()) {
Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); Log::info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
@@ -2219,7 +2219,7 @@ class OperatorQuerySearch implements SearchInterface
case '-tag_is': case '-tag_is':
case 'tag_is_not': case 'tag_is_not':
$result = $this->tagRepository->findByTag($value); $result = $this->tagRepository->findByTag($value);
if (null !== $result) { if (null !== $result) {
$this->excludeTags[] = $result->id; $this->excludeTags[] = $result->id;
$this->excludeTags = array_unique($this->excludeTags); $this->excludeTags = array_unique($this->excludeTags);
@@ -2227,9 +2227,9 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// notes // notes
// //
case 'notes_contains': case 'notes_contains':
$this->collector->notesContain($value); $this->collector->notesContain($value);
@@ -2292,14 +2292,14 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// amount // amount
// //
case 'amount_is': case 'amount_is':
// strip comma's, make dots. // strip comma's, make dots.
Log::debug(sprintf('Original value "%s"', $value)); Log::debug(sprintf('Original value "%s"', $value));
$value = str_replace(',', '.', $value); $value = str_replace(',', '.', $value);
$amount = app('steam')->positive($value); $amount = app('steam')->positive($value);
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->amountIs($amount); $this->collector->amountIs($amount);
@@ -2308,8 +2308,8 @@ class OperatorQuerySearch implements SearchInterface
case '-amount_is': case '-amount_is':
// strip comma's, make dots. // strip comma's, make dots.
Log::debug(sprintf('Original value "%s"', $value)); Log::debug(sprintf('Original value "%s"', $value));
$value = str_replace(',', '.', $value); $value = str_replace(',', '.', $value);
$amount = app('steam')->positive($value); $amount = app('steam')->positive($value);
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->amountIsNot($amount); $this->collector->amountIsNot($amount);
@@ -2317,9 +2317,9 @@ class OperatorQuerySearch implements SearchInterface
case 'foreign_amount_is': case 'foreign_amount_is':
// strip comma's, make dots. // strip comma's, make dots.
$value = str_replace(',', '.', $value); $value = str_replace(',', '.', $value);
$amount = app('steam')->positive($value); $amount = app('steam')->positive($value);
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->foreignAmountIs($amount); $this->collector->foreignAmountIs($amount);
@@ -2327,9 +2327,9 @@ class OperatorQuerySearch implements SearchInterface
case '-foreign_amount_is': case '-foreign_amount_is':
// strip comma's, make dots. // strip comma's, make dots.
$value = str_replace(',', '.', $value); $value = str_replace(',', '.', $value);
$amount = app('steam')->positive($value); $amount = app('steam')->positive($value);
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->foreignAmountIsNot($amount); $this->collector->foreignAmountIsNot($amount);
@@ -2338,9 +2338,9 @@ class OperatorQuerySearch implements SearchInterface
case '-amount_more': case '-amount_more':
case 'amount_less': case 'amount_less':
// strip comma's, make dots. // strip comma's, make dots.
$value = str_replace(',', '.', $value); $value = str_replace(',', '.', $value);
$amount = app('steam')->positive($value); $amount = app('steam')->positive($value);
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->amountLess($amount); $this->collector->amountLess($amount);
@@ -2349,9 +2349,9 @@ class OperatorQuerySearch implements SearchInterface
case '-foreign_amount_more': case '-foreign_amount_more':
case 'foreign_amount_less': case 'foreign_amount_less':
// strip comma's, make dots. // strip comma's, make dots.
$value = str_replace(',', '.', $value); $value = str_replace(',', '.', $value);
$amount = app('steam')->positive($value); $amount = app('steam')->positive($value);
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->foreignAmountLess($amount); $this->collector->foreignAmountLess($amount);
@@ -2361,8 +2361,8 @@ class OperatorQuerySearch implements SearchInterface
case 'amount_more': case 'amount_more':
Log::debug(sprintf('Now handling operator "%s"', $operator)); Log::debug(sprintf('Now handling operator "%s"', $operator));
// strip comma's, make dots. // strip comma's, make dots.
$value = str_replace(',', '.', $value); $value = str_replace(',', '.', $value);
$amount = app('steam')->positive($value); $amount = app('steam')->positive($value);
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->amountMore($amount); $this->collector->amountMore($amount);
@@ -2372,16 +2372,16 @@ class OperatorQuerySearch implements SearchInterface
case 'foreign_amount_more': case 'foreign_amount_more':
Log::debug(sprintf('Now handling operator "%s"', $operator)); Log::debug(sprintf('Now handling operator "%s"', $operator));
// strip comma's, make dots. // strip comma's, make dots.
$value = str_replace(',', '.', $value); $value = str_replace(',', '.', $value);
$amount = app('steam')->positive($value); $amount = app('steam')->positive($value);
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->foreignAmountMore($amount); $this->collector->foreignAmountMore($amount);
break; break;
// //
// transaction type // transaction type
// //
case 'transaction_type': case 'transaction_type':
$this->collector->setTypes([ucfirst($value)]); $this->collector->setTypes([ucfirst($value)]);
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
@@ -2394,152 +2394,152 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// dates // dates
// //
case '-date_on': case '-date_on':
case 'date_on': case 'date_on':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setExactDateParams($range, $prohibited); $this->setExactDateParams($range, $prohibited);
return false; return false;
case 'date_before': case 'date_before':
case '-date_after': case '-date_after':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setDateBeforeParams($range); $this->setDateBeforeParams($range);
return false; return false;
case 'date_after': case 'date_after':
case '-date_before': case '-date_before':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setDateAfterParams($range); $this->setDateAfterParams($range);
return false; return false;
case 'interest_date_on': case 'interest_date_on':
case '-interest_date_on': case '-interest_date_on':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('interest_date', $range, $prohibited); $this->setExactMetaDateParams('interest_date', $range, $prohibited);
return false; return false;
case 'interest_date_before': case 'interest_date_before':
case '-interest_date_after': case '-interest_date_after':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('interest_date', $range); $this->setMetaDateBeforeParams('interest_date', $range);
return false; return false;
case 'interest_date_after': case 'interest_date_after':
case '-interest_date_before': case '-interest_date_before':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('interest_date', $range); $this->setMetaDateAfterParams('interest_date', $range);
return false; return false;
case 'book_date_on': case 'book_date_on':
case '-book_date_on': case '-book_date_on':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('book_date', $range, $prohibited); $this->setExactMetaDateParams('book_date', $range, $prohibited);
return false; return false;
case 'book_date_before': case 'book_date_before':
case '-book_date_after': case '-book_date_after':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('book_date', $range); $this->setMetaDateBeforeParams('book_date', $range);
return false; return false;
case 'book_date_after': case 'book_date_after':
case '-book_date_before': case '-book_date_before':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('book_date', $range); $this->setMetaDateAfterParams('book_date', $range);
return false; return false;
case 'process_date_on': case 'process_date_on':
case '-process_date_on': case '-process_date_on':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('process_date', $range, $prohibited); $this->setExactMetaDateParams('process_date', $range, $prohibited);
return false; return false;
case 'process_date_before': case 'process_date_before':
case '-process_date_after': case '-process_date_after':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('process_date', $range); $this->setMetaDateBeforeParams('process_date', $range);
return false; return false;
case 'process_date_after': case 'process_date_after':
case '-process_date_before': case '-process_date_before':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('process_date', $range); $this->setMetaDateAfterParams('process_date', $range);
return false; return false;
case 'due_date_on': case 'due_date_on':
case '-due_date_on': case '-due_date_on':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('due_date', $range, $prohibited); $this->setExactMetaDateParams('due_date', $range, $prohibited);
return false; return false;
case 'due_date_before': case 'due_date_before':
case '-due_date_after': case '-due_date_after':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('due_date', $range); $this->setMetaDateBeforeParams('due_date', $range);
return false; return false;
case 'due_date_after': case 'due_date_after':
case '-due_date_before': case '-due_date_before':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('due_date', $range); $this->setMetaDateAfterParams('due_date', $range);
return false; return false;
case 'payment_date_on': case 'payment_date_on':
case '-payment_date_on': case '-payment_date_on':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('payment_date', $range, $prohibited); $this->setExactMetaDateParams('payment_date', $range, $prohibited);
return false; return false;
case 'payment_date_before': case 'payment_date_before':
case '-payment_date_after': case '-payment_date_after':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('payment_date', $range); $this->setMetaDateBeforeParams('payment_date', $range);
return false; return false;
case 'payment_date_after': case 'payment_date_after':
case '-payment_date_before': case '-payment_date_before':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('payment_date', $range); $this->setMetaDateAfterParams('payment_date', $range);
return false; return false;
case 'invoice_date_on': case 'invoice_date_on':
case '-invoice_date_on': case '-invoice_date_on':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setExactMetaDateParams('invoice_date', $range, $prohibited); $this->setExactMetaDateParams('invoice_date', $range, $prohibited);
return false; return false;
case 'invoice_date_before': case 'invoice_date_before':
case '-invoice_date_after': case '-invoice_date_after':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateBeforeParams('invoice_date', $range); $this->setMetaDateBeforeParams('invoice_date', $range);
return false; return false;
case 'invoice_date_after': case 'invoice_date_after':
case '-invoice_date_before': case '-invoice_date_before':
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setMetaDateAfterParams('invoice_date', $range); $this->setMetaDateAfterParams('invoice_date', $range);
return false; return false;
@@ -2547,7 +2547,7 @@ class OperatorQuerySearch implements SearchInterface
case 'created_at_on': case 'created_at_on':
case '-created_at_on': case '-created_at_on':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setExactObjectDateParams('created_at', $range, $prohibited); $this->setExactObjectDateParams('created_at', $range, $prohibited);
return false; return false;
@@ -2555,7 +2555,7 @@ class OperatorQuerySearch implements SearchInterface
case 'created_at_before': case 'created_at_before':
case '-created_at_after': case '-created_at_after':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setObjectDateBeforeParams('created_at', $range); $this->setObjectDateBeforeParams('created_at', $range);
return false; return false;
@@ -2563,7 +2563,7 @@ class OperatorQuerySearch implements SearchInterface
case 'created_at_after': case 'created_at_after':
case '-created_at_before': case '-created_at_before':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setObjectDateAfterParams('created_at', $range); $this->setObjectDateAfterParams('created_at', $range);
return false; return false;
@@ -2571,7 +2571,7 @@ class OperatorQuerySearch implements SearchInterface
case 'updated_at_on': case 'updated_at_on':
case '-updated_at_on': case '-updated_at_on':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setExactObjectDateParams('updated_at', $range, $prohibited); $this->setExactObjectDateParams('updated_at', $range, $prohibited);
return false; return false;
@@ -2579,7 +2579,7 @@ class OperatorQuerySearch implements SearchInterface
case 'updated_at_before': case 'updated_at_before':
case '-updated_at_after': case '-updated_at_after':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setObjectDateBeforeParams('updated_at', $range); $this->setObjectDateBeforeParams('updated_at', $range);
return false; return false;
@@ -2587,14 +2587,14 @@ class OperatorQuerySearch implements SearchInterface
case 'updated_at_after': case 'updated_at_after':
case '-updated_at_before': case '-updated_at_before':
Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); Log::debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
$range = $this->parseDateRange($operator, $value); $range = $this->parseDateRange($operator, $value);
$this->setObjectDateAfterParams('updated_at', $range); $this->setObjectDateAfterParams('updated_at', $range);
return false; return false;
// //
// external URL // external URL
// //
case '-any_external_url': case '-any_external_url':
case 'no_external_url': case 'no_external_url':
$this->collector->withoutExternalUrl(); $this->collector->withoutExternalUrl();
@@ -2659,9 +2659,9 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// other fields // other fields
// //
case 'external_id_is': case 'external_id_is':
$this->collector->setExternalId($value); $this->collector->setExternalId($value);

View File

@@ -32,6 +32,7 @@ use Gdbots\QueryParser\QueryParser as BaseQueryParser;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use LogicException; use LogicException;
use TypeError; use TypeError;
use function Safe\fwrite; use function Safe\fwrite;
class GdbotsQueryParser implements QueryParserInterface class GdbotsQueryParser implements QueryParserInterface
@@ -51,12 +52,12 @@ class GdbotsQueryParser implements QueryParserInterface
try { try {
$result = $this->parser->parse($query); $result = $this->parser->parse($query);
$nodes = array_map( $nodes = array_map(
fn(GdbotsNode\Node $node) => $this->convertNode($node), fn (GdbotsNode\Node $node) => $this->convertNode($node),
$result->getNodes() $result->getNodes()
); );
return new NodeGroup($nodes); return new NodeGroup($nodes);
} catch (LogicException | TypeError $e) { } catch (LogicException|TypeError $e) {
fwrite(STDERR, "Setting up GdbotsQueryParserTest\n"); fwrite(STDERR, "Setting up GdbotsQueryParserTest\n");
app('log')->error($e->getMessage()); app('log')->error($e->getMessage());
app('log')->error(sprintf('Could not parse search: "%s".', $query)); app('log')->error(sprintf('Could not parse search: "%s".', $query));
@@ -84,7 +85,7 @@ class GdbotsQueryParser implements QueryParserInterface
return new NodeGroup( return new NodeGroup(
array_map( array_map(
fn(GdbotsNode\Node $subNode) => $this->convertNode($subNode), fn (GdbotsNode\Node $subNode) => $this->convertNode($subNode),
$node->getNodes() $node->getNodes()
) )
); );

View File

@@ -139,7 +139,7 @@ class QueryParser implements QueryParserInterface
if ('' === $tokenUnderConstruction) { if ('' === $tokenUnderConstruction) {
// In any other location, it's just a normal character // In any other location, it's just a normal character
$tokenUnderConstruction .= $char; $tokenUnderConstruction .= $char;
$skipNext = true; $skipNext = true;
} }
if ('' !== $tokenUnderConstruction && !$skipNext) { // @phpstan-ignore-line if ('' !== $tokenUnderConstruction && !$skipNext) { // @phpstan-ignore-line
Log::debug(sprintf('Turns out that "%s" is a field name. Reset the token.', $tokenUnderConstruction)); Log::debug(sprintf('Turns out that "%s" is a field name. Reset the token.', $tokenUnderConstruction));
@@ -171,7 +171,7 @@ class QueryParser implements QueryParserInterface
++$this->position; ++$this->position;
} }
$finalNode = '' !== $tokenUnderConstruction || '' !== $fieldName $finalNode = '' !== $tokenUnderConstruction || '' !== $fieldName
? $this->createNode($tokenUnderConstruction, $fieldName, $prohibited) ? $this->createNode($tokenUnderConstruction, $fieldName, $prohibited)
: null; : null;
@@ -184,7 +184,7 @@ class QueryParser implements QueryParserInterface
$nodeResult = $this->buildNextNode($isSubquery); $nodeResult = $this->buildNextNode($isSubquery);
while ($nodeResult->node instanceof Node) { while ($nodeResult->node instanceof Node) {
$nodes[] = $nodeResult->node; $nodes[] = $nodeResult->node;
if ($nodeResult->isSubqueryEnd) { if ($nodeResult->isSubqueryEnd) {
break; break;
} }

View File

@@ -29,7 +29,7 @@ class PreferencesSingleton
{ {
private static ?PreferencesSingleton $instance = null; private static ?PreferencesSingleton $instance = null;
private array $preferences = []; private array $preferences = [];
private function __construct() private function __construct()
{ {

View File

@@ -38,6 +38,7 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use ValueError; use ValueError;
use function Safe\parse_url; use function Safe\parse_url;
use function Safe\preg_replace; use function Safe\preg_replace;
@@ -49,45 +50,46 @@ class Steam
public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array
{ {
Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String())); Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String()));
$result = []; $result = [];
$convertToPrimary ??= Amount::convertToPrimary(); $convertToPrimary ??= Amount::convertToPrimary();
$primary ??= Amount::getPrimaryCurrency(); $primary ??= Amount::getPrimaryCurrency();
$currencies = $this->getCurrencies($accounts); $currencies = $this->getCurrencies($accounts);
// balance(s) in all currencies for ALL accounts. // balance(s) in all currencies for ALL accounts.
$arrayOfSums = Transaction::whereIn('account_id', $accounts->pluck('id')->toArray()) $arrayOfSums = Transaction::whereIn('account_id', $accounts->pluck('id')->toArray())
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->groupBy(['transactions.account_id', 'transaction_currencies.code']) ->groupBy(['transactions.account_id', 'transaction_currencies.code'])
->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->toArray(); ->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->toArray()
;
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
// this array is PER account, so we wait a bit before we change code here. // this array is PER account, so we wait a bit before we change code here.
$return = [ $return = [
'pc_balance' => '0', 'pc_balance' => '0',
'balance' => '0', // this key is overwritten right away, but I must remember it is always created. 'balance' => '0', // this key is overwritten right away, but I must remember it is always created.
]; ];
$currency = $currencies[$account->id]; $currency = $currencies[$account->id];
// second array // second array
$accountSum = array_filter($arrayOfSums, fn($entry) => $entry['account_id'] === $account->id); $accountSum = array_filter($arrayOfSums, fn ($entry) => $entry['account_id'] === $account->id);
if (0 === count($accountSum)) { if (0 === count($accountSum)) {
$result[$account->id] = $return; $result[$account->id] = $return;
continue; continue;
} }
$accountSum = array_values($accountSum)[0]; $accountSum = array_values($accountSum)[0];
$sumOfAmount = (string)$accountSum['sum_of_amount']; $sumOfAmount = (string)$accountSum['sum_of_amount'];
$sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount); $sumOfAmount = $this->floatalize('' === $sumOfAmount ? '0' : $sumOfAmount);
$sumsByCode = [ $sumsByCode = [
$accountSum['code'] => $sumOfAmount, $accountSum['code'] => $sumOfAmount,
]; ];
// Log::debug('All balances are (joined)', $others); // Log::debug('All balances are (joined)', $others);
// if there is no request to convert, take this as "balance" and "pc_balance". // if there is no request to convert, take this as "balance" and "pc_balance".
$return['balance'] = $sumsByCode[$currency->code] ?? '0'; $return['balance'] = $sumsByCode[$currency->code] ?? '0';
if (!$convertToPrimary) { if (!$convertToPrimary) {
unset($return['pc_balance']); unset($return['pc_balance']);
// Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance'])); // Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance']));
@@ -99,7 +101,7 @@ class Steam
} }
// either way, the balance is always combined with the virtual balance: // either way, the balance is always combined with the virtual balance:
$virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance);
if ($convertToPrimary) { if ($convertToPrimary) {
// the primary currency balance is combined with a converted virtual_balance: // the primary currency balance is combined with a converted virtual_balance:
@@ -140,10 +142,10 @@ class Steam
// Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision)); // Log::debug(sprintf('Trying bcround("%s",%d)', $number, $precision));
if (str_contains($number, '.')) { if (str_contains($number, '.')) {
if ('-' !== $number[0]) { if ('-' !== $number[0]) {
return bcadd($number, '0.' . str_repeat('0', $precision) . '5', $precision); return bcadd($number, '0.'.str_repeat('0', $precision).'5', $precision);
} }
return bcsub($number, '0.' . str_repeat('0', $precision) . '5', $precision); return bcsub($number, '0.'.str_repeat('0', $precision).'5', $precision);
} }
return $number; return $number;
@@ -287,7 +289,7 @@ class Steam
public function finalAccountBalance(Account $account, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array public function finalAccountBalance(Account $account, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty($date); $cache->addProperty($date);
if ($cache->has()) { if ($cache->has()) {
@@ -303,7 +305,7 @@ class Steam
$primary = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); $primary = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
} }
// account balance thing. // account balance thing.
$currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency'];
if ($currencyPresent) { if ($currencyPresent) {
$accountCurrency = $account->meta['currency']; $accountCurrency = $account->meta['currency'];
} }
@@ -311,19 +313,20 @@ class Steam
$accountCurrency = $this->getAccountCurrency($account); $accountCurrency = $this->getAccountCurrency($account);
} }
$hasCurrency = null !== $accountCurrency; $hasCurrency = null !== $accountCurrency;
$currency = $hasCurrency ? $accountCurrency : $primary; $currency = $hasCurrency ? $accountCurrency : $primary;
$return = [ $return = [
'pc_balance' => '0', 'pc_balance' => '0',
'balance' => '0', // this key is overwritten right away, but I must remember it is always created. 'balance' => '0', // this key is overwritten right away, but I must remember it is always created.
]; ];
// balance(s) in all currencies. // balance(s) in all currencies.
$array = $account->transactions() $array = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') ->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id') ->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
->get(['transaction_currencies.code', 'transactions.amount'])->toArray(); ->get(['transaction_currencies.code', 'transactions.amount'])->toArray()
$others = $this->groupAndSumTransactions($array, 'code', 'amount'); ;
$others = $this->groupAndSumTransactions($array, 'code', 'amount');
// Log::debug('All balances are (joined)', $others); // Log::debug('All balances are (joined)', $others);
// if there is no request to convert, take this as "balance" and "pc_balance". // if there is no request to convert, take this as "balance" and "pc_balance".
$return['balance'] = $others[$currency->code] ?? '0'; $return['balance'] = $others[$currency->code] ?? '0';
@@ -338,7 +341,7 @@ class Steam
} }
// either way, the balance is always combined with the virtual balance: // either way, the balance is always combined with the virtual balance:
$virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance); $virtualBalance = (string)('' === (string)$account->virtual_balance ? '0' : $account->virtual_balance);
if ($convertToPrimary) { if ($convertToPrimary) {
// the primary currency balance is combined with a converted virtual_balance: // the primary currency balance is combined with a converted virtual_balance:
@@ -352,7 +355,7 @@ class Steam
$return['balance'] = bcadd($return['balance'], $virtualBalance); $return['balance'] = bcadd($return['balance'], $virtualBalance);
// Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance'])); // Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance']));
} }
$final = array_merge($return, $others); $final = array_merge($return, $others);
// Log::debug('Final balance is', $final); // Log::debug('Final balance is', $final);
$cache->store($final); $cache->store($final);
@@ -367,7 +370,7 @@ class Steam
Log::debug(sprintf('finalAccountBalanceInRange(#%d, %s, %s)', $account->id, $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('finalAccountBalanceInRange(#%d, %s, %s)', $account->id, $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// set up cache // set up cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty('final-balance-in-range'); $cache->addProperty('final-balance-in-range');
$cache->addProperty($start); $cache->addProperty($start);
@@ -377,22 +380,22 @@ class Steam
return $cache->get(); return $cache->get();
} }
$balances = []; $balances = [];
$formatted = $start->format('Y-m-d'); $formatted = $start->format('Y-m-d');
/* /*
* To make sure the start balance is correct, we need to get the balance at the exact end of the previous day. * To make sure the start balance is correct, we need to get the balance at the exact end of the previous day.
* Since we just did "startOfDay" we can do subDay()->endOfDay() to get the correct moment. * Since we just did "startOfDay" we can do subDay()->endOfDay() to get the correct moment.
* THAT will be the start balance. * THAT will be the start balance.
*/ */
$request = clone $start; $request = clone $start;
$request->subDay()->endOfDay(); $request->subDay()->endOfDay();
Log::debug('Get first balance to start.'); Log::debug('Get first balance to start.');
Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String())); Log::debug(sprintf('finalAccountBalanceInRange: Call finalAccountBalance with date/time "%s"', $request->toIso8601String()));
$startBalance = $this->finalAccountBalance($account, $request); $startBalance = $this->finalAccountBalance($account, $request);
$primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup); $primaryCurrency = Amount::getPrimaryCurrencyByUserGroup($account->user->userGroup);
$accountCurrency = $this->getAccountCurrency($account); $accountCurrency = $this->getAccountCurrency($account);
$hasCurrency = $accountCurrency instanceof TransactionCurrency; $hasCurrency = $accountCurrency instanceof TransactionCurrency;
$currency = $accountCurrency ?? $primaryCurrency; $currency = $accountCurrency ?? $primaryCurrency;
Log::debug(sprintf('Currency is %s', $currency->code)); Log::debug(sprintf('Currency is %s', $currency->code));
@@ -405,7 +408,7 @@ class Steam
Log::debug(sprintf('Also set start balance in %s', $primaryCurrency->code)); Log::debug(sprintf('Also set start balance in %s', $primaryCurrency->code));
$startBalance[$primaryCurrency->code] ??= '0'; $startBalance[$primaryCurrency->code] ??= '0';
} }
$currencies = [ $currencies = [
$currency->id => $currency, $currency->id => $currency,
$primaryCurrency->id => $primaryCurrency, $primaryCurrency->id => $primaryCurrency,
]; ];
@@ -415,47 +418,48 @@ class Steam
// sums up the balance changes per day. // sums up the balance changes per day.
Log::debug(sprintf('Date >= %s and <= %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s'))); Log::debug(sprintf('Date >= %s and <= %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
$set = $account->transactions() $set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id') ->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s'))
->groupBy('transaction_journals.date') ->groupBy('transaction_journals.date')
->groupBy('transactions.transaction_currency_id') ->groupBy('transactions.transaction_currency_id')
->orderBy('transaction_journals.date', 'ASC') ->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at') ->whereNull('transaction_journals.deleted_at')
->get( ->get(
[ // @phpstan-ignore-line [ // @phpstan-ignore-line
'transaction_journals.date', 'transaction_journals.date',
'transactions.transaction_currency_id', 'transactions.transaction_currency_id',
DB::raw('SUM(transactions.amount) AS sum_of_day'), DB::raw('SUM(transactions.amount) AS sum_of_day'),
] ]
); )
;
$currentBalance = $startBalance; $currentBalance = $startBalance;
$converter = new ExchangeRateConverter(); $converter = new ExchangeRateConverter();
/** @var Transaction $entry */ /** @var Transaction $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
// get date object // get date object
$carbon = new Carbon($entry->date, $entry->date_tz); $carbon = new Carbon($entry->date, $entry->date_tz);
$carbonKey = $carbon->format('Y-m-d'); $carbonKey = $carbon->format('Y-m-d');
// make sure sum is a string: // make sure sum is a string:
$sumOfDay = (string)($entry->sum_of_day ?? '0'); $sumOfDay = (string)($entry->sum_of_day ?? '0');
// #10426 make sure sum is not in scientific notation. // #10426 make sure sum is not in scientific notation.
$sumOfDay = $this->floatalize($sumOfDay); $sumOfDay = $this->floatalize($sumOfDay);
// find currency of this entry, does not have to exist. // find currency of this entry, does not have to exist.
$currencies[$entry->transaction_currency_id] ??= Amount::getTransactionCurrencyById($entry->transaction_currency_id); $currencies[$entry->transaction_currency_id] ??= Amount::getTransactionCurrencyById($entry->transaction_currency_id);
// make sure this $entry has its own $entryCurrency // make sure this $entry has its own $entryCurrency
/** @var TransactionCurrency $entryCurrency */ /** @var TransactionCurrency $entryCurrency */
$entryCurrency = $currencies[$entry->transaction_currency_id]; $entryCurrency = $currencies[$entry->transaction_currency_id];
Log::debug(sprintf('Processing transaction(s) on moment %s', $carbon->format('Y-m-d H:i:s'))); Log::debug(sprintf('Processing transaction(s) on moment %s', $carbon->format('Y-m-d H:i:s')));
// add amount to current balance in currency code. // add amount to current balance in currency code.
$currentBalance[$entryCurrency->code] ??= '0'; $currentBalance[$entryCurrency->code] ??= '0';
$currentBalance[$entryCurrency->code] = bcadd($sumOfDay, (string)$currentBalance[$entryCurrency->code]); $currentBalance[$entryCurrency->code] = bcadd($sumOfDay, (string)$currentBalance[$entryCurrency->code]);
// if not requested to convert to primary currency, add the amount to "balance", do nothing else. // if not requested to convert to primary currency, add the amount to "balance", do nothing else.
@@ -473,7 +477,7 @@ class Steam
} }
} }
// add to final array. // add to final array.
$balances[$carbonKey] = $currentBalance; $balances[$carbonKey] = $currentBalance;
Log::debug(sprintf('Updated entry [%s]', $carbonKey), $currentBalance); Log::debug(sprintf('Updated entry [%s]', $carbonKey), $currentBalance);
} }
$cache->store($balances); $cache->store($balances);
@@ -490,7 +494,7 @@ class Steam
*/ */
public function floatalize(string $value): string public function floatalize(string $value): string
{ {
$value = strtoupper($value); $value = strtoupper($value);
if (!str_contains($value, 'E')) { if (!str_contains($value, 'E')) {
return $value; return $value;
} }
@@ -514,8 +518,8 @@ class Steam
public function getAccountCurrency(Account $account): ?TransactionCurrency public function getAccountCurrency(Account $account): ?TransactionCurrency
{ {
$type = $account->accountType->type; $type = $account->accountType->type;
$list = config('firefly.valid_currency_account_types'); $list = config('firefly.valid_currency_account_types');
// return null if not in this list. // return null if not in this list.
if (!in_array($type, $list, true)) { if (!in_array($type, $list, true)) {
@@ -569,15 +573,15 @@ class Steam
{ {
$list = []; $list = [];
$set = auth()->user()->transactions() $set = auth()->user()->transactions()
->whereIn('transactions.account_id', $accounts) ->whereIn('transactions.account_id', $accounts)
->groupBy(['transactions.account_id', 'transaction_journals.user_id']) ->groupBy(['transactions.account_id', 'transaction_journals.user_id'])
->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line ->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]) // @phpstan-ignore-line
; ;
/** @var Transaction $entry */ /** @var Transaction $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
$date = new Carbon($entry->max_date, config('app.timezone')); $date = new Carbon($entry->max_date, config('app.timezone'));
$date->setTimezone(config('app.timezone')); $date->setTimezone(config('app.timezone'));
$list[(int)$entry->account_id] = $date; $list[(int)$entry->account_id] = $date;
} }
@@ -591,24 +595,25 @@ class Steam
public function getLocale(): string // get preference public function getLocale(): string // get preference
{ {
$singleton = PreferencesSingleton::getInstance(); $singleton = PreferencesSingleton::getInstance();
$cached = $singleton->getPreference('locale'); $cached = $singleton->getPreference('locale');
if(null !== $cached) { if (null !== $cached) {
return $cached; return $cached;
} }
$locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data; $locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data;
if (is_array($locale)) { if (is_array($locale)) {
$locale = 'equal'; $locale = 'equal';
} }
if ('equal' === $locale) { if ('equal' === $locale) {
$locale = $this->getLanguage(); $locale = $this->getLanguage();
} }
$locale = (string)$locale; $locale = (string)$locale;
// Check for Windows to replace the locale correctly. // Check for Windows to replace the locale correctly.
if ('WIN' === strtoupper(substr(PHP_OS, 0, 3))) { if ('WIN' === strtoupper(substr(PHP_OS, 0, 3))) {
$locale = str_replace('_', '-', $locale); $locale = str_replace('_', '-', $locale);
} }
$singleton->setPreference('locale', $locale); $singleton->setPreference('locale', $locale);
return $locale; return $locale;
} }
@@ -642,9 +647,9 @@ class Steam
public function getSafeUrl(string $unknownUrl, string $safeUrl): string public function getSafeUrl(string $unknownUrl, string $safeUrl): string
{ {
// Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl)); // Log::debug(sprintf('getSafeUrl(%s, %s)', $unknownUrl, $safeUrl));
$returnUrl = $safeUrl; $returnUrl = $safeUrl;
$unknownHost = parse_url($unknownUrl, PHP_URL_HOST); $unknownHost = parse_url($unknownUrl, PHP_URL_HOST);
$safeHost = parse_url($safeUrl, PHP_URL_HOST); $safeHost = parse_url($safeUrl, PHP_URL_HOST);
if (null !== $unknownHost && $unknownHost === $safeHost) { if (null !== $unknownHost && $unknownHost === $safeHost) {
$returnUrl = $unknownUrl; $returnUrl = $unknownUrl;
@@ -746,12 +751,12 @@ class Steam
if (null === $preference) { if (null === $preference) {
$singleton->setPreference($key, $currency); $singleton->setPreference($key, $currency);
} }
$current = $amount; $current = $amount;
if ($currency->id !== $primary->id) { if ($currency->id !== $primary->id) {
$current = $converter->convert($currency, $primary, $date, $amount); $current = $converter->convert($currency, $primary, $date, $amount);
Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current)); Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current));
} }
$total = bcadd($current, $total); $total = bcadd($current, $total);
} }
return $total; return $total;
@@ -765,8 +770,8 @@ class Steam
$primary = Amount::getPrimaryCurrency(); $primary = Amount::getPrimaryCurrency();
$currencies[$primary->id] = $primary; $currencies[$primary->id] = $primary;
$ids = $accounts->pluck('id')->toArray(); $ids = $accounts->pluck('id')->toArray();
$result = AccountMeta::whereIn('account_id', $ids)->where('name', 'currency_id')->get(); $result = AccountMeta::whereIn('account_id', $ids)->where('name', 'currency_id')->get();
/** @var AccountMeta $item */ /** @var AccountMeta $item */
foreach ($result as $item) { foreach ($result as $item) {
@@ -776,7 +781,7 @@ class Steam
} }
} }
// collect those currencies, skip primary because we already have it. // collect those currencies, skip primary because we already have it.
$set = TransactionCurrency::whereIn('id', $accountPreferences)->where('id', '!=', $primary->id)->get(); $set = TransactionCurrency::whereIn('id', $accountPreferences)->where('id', '!=', $primary->id)->get();
foreach ($set as $item) { foreach ($set as $item) {
$currencies[$item->id] = $item; $currencies[$item->id] = $item;
} }
@@ -787,7 +792,7 @@ class Steam
$currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency']; $currencyPresent = isset($account->meta) && array_key_exists('currency', $account->meta) && null !== $account->meta['currency'];
if ($currencyPresent) { if ($currencyPresent) {
$currencyId = $account->meta['currency']->id; $currencyId = $account->meta['currency']->id;
$currencies[$currencyId] ??= $account->meta['currency']; $currencies[$currencyId] ??= $account->meta['currency'];
$accountCurrencies[$accountId] = $account->meta['currency']; $accountCurrencies[$accountId] = $account->meta['currency'];
} }
if (!$currencyPresent && !array_key_exists($accountId, $accountPreferences)) { if (!$currencyPresent && !array_key_exists($accountId, $accountPreferences)) {

View File

@@ -31,6 +31,7 @@ use Illuminate\Support\Facades\Crypt;
use Laravel\Passport\Console\KeysCommand; use Laravel\Passport\Console\KeysCommand;
use Psr\Container\ContainerExceptionInterface; use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface; use Psr\Container\NotFoundExceptionInterface;
use function Safe\file_get_contents; use function Safe\file_get_contents;
use function Safe\file_put_contents; use function Safe\file_put_contents;
@@ -65,7 +66,7 @@ class OAuthKeys
try { try {
$privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data; $privateKey = (string)app('fireflyconfig')->get(self::PRIVATE_KEY)?->data;
$publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; $publicKey = (string)app('fireflyconfig')->get(self::PUBLIC_KEY)?->data;
} catch (ContainerExceptionInterface | FireflyException | NotFoundExceptionInterface $e) { } catch (ContainerExceptionInterface|FireflyException|NotFoundExceptionInterface $e) {
app('log')->error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage())); app('log')->error(sprintf('Could not validate keysInDatabase(): %s', $e->getMessage()));
app('log')->error($e->getTraceAsString()); app('log')->error($e->getTraceAsString());
} }
@@ -98,8 +99,8 @@ class OAuthKeys
return false; return false;
} }
$private = storage_path('oauth-private.key'); $private = storage_path('oauth-private.key');
$public = storage_path('oauth-public.key'); $public = storage_path('oauth-public.key');
file_put_contents($private, $privateContent); file_put_contents($private, $privateContent);
file_put_contents($public, $publicContent); file_put_contents($public, $publicContent);

View File

@@ -145,13 +145,13 @@ class AmountFormat extends AbstractExtension
static function (string $amount, ?string $symbol = null, ?int $decimalPlaces = null, ?bool $coloured = null): string { static function (string $amount, ?string $symbol = null, ?int $decimalPlaces = null, ?bool $coloured = null): string {
if (null === $symbol) { if (null === $symbol) {
$message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true)); $message = sprintf('formatAmountBySymbol("%s", %s, %d, %s) was called without a symbol. Please browse to /flush to clear your cache.', $amount, var_export($symbol, true), $decimalPlaces, var_export($coloured, true));
Log::error($message); Log::error($message);
$currency = Amount::getPrimaryCurrency(); $currency = Amount::getPrimaryCurrency();
} }
if (null !== $symbol) { if (null !== $symbol) {
$decimalPlaces ??= 2; $decimalPlaces ??= 2;
$coloured ??= true; $coloured ??= true;
$currency = new TransactionCurrency(); $currency = new TransactionCurrency();
$currency->symbol = $symbol; $currency->symbol = $symbol;
$currency->decimal_places = $decimalPlaces; $currency->decimal_places = $decimalPlaces;

View File

@@ -37,6 +37,7 @@ use Override;
use Twig\Extension\AbstractExtension; use Twig\Extension\AbstractExtension;
use Twig\TwigFilter; use Twig\TwigFilter;
use Twig\TwigFunction; use Twig\TwigFunction;
use function Safe\parse_url; use function Safe\parse_url;
/** /**
@@ -104,7 +105,7 @@ class General extends AbstractExtension
'activeRoutePartialObjectType', 'activeRoutePartialObjectType',
static function ($context): string { static function ($context): string {
[, $route, $objectType] = func_get_args(); [, $route, $objectType] = func_get_args();
$activeObjectType = $context['objectType'] ?? false; $activeObjectType = $context['objectType'] ?? false;
if ($objectType === $activeObjectType if ($objectType === $activeObjectType
&& false !== stripos( && false !== stripos(
@@ -154,14 +155,14 @@ class General extends AbstractExtension
} }
/** @var Carbon $date */ /** @var Carbon $date */
$date = session('end', today(config('app.timezone'))->endOfMonth()); $date = session('end', today(config('app.timezone'))->endOfMonth());
Log::debug(sprintf('twig balance: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); Log::debug(sprintf('twig balance: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
$info = Steam::finalAccountBalance($account, $date); $info = Steam::finalAccountBalance($account, $date);
$currency = Steam::getAccountCurrency($account); $currency = Steam::getAccountCurrency($account);
$primary = Amount::getPrimaryCurrency(); $primary = Amount::getPrimaryCurrency();
$convertToPrimary = Amount::convertToPrimary(); $convertToPrimary = Amount::convertToPrimary();
$usePrimary = $convertToPrimary && $primary->id !== $currency->id; $usePrimary = $convertToPrimary && $primary->id !== $currency->id;
$currency ??= $primary; $currency ??= $primary;
$strings = []; $strings = [];
foreach ($info as $key => $balance) { foreach ($info as $key => $balance) {
if ('balance' === $key) { if ('balance' === $key) {
@@ -196,7 +197,7 @@ class General extends AbstractExtension
{ {
return new TwigFunction( return new TwigFunction(
'carbonize', 'carbonize',
static fn(string $date): Carbon => new Carbon($date, config('app.timezone')) static fn (string $date): Carbon => new Carbon($date, config('app.timezone'))
); );
} }
@@ -225,15 +226,15 @@ class General extends AbstractExtension
static function (int $size): string { static function (int $size): string {
// less than one GB, more than one MB // less than one GB, more than one MB
if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) { if ($size < (1024 * 1024 * 2014) && $size >= (1024 * 1024)) {
return round($size / (1024 * 1024), 2) . ' MB'; return round($size / (1024 * 1024), 2).' MB';
} }
// less than one MB // less than one MB
if ($size < (1024 * 1024)) { if ($size < (1024 * 1024)) {
return round($size / 1024, 2) . ' KB'; return round($size / 1024, 2).' KB';
} }
return $size . ' bytes'; return $size.' bytes';
} }
); );
} }
@@ -337,7 +338,7 @@ class General extends AbstractExtension
{ {
return new TwigFilter( return new TwigFilter(
'mimeIcon', 'mimeIcon',
static fn(string $string): string => match ($string) { static fn (string $string): string => match ($string) {
'application/pdf' => 'fa-file-pdf-o', 'application/pdf' => 'fa-file-pdf-o',
'image/webp', 'image/png', 'image/jpeg', 'image/svg+xml', 'image/heic', 'image/heic-sequence', 'application/vnd.oasis.opendocument.image' => 'fa-file-image-o', 'image/webp', 'image/png', 'image/jpeg', 'image/svg+xml', 'image/heic', 'image/heic-sequence', 'application/vnd.oasis.opendocument.image' => 'fa-file-image-o',
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/x-iwork-pages-sffpages', 'application/vnd.sun.xml.writer', 'application/vnd.sun.xml.writer.template', 'application/vnd.sun.xml.writer.global', 'application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.text-template', 'application/vnd.oasis.opendocument.text-web', 'application/vnd.oasis.opendocument.text-master' => 'fa-file-word-o', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'application/x-iwork-pages-sffpages', 'application/vnd.sun.xml.writer', 'application/vnd.sun.xml.writer.template', 'application/vnd.sun.xml.writer.global', 'application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global', 'application/vnd.oasis.opendocument.text', 'application/vnd.oasis.opendocument.text-template', 'application/vnd.oasis.opendocument.text-web', 'application/vnd.oasis.opendocument.text-master' => 'fa-file-word-o',
@@ -380,7 +381,7 @@ class General extends AbstractExtension
{ {
return new TwigFunction( return new TwigFunction(
'phpdate', 'phpdate',
static fn(string $str): string => date($str) static fn (string $str): string => date($str)
); );
} }
} }

View File

@@ -42,7 +42,7 @@ class Rule extends AbstractExtension
$ruleActions = array_keys(Config::get('firefly.rule-actions')); $ruleActions = array_keys(Config::get('firefly.rule-actions'));
$possibleActions = []; $possibleActions = [];
foreach ($ruleActions as $key) { foreach ($ruleActions as $key) {
$possibleActions[$key] = (string)trans('firefly.rule_action_' . $key . '_choice'); $possibleActions[$key] = (string)trans('firefly.rule_action_'.$key.'_choice');
} }
unset($ruleActions); unset($ruleActions);
asort($possibleActions); asort($possibleActions);
@@ -56,7 +56,7 @@ class Rule extends AbstractExtension
{ {
return new TwigFunction( return new TwigFunction(
'allJournalTriggers', 'allJournalTriggers',
static fn() => [ static fn () => [
'store-journal' => (string)trans('firefly.rule_trigger_store_journal'), 'store-journal' => (string)trans('firefly.rule_trigger_store_journal'),
'update-journal' => (string)trans('firefly.rule_trigger_update_journal'), 'update-journal' => (string)trans('firefly.rule_trigger_update_journal'),
'manual-activation' => (string)trans('firefly.rule_trigger_manual'), 'manual-activation' => (string)trans('firefly.rule_trigger_manual'),
@@ -73,7 +73,7 @@ class Rule extends AbstractExtension
$possibleTriggers = []; $possibleTriggers = [];
foreach ($ruleTriggers as $key) { foreach ($ruleTriggers as $key) {
if ('user_action' !== $key) { if ('user_action' !== $key) {
$possibleTriggers[$key] = (string)trans('firefly.rule_trigger_' . $key . '_choice'); $possibleTriggers[$key] = (string)trans('firefly.rule_trigger_'.$key.'_choice');
} }
} }
unset($ruleTriggers); unset($ruleTriggers);

View File

@@ -34,6 +34,7 @@ use Illuminate\Support\Facades\DB;
use Override; use Override;
use Twig\Extension\AbstractExtension; use Twig\Extension\AbstractExtension;
use Twig\TwigFunction; use Twig\TwigFunction;
use function Safe\json_decode; use function Safe\json_decode;
/** /**
@@ -82,10 +83,11 @@ class TransactionGroupTwig extends AbstractExtension
static function (int $journalId, string $metaField) { static function (int $journalId, string $metaField) {
/** @var null|TransactionJournalMeta $entry */ /** @var null|TransactionJournalMeta $entry */
$entry = DB::table('journal_meta') $entry = DB::table('journal_meta')
->where('name', $metaField) ->where('name', $metaField)
->where('transaction_journal_id', $journalId) ->where('transaction_journal_id', $journalId)
->whereNull('deleted_at') ->whereNull('deleted_at')
->first(); ->first()
;
if (null === $entry) { if (null === $entry) {
return today(config('app.timezone')); return today(config('app.timezone'));
} }
@@ -102,10 +104,11 @@ class TransactionGroupTwig extends AbstractExtension
static function (int $journalId, string $metaField) { static function (int $journalId, string $metaField) {
/** @var null|TransactionJournalMeta $entry */ /** @var null|TransactionJournalMeta $entry */
$entry = DB::table('journal_meta') $entry = DB::table('journal_meta')
->where('name', $metaField) ->where('name', $metaField)
->where('transaction_journal_id', $journalId) ->where('transaction_journal_id', $journalId)
->whereNull('deleted_at') ->whereNull('deleted_at')
->first(); ->first()
;
if (null === $entry) { if (null === $entry) {
return ''; return '';
} }
@@ -121,10 +124,11 @@ class TransactionGroupTwig extends AbstractExtension
'journalHasMeta', 'journalHasMeta',
static function (int $journalId, string $metaField) { static function (int $journalId, string $metaField) {
$count = DB::table('journal_meta') $count = DB::table('journal_meta')
->where('name', $metaField) ->where('name', $metaField)
->where('transaction_journal_id', $journalId) ->where('transaction_journal_id', $journalId)
->whereNull('deleted_at') ->whereNull('deleted_at')
->count(); ->count()
;
return 1 === $count; return 1 === $count;
} }
@@ -157,9 +161,9 @@ class TransactionGroupTwig extends AbstractExtension
*/ */
private function foreignJournalArrayAmount(array $array): string private function foreignJournalArrayAmount(array $array): string
{ {
$type = $array['transaction_type_type'] ?? TransactionTypeEnum::WITHDRAWAL->value; $type = $array['transaction_type_type'] ?? TransactionTypeEnum::WITHDRAWAL->value;
$amount = $array['foreign_amount'] ?? '0'; $amount = $array['foreign_amount'] ?? '0';
$colored = true; $colored = true;
$sourceType = $array['source_account_type'] ?? 'invalid'; $sourceType = $array['source_account_type'] ?? 'invalid';
$amount = $this->signAmount($amount, $type, $sourceType); $amount = $this->signAmount($amount, $type, $sourceType);
@@ -167,7 +171,7 @@ class TransactionGroupTwig extends AbstractExtension
if (TransactionTypeEnum::TRANSFER->value === $type) { if (TransactionTypeEnum::TRANSFER->value === $type) {
$colored = false; $colored = false;
} }
$result = app('amount')->formatFlat($array['foreign_currency_symbol'], (int)$array['foreign_currency_decimal_places'], $amount, $colored); $result = app('amount')->formatFlat($array['foreign_currency_symbol'], (int)$array['foreign_currency_decimal_places'], $amount, $colored);
if (TransactionTypeEnum::TRANSFER->value === $type) { if (TransactionTypeEnum::TRANSFER->value === $type) {
return sprintf('<span class="text-info money-transfer">%s</span>', $result); return sprintf('<span class="text-info money-transfer">%s</span>', $result);
} }
@@ -180,7 +184,7 @@ class TransactionGroupTwig extends AbstractExtension
*/ */
private function foreignJournalObjectAmount(TransactionJournal $journal): string private function foreignJournalObjectAmount(TransactionJournal $journal): string
{ {
$type = $journal->transactionType->type; $type = $journal->transactionType->type;
/** @var Transaction $first */ /** @var Transaction $first */
$first = $journal->transactions()->where('amount', '<', 0)->first(); $first = $journal->transactions()->where('amount', '<', 0)->first();
@@ -189,12 +193,12 @@ class TransactionGroupTwig extends AbstractExtension
$colored = true; $colored = true;
$sourceType = $first->account->accountType()->first()->type; $sourceType = $first->account->accountType()->first()->type;
$amount = $this->signAmount($amount, $type, $sourceType); $amount = $this->signAmount($amount, $type, $sourceType);
if (TransactionTypeEnum::TRANSFER->value === $type) { if (TransactionTypeEnum::TRANSFER->value === $type) {
$colored = false; $colored = false;
} }
$result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored);
if (TransactionTypeEnum::TRANSFER->value === $type) { if (TransactionTypeEnum::TRANSFER->value === $type) {
return sprintf('<span class="text-info money-transfer">%s</span>', $result); return sprintf('<span class="text-info money-transfer">%s</span>', $result);
} }
@@ -225,7 +229,7 @@ class TransactionGroupTwig extends AbstractExtension
$colored = false; $colored = false;
} }
$result = app('amount')->formatFlat($array['currency_symbol'], (int)$array['currency_decimal_places'], $amount, $colored); $result = app('amount')->formatFlat($array['currency_symbol'], (int)$array['currency_decimal_places'], $amount, $colored);
if (TransactionTypeEnum::TRANSFER->value === $type) { if (TransactionTypeEnum::TRANSFER->value === $type) {
return sprintf('<span class="text-info money-transfer">%s</span>', $result); return sprintf('<span class="text-info money-transfer">%s</span>', $result);
} }
@@ -238,7 +242,7 @@ class TransactionGroupTwig extends AbstractExtension
*/ */
private function normalJournalObjectAmount(TransactionJournal $journal): string private function normalJournalObjectAmount(TransactionJournal $journal): string
{ {
$type = $journal->transactionType->type; $type = $journal->transactionType->type;
/** @var Transaction $first */ /** @var Transaction $first */
$first = $journal->transactions()->where('amount', '<', 0)->first(); $first = $journal->transactions()->where('amount', '<', 0)->first();
@@ -247,12 +251,12 @@ class TransactionGroupTwig extends AbstractExtension
$colored = true; $colored = true;
$sourceType = $first->account->accountType()->first()->type; $sourceType = $first->account->accountType()->first()->type;
$amount = $this->signAmount($amount, $type, $sourceType); $amount = $this->signAmount($amount, $type, $sourceType);
if (TransactionTypeEnum::TRANSFER->value === $type) { if (TransactionTypeEnum::TRANSFER->value === $type) {
$colored = false; $colored = false;
} }
$result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored); $result = app('amount')->formatFlat($currency->symbol, $currency->decimal_places, $amount, $colored);
if (TransactionTypeEnum::TRANSFER->value === $type) { if (TransactionTypeEnum::TRANSFER->value === $type) {
return sprintf('<span class="text-info money-transfer">%s</span>', $result); return sprintf('<span class="text-info money-transfer">%s</span>', $result);
} }

View File

@@ -39,7 +39,7 @@ class Translation extends AbstractExtension
return [ return [
new TwigFilter( new TwigFilter(
'_', '_',
static fn($name) => (string)trans(sprintf('firefly.%s', $name)), static fn ($name) => (string)trans(sprintf('firefly.%s', $name)),
['is_safe' => ['html']] ['is_safe' => ['html']]
), ),
]; ];

Some files were not shown because too many files have changed in this diff Show More