Clean up balance methods.

This commit is contained in:
James Cole
2024-12-22 20:32:58 +01:00
parent a0e92b6969
commit d90ac519f7
93 changed files with 1233 additions and 1853 deletions

View File

@@ -116,13 +116,13 @@ class AccountController extends Controller
]; ];
// TODO this code is also present in the V2 chart account controller so this method is due to be deprecated. // TODO this code is also present in the V2 chart account controller so this method is due to be deprecated.
$currentStart = clone $start; $currentStart = clone $start;
$range = app('steam')->balanceInRange($account, $start, clone $end); $range = app('steam')->finalAccountBalanceInRange($account, $start, clone $end);
// 2022-10-11 this method no longer converts to float. // 2022-10-11 this method no longer converts to float.
$previous = array_values($range)[0]; $previous = array_values($range)[0];
while ($currentStart <= $end) { while ($currentStart <= $end) {
$format = $currentStart->format('Y-m-d'); $format = $currentStart->format('Y-m-d');
$label = $currentStart->toAtomString(); $label = $currentStart->toAtomString();
$balance = array_key_exists($format, $range) ? $range[$format] : $previous; $balance = array_key_exists($format, $range) ? $range[$format]['balance'] : $previous;
$previous = $balance; $previous = $balance;
$currentStart->addDay(); $currentStart->addDay();
$currentSet['entries'][$label] = $balance; $currentSet['entries'][$label] = $balance;

View File

@@ -56,8 +56,8 @@ class AccountController extends Controller
function ($request, $next) { function ($request, $next) {
$this->repository = app(AccountRepositoryInterface::class); $this->repository = app(AccountRepositoryInterface::class);
$this->repository->setUserGroup($this->validateUserGroup($request)); $this->repository->setUserGroup($this->validateUserGroup($request));
$this->chartData = new ChartData(); $this->chartData = new ChartData();
$this->default = app('amount')->getDefaultCurrency(); $this->default = app('amount')->getDefaultCurrency();
return $next($request); return $next($request);
} }
@@ -92,11 +92,11 @@ class AccountController extends Controller
*/ */
private function renderAccountData(array $params, Account $account): void private function renderAccountData(array $params, Account $account): void
{ {
$currency = $this->repository->getAccountCurrency($account); $currency = $this->repository->getAccountCurrency($account);
if (null === $currency) { if (null === $currency) {
$currency = $this->default; $currency = $this->default;
} }
$currentSet = [ $currentSet = [
'label' => $account->name, 'label' => $account->name,
// the currency that belongs to the account. // the currency that belongs to the account.
@@ -117,23 +117,22 @@ class AccountController extends Controller
'entries' => [], 'entries' => [],
'native_entries' => [], 'native_entries' => [],
]; ];
$currentStart = clone $params['start']; $currentStart = clone $params['start'];
$range = app('steam')->balanceInRange($account, $params['start'], clone $params['end'], $currency); $range = app('steam')->finalAccountBalanceInRange($account, $params['start'], clone $params['end'], $currency);
$rangeConverted = app('steam')->balanceInRangeConverted($account, $params['start'], clone $params['end'], $this->default);
$previous = array_values($range)[0]; $previous = array_values($range)[0]['balance'];
$previousConverted = array_values($rangeConverted)[0]; $previousNative = array_values($range)[0]['native_balance'];
while ($currentStart <= $params['end']) { while ($currentStart <= $params['end']) {
$format = $currentStart->format('Y-m-d'); $format = $currentStart->format('Y-m-d');
$label = $currentStart->toAtomString(); $label = $currentStart->toAtomString();
$balance = array_key_exists($format, $range) ? $range[$format] : $previous; $balance = array_key_exists($format, $range) ? $range[$format]['balance'] : $previous;
$balanceConverted = array_key_exists($format, $rangeConverted) ? $rangeConverted[$format] : $previousConverted; $balanceNative = array_key_exists($format, $range) ? $range[$format]['balance_native'] : $previousNative;
$previous = $balance; $previous = $balance;
$previousConverted = $balanceConverted; $previousNative = $balanceNative;
$currentStart->addDay(); $currentStart->addDay();
$currentSet['entries'][$label] = $balance; $currentSet['entries'][$label] = $balance;
$currentSet['native_entries'][$label] = $balanceConverted; $currentSet['native_entries'][$label] = $balanceNative;
} }
$this->chartData->add($currentSet); $this->chartData->add($currentSet);
} }

View File

@@ -95,7 +95,7 @@ class NetWorth implements NetWorthInterface
'native_currency_decimal_places' => $default->decimal_places, 'native_currency_decimal_places' => $default->decimal_places,
], ],
]; ];
$balances = app('steam')->balancesByAccountsConverted($accounts, $date); $balances = app('steam')->finalAccountsBalance($accounts, $date);
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
@@ -187,10 +187,10 @@ class NetWorth implements NetWorthInterface
*/ */
$accounts = $this->getAccounts(); $accounts = $this->getAccounts();
$return = []; $return = [];
$balances = app('steam')->balancesByAccounts($accounts, $date); $balances = app('steam')->finalAccountsBalance($accounts, $date);
foreach ($accounts as $account) { foreach ($accounts as $account) {
$currency = $this->getRepository()->getAccountCurrency($account); $currency = $this->getRepository()->getAccountCurrency($account);
$balance = $balances[$account->id] ?? '0'; $balance = $balances[$account->id]['balance'] ?? '0';
// always subtract virtual balance. // always subtract virtual balance.
$virtualBalance = $account->virtual_balance; $virtualBalance = $account->virtual_balance;

View File

@@ -89,8 +89,8 @@ class IndexController extends Controller
$start->subDay(); $start->subDay();
$ids = $accounts->pluck('id')->toArray(); $ids = $accounts->pluck('id')->toArray();
$startBalances = app('steam')->balancesByAccounts($accounts, $start); $startBalances = app('steam')->finalAccountsBalance($accounts, $start);
$endBalances = app('steam')->balancesByAccounts($accounts, $end); $endBalances = app('steam')->finalAccountsBalance($accounts, $end);
$activities = app('steam')->getLastActivities($ids); $activities = app('steam')->getLastActivities($ids);
$accounts->each( $accounts->each(
@@ -149,8 +149,8 @@ class IndexController extends Controller
$start->subDay(); $start->subDay();
$ids = $accounts->pluck('id')->toArray(); $ids = $accounts->pluck('id')->toArray();
$startBalances = app('steam')->balancesByAccounts($accounts, $start); $startBalances = app('steam')->finalAccountsBalance($accounts, $start);
$endBalances = app('steam')->balancesByAccounts($accounts, $end); $endBalances = app('steam')->finalAccountsBalance($accounts, $end);
$activities = app('steam')->getLastActivities($ids); $activities = app('steam')->getLastActivities($ids);
$accounts->each( $accounts->each(

View File

@@ -106,28 +106,27 @@ class AccountController extends Controller
$accountNames = $this->extractNames($accounts); $accountNames = $this->extractNames($accounts);
// grab all balances // grab all balances
$startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start); $startBalances = app('steam')->finalAccountsBalance($accounts, $start);
$endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end); $endBalances = app('steam')->finalAccountsBalance($accounts, $end);
// loop the end balances. This is an array for each account ($expenses) // loop the end balances. This is an array for each account ($expenses)
foreach ($endBalances as $accountId => $expenses) { foreach ($endBalances as $accountId => $expenses) {
$accountId = (int) $accountId; $accountId = (int) $accountId;
// loop each expense entry (each entry can be a different currency). // loop each expense entry (each entry can be a different currency).
foreach ($expenses as $currencyId => $endAmount) { foreach ($expenses as $currencyCode => $endAmount) {
$currencyId = (int) $currencyId;
// see if there is an accompanying start amount. // see if there is an accompanying start amount.
// grab the difference and find the currency. // grab the difference and find the currency.
$startAmount = (string) ($startBalances[$accountId][$currencyId] ?? '0'); $startAmount = (string) ($startBalances[$accountId][$currencyCode] ?? '0');
$diff = bcsub((string) $endAmount, $startAmount); $diff = bcsub((string) $endAmount, $startAmount);
$currencies[$currencyId] ??= $this->currencyRepository->find($currencyId); $currencies[$currencyCode] ??= $this->currencyRepository->findByCode($currencyCode);
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array. // store the values in a temporary array.
$tempData[] = [ $tempData[] = [
'name' => $accountNames[$accountId], 'name' => $accountNames[$accountId],
'difference' => $diff, 'difference' => $diff,
'diff_float' => (float) $diff, // intentional float 'diff_float' => (float) $diff, // intentional float
'currency_id' => $currencyId, 'currency_id' => $currencies[$currencyCode]->id,
]; ];
} }
} }
@@ -437,11 +436,11 @@ class AccountController extends Controller
if ('1D' === $step) { if ('1D' === $step) {
// per day the entire period, balance for every day. // per day the entire period, balance for every day.
$format = (string) trans('config.month_and_day_js', [], $locale); $format = (string) trans('config.month_and_day_js', [], $locale);
$range = app('steam')->balanceInRange($account, $start, $end, $currency); $range = app('steam')->finalAccountBalanceInRange($account, $start, $end);
$previous = array_values($range)[0]; $previous = array_values($range)[0];
while ($end >= $current) { while ($end >= $current) {
$theDate = $current->format('Y-m-d'); $theDate = $current->format('Y-m-d');
$balance = $range[$theDate] ?? $previous; $balance = $range[$theDate]['balance'] ?? $previous;
$label = $current->isoFormat($format); $label = $current->isoFormat($format);
$entries[$label] = (float) $balance; $entries[$label] = (float) $balance;
$previous = $balance; $previous = $balance;
@@ -507,28 +506,27 @@ class AccountController extends Controller
$accountNames = $this->extractNames($accounts); $accountNames = $this->extractNames($accounts);
// grab all balances // grab all balances
$startBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $start); $startBalances = app('steam')->finalAccountsBalance($accounts, $start);
$endBalances = app('steam')->balancesPerCurrencyByAccounts($accounts, $end); $endBalances = app('steam')->finalAccountsBalance($accounts, $end);
// loop the end balances. This is an array for each account ($expenses) // loop the end balances. This is an array for each account ($expenses)
foreach ($endBalances as $accountId => $expenses) { foreach ($endBalances as $accountId => $expenses) {
$accountId = (int) $accountId; $accountId = (int) $accountId;
// loop each expense entry (each entry can be a different currency). // loop each expense entry (each entry can be a different currency).
foreach ($expenses as $currencyId => $endAmount) { foreach ($expenses as $currencyCode => $endAmount) {
$currencyId = (int) $currencyId;
// see if there is an accompanying start amount. // see if there is an accompanying start amount.
// grab the difference and find the currency. // grab the difference and find the currency.
$startAmount = (string) ($startBalances[$accountId][$currencyId] ?? '0'); $startAmount = (string) ($startBalances[$accountId][$currencyCode] ?? '0');
$diff = bcsub((string) $endAmount, $startAmount); $diff = bcsub((string) $endAmount, $startAmount);
$currencies[$currencyId] ??= $this->currencyRepository->find($currencyId); $currencies[$currencyCode] ??= $this->currencyRepository->findByCode($currencyCode);
if (0 !== bccomp($diff, '0')) { if (0 !== bccomp($diff, '0')) {
// store the values in a temporary array. // store the values in a temporary array.
$tempData[] = [ $tempData[] = [
'name' => $accountNames[$accountId], 'name' => $accountNames[$accountId],
'difference' => $diff, 'difference' => $diff,
'diff_float' => (float) $diff, // intentional float 'diff_float' => (float) $diff, // intentional float
'currency_id' => $currencyId, 'currency_id' => $currencies[$currencyCode]->id,
]; ];
} }
} }

View File

@@ -47,8 +47,8 @@ class AccountTasker implements AccountTaskerInterface
{ {
$yesterday = clone $start; $yesterday = clone $start;
$yesterday->subDay(); $yesterday->subDay();
$startSet = app('steam')->balancesByAccounts($accounts, $yesterday); $startSet = app('steam')->finalAccountsBalance($accounts, $yesterday);
$endSet = app('steam')->balancesByAccounts($accounts, $end); $endSet = app('steam')->finalAccountsBalance($accounts, $end);
app('log')->debug('Start of accountreport'); app('log')->debug('Start of accountreport');
/** @var AccountRepositoryInterface $repository */ /** @var AccountRepositoryInterface $repository */
@@ -86,8 +86,8 @@ class AccountTasker implements AccountTaskerInterface
// get first journal date: // get first journal date:
$first = $repository->oldestJournal($account); $first = $repository->oldestJournal($account);
$entry['start_balance'] = $startSet[$account->id] ?? '0'; $entry['start_balance'] = $startSet[$account->id]['balance'] ?? '0';
$entry['end_balance'] = $endSet[$account->id] ?? '0'; $entry['end_balance'] = $endSet[$account->id]['balance'] ?? '0';
// first journal exists, and is on start, then this is the actual opening balance: // first journal exists, and is on start, then this is the actual opening balance:
if (null !== $first && $first->date->isSameDay($start) && TransactionType::OPENING_BALANCE === $first->transactionType->type) { if (null !== $first && $first->date->isSameDay($start) && TransactionType::OPENING_BALANCE === $first->transactionType->type) {

View File

@@ -28,6 +28,7 @@ use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\UserGroup; use FireflyIII\Models\UserGroup;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use NumberFormatter;
/** /**
* Class Amount. * Class Amount.
@@ -55,15 +56,15 @@ class Amount
*/ */
public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string public function formatFlat(string $symbol, int $decimalPlaces, string $amount, ?bool $coloured = null): string
{ {
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
$rounded = app('steam')->bcround($amount, $decimalPlaces); $rounded = app('steam')->bcround($amount, $decimalPlaces);
$coloured ??= true; $coloured ??= true;
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY);
$fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $symbol); $fmt->setSymbol(NumberFormatter::CURRENCY_SYMBOL, $symbol);
$fmt->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces); $fmt->setAttribute(NumberFormatter::MIN_FRACTION_DIGITS, $decimalPlaces);
$fmt->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces); $fmt->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $decimalPlaces);
$result = (string) $fmt->format((float) $rounded); // intentional float $result = (string) $fmt->format((float) $rounded); // intentional float
if (true === $coloured) { if (true === $coloured) {
if (1 === bccomp($rounded, '0')) { if (1 === bccomp($rounded, '0')) {
@@ -109,7 +110,7 @@ class Amount
public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency public function getDefaultCurrencyByUserGroup(UserGroup $userGroup): TransactionCurrency
{ {
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty('getDefaultCurrencyByGroup'); $cache->addProperty('getDefaultCurrencyByGroup');
$cache->addProperty($userGroup->id); $cache->addProperty($userGroup->id);
if ($cache->has()) { if ($cache->has()) {
@@ -172,23 +173,23 @@ class Amount
private function getLocaleInfo(): array private function getLocaleInfo(): array
{ {
// get config from preference, not from translation: // get config from preference, not from translation:
$locale = app('steam')->getLocale(); $locale = app('steam')->getLocale();
$array = app('steam')->getLocaleArray($locale); $array = app('steam')->getLocaleArray($locale);
setlocale(LC_MONETARY, $array); setlocale(LC_MONETARY, $array);
$info = localeconv(); $info = localeconv();
// correct variables // correct variables
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes'); $info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes');
$info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes'); $info['p_cs_precedes'] = $this->getLocaleField($info, 'p_cs_precedes');
$info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space'); $info['n_sep_by_space'] = $this->getLocaleField($info, 'n_sep_by_space');
$info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space'); $info['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space');
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY); $fmt = new NumberFormatter($locale, NumberFormatter::CURRENCY);
$info['mon_decimal_point'] = $fmt->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL); $info['mon_decimal_point'] = $fmt->getSymbol(NumberFormatter::MONETARY_SEPARATOR_SYMBOL);
$info['mon_thousands_sep'] = $fmt->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL); $info['mon_thousands_sep'] = $fmt->getSymbol(NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL);
return $info; return $info;
} }
@@ -208,7 +209,7 @@ class Amount
public static function getAmountJsConfig(bool $sepBySpace, int $signPosn, string $sign, bool $csPrecedes): string public static function getAmountJsConfig(bool $sepBySpace, int $signPosn, string $sign, bool $csPrecedes): string
{ {
// negative first: // negative first:
$space = ' '; $space = ' ';
// require space between symbol and amount? // require space between symbol and amount?
if (false === $sepBySpace) { if (false === $sepBySpace) {
@@ -217,11 +218,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
@@ -263,11 +264,11 @@ class Amount
} }
// default is amount before currency // default is amount before currency
$format = $posA.$posD.'%v'.$space.$posB.'%s'.$posC.$posE; $format = $posA . $posD . '%v' . $space . $posB . '%s' . $posC . $posE;
if ($csPrecedes) { if ($csPrecedes) {
// alternative is currency before amount // alternative is currency before amount
$format = $posA.$posB.'%s'.$posC.$space.$posD.'%v'.$posE; $format = $posA . $posB . '%s' . $posC . $space . $posD . '%v' . $posE;
} }
return $format; return $format;

View File

@@ -47,7 +47,7 @@ class RemoteUserGuard implements Guard
public function __construct(UserProvider $provider, Application $app) public function __construct(UserProvider $provider, Application $app)
{ {
/** @var null|Request $request */ /** @var null|Request $request */
$request = $app->get('request'); $request = $app->get('request');
app('log')->debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri())); app('log')->debug(sprintf('Created RemoteUserGuard for %s "%s"', $request?->getMethod(), $request?->getRequestUri()));
$this->application = $app; $this->application = $app;
$this->provider = $provider; $this->provider = $provider;
@@ -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')) {
app('log')->debug('Use apache_request_headers to find user ID.'); app('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
} }
app('log')->debug(sprintf('Result of getting user from provider: %s', $retrievedUser->email)); app('log')->debug(sprintf('Result of getting user from provider: %s', $retrievedUser->email));
$this->user = $retrievedUser; $this->user = $retrievedUser;
} }
public function guest(): bool public function guest(): bool
@@ -139,14 +139,14 @@ class RemoteUserGuard implements Guard
/** /**
* @SuppressWarnings(PHPMD.ShortMethodName) * @SuppressWarnings(PHPMD.ShortMethodName)
*/ */
public function id(): null|int|string public function id(): null | int | string
{ {
app('log')->debug(sprintf('Now at %s', __METHOD__)); app('log')->debug(sprintf('Now at %s', __METHOD__));
return $this->user?->id; return $this->user?->id;
} }
public function setUser(null|Authenticatable|User $user): void // @phpstan-ignore-line public function setUser(null | Authenticatable | User $user): void // @phpstan-ignore-line
{ {
app('log')->debug(sprintf('Now at %s', __METHOD__)); app('log')->debug(sprintf('Now at %s', __METHOD__));
if ($user instanceof User) { if ($user instanceof User) {

View File

@@ -30,13 +30,15 @@ use FireflyIII\Models\Role;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable; use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Auth\UserProvider;
use Override;
use Str;
/** /**
* Class RemoteUserProvider * Class RemoteUserProvider
*/ */
class RemoteUserProvider implements UserProvider class RemoteUserProvider implements UserProvider
{ {
#[\Override] #[Override]
public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false): void public function rehashPasswordIfRequired(Authenticatable $user, array $credentials, bool $force = false): void
{ {
app('log')->debug(sprintf('Now at %s', __METHOD__)); app('log')->debug(sprintf('Now at %s', __METHOD__));
@@ -72,7 +74,7 @@ class RemoteUserProvider implements UserProvider
'blocked' => false, 'blocked' => false,
'blocked_code' => null, 'blocked_code' => null,
'email' => $identifier, 'email' => $identifier,
'password' => bcrypt(\Str::random(64)), 'password' => bcrypt(Str::random(64)),
] ]
); );
// if this is the first user, give them admin as well. // if this is the first user, give them admin as well.

View File

@@ -48,19 +48,18 @@ 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] ??= TransactionCurrency::find($currencyId); $currencies[$currencyId] ??= TransactionCurrency::find($currencyId);
$return[$accountId] ??= []; $return[$accountId] ??= [];
if (array_key_exists($currencyId, $return[$accountId])) { if (array_key_exists($currencyId, $return[$accountId])) {

View File

@@ -43,23 +43,21 @@ 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', [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]) ->whereIn('account_types.type', [AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE])
->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,13 +41,12 @@ 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.');
@@ -57,10 +56,9 @@ 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,21 +41,19 @@ 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([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::RECONCILIATION]); $collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::RECONCILIATION]);
$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,11 +43,10 @@ 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
@@ -59,7 +58,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,9 +42,8 @@ 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,9 +42,8 @@ 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,9 +39,8 @@ 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,9 +39,8 @@ 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

@@ -23,7 +23,9 @@ declare(strict_types=1);
namespace FireflyIII\Support; namespace FireflyIII\Support;
use Cache;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use JsonException;
/** /**
* Class CacheProperties. * Class CacheProperties.
@@ -55,7 +57,7 @@ class CacheProperties
*/ */
public function get() public function get()
{ {
return \Cache::get($this->hash); return Cache::get($this->hash);
} }
public function getHash(): string public function getHash(): string
@@ -70,16 +72,16 @@ class CacheProperties
} }
$this->hash(); $this->hash();
return \Cache::has($this->hash); return Cache::has($this->hash);
} }
private function hash(): void private function hash(): void
{ {
$content = ''; $content = '';
foreach ($this->properties as $property) { foreach ($this->properties as $property) {
try { try {
$content .= json_encode($property, JSON_THROW_ON_ERROR); $content .= json_encode($property, JSON_THROW_ON_ERROR);
} catch (\JsonException $e) { } catch (JsonException $e) {
// @ignoreException // @ignoreException
$content .= hash('sha256', (string) time()); $content .= hash('sha256', (string) time());
} }
@@ -92,6 +94,6 @@ class CacheProperties
*/ */
public function store($data): void public function store($data): void
{ {
\Cache::forever($this->hash, $data); Cache::forever($this->hash, $data);
} }
} }

View File

@@ -26,15 +26,16 @@ namespace FireflyIII\Support\Calendar;
use Carbon\Carbon; use Carbon\Carbon;
use FireflyIII\Exceptions\IntervalException; use FireflyIII\Exceptions\IntervalException;
use SplObjectStorage;
/** /**
* Class Calculator * Class Calculator
*/ */
class Calculator class Calculator
{ {
public const int DEFAULT_INTERVAL = 1; public const int DEFAULT_INTERVAL = 1;
private static ?\SplObjectStorage $intervalMap = null; private static ?SplObjectStorage $intervalMap = null;
private static array $intervals = []; private static array $intervals = [];
/** /**
* @throws IntervalException * @throws IntervalException
@@ -65,14 +66,14 @@ class Calculator
/** /**
* @SuppressWarnings(PHPMD.MissingImport) * @SuppressWarnings(PHPMD.MissingImport)
*/ */
private static function loadIntervalMap(): \SplObjectStorage private static function loadIntervalMap(): SplObjectStorage
{ {
if (null !== self::$intervalMap) { if (null !== self::$intervalMap) {
return self::$intervalMap; return self::$intervalMap;
} }
self::$intervalMap = new \SplObjectStorage(); self::$intervalMap = new SplObjectStorage();
foreach (Periodicity::cases() as $interval) { foreach (Periodicity::cases() as $interval) {
$periodicityClass = __NAMESPACE__."\\Periodicity\\{$interval->name}"; $periodicityClass = __NAMESPACE__ . "\\Periodicity\\{$interval->name}";
self::$intervals[] = $interval->name; self::$intervals[] = $interval->name;
self::$intervalMap->attach($interval, new $periodicityClass()); self::$intervalMap->attach($interval, new $periodicityClass());
} }

View File

@@ -155,7 +155,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']);
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(
'%s (%s) (%s - %s)', '%s (%s) (%s - %s)',
@@ -165,7 +165,7 @@ class FrontpageChartGenerator
$limit->end_date->isoFormat($this->monthAndDayFormat) $limit->end_date->isoFormat($this->monthAndDayFormat)
); );
} }
$sumSpent = bcmul($entry['sum'], '-1'); // spent $sumSpent = bcmul($entry['sum'], '-1'); // spent
$data[0]['entries'][$title] = 1 === bccomp($sumSpent, $limit->amount) ? $limit->amount : $sumSpent; // spent $data[0]['entries'][$title] = 1 === bccomp($sumSpent, $limit->amount) ? $limit->amount : $sumSpent; // spent
$data[1]['entries'][$title] = 1 === bccomp($limit->amount, $sumSpent) ? bcadd($entry['sum'], $limit->amount) : '0'; // left to spent $data[1]['entries'][$title] = 1 === bccomp($limit->amount, $sumSpent) ? bcadd($entry['sum'], $limit->amount) : '0'; // left to spent

View File

@@ -65,13 +65,13 @@ class FrontpageChartGenerator
public function generate(): array public function generate(): array
{ {
$categories = $this->repository->getCategories(); $categories = $this->repository->getCategories();
$accounts = $this->accountRepos->getAccountsByType( $accounts = $this->accountRepos->getAccountsByType(
[AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::ASSET, AccountType::DEFAULT] [AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::ASSET, AccountType::DEFAULT]
); );
// get expenses + income per category: // get expenses + income per category:
$collection = []; $collection = [];
/** @var Category $category */ /** @var Category $category */
foreach ($categories as $category) { foreach ($categories as $category) {
@@ -82,10 +82,10 @@ class FrontpageChartGenerator
// collect for no-category: // collect for no-category:
$collection[] = $this->collectNoCatExpenses($accounts); $collection[] = $this->collectNoCatExpenses($accounts);
$tempData = array_merge(...$collection); $tempData = array_merge(...$collection);
// sort temp array by amount. // sort temp array by amount.
$amounts = array_column($tempData, 'sum_float'); $amounts = array_column($tempData, 'sum_float');
array_multisort($amounts, SORT_ASC, $tempData); array_multisort($amounts, SORT_ASC, $tempData);
$currencyData = $this->createCurrencyGroups($tempData); $currencyData = $this->createCurrencyGroups($tempData);

View File

@@ -38,22 +38,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([$category]); $collection = new Collection([$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 = [AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]; $types = [AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE];
$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');
@@ -63,14 +63,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',
@@ -85,11 +85,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

@@ -46,7 +46,7 @@ class ChartData
if (array_key_exists('native_currency_id', $data)) { if (array_key_exists('native_currency_id', $data)) {
$data['native_currency_id'] = (string) $data['native_currency_id']; $data['native_currency_id'] = (string) $data['native_currency_id'];
} }
$required = ['start', 'date', 'end', 'entries', 'native_entries']; $required = ['start', 'date', 'end', 'entries', 'native_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

@@ -68,7 +68,7 @@ class AutoBudgetCronjob extends AbstractCronjob
app('log')->info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d'))); app('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

@@ -80,7 +80,7 @@ class BillWarningCronjob extends AbstractCronjob
app('log')->info(sprintf('Will now fire bill warning job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); app('log')->info(sprintf('Will now fire bill warning 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

@@ -69,7 +69,7 @@ class ExchangeRatesCronjob extends AbstractCronjob
app('log')->info(sprintf('Will now fire exchange rates cron job task for date "%s".', $this->date->format('Y-m-d'))); app('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

@@ -78,7 +78,7 @@ class RecurringCronjob extends AbstractCronjob
{ {
app('log')->info(sprintf('Will now fire recurring cron job task for date "%s".', $this->date->format('Y-m-d H:i:s'))); app('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

@@ -27,6 +27,7 @@ use Eloquent;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Form\FormSupport; use FireflyIII\Support\Form\FormSupport;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Throwable;
/** /**
* Class ExpandedForm. * Class ExpandedForm.
@@ -42,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);
@@ -56,7 +57,7 @@ class ExpandedForm
// } // }
try { try {
$html = view('form.amount-no-currency', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.amount-no-currency', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Could not render amountNoCurrency(): %s', $e->getMessage())); app('log')->error(sprintf('Could not render amountNoCurrency(): %s', $e->getMessage()));
$html = 'Could not render amountNoCurrency.'; $html = 'Could not render amountNoCurrency.';
@@ -73,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')) {
@@ -82,16 +83,16 @@ 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']);
try { try {
$html = view('form.checkbox', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.checkbox', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render checkbox(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render checkbox(): %s', $e->getMessage()));
$html = 'Could not render checkbox.'; $html = 'Could not render checkbox.';
@@ -116,7 +117,7 @@ class ExpandedForm
try { try {
$html = view('form.date', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.date', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render date(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render date(): %s', $e->getMessage()));
$html = 'Could not render date.'; $html = 'Could not render date.';
@@ -138,7 +139,7 @@ class ExpandedForm
try { try {
$html = view('form.file', compact('classes', 'name', 'label', 'options'))->render(); $html = view('form.file', compact('classes', 'name', 'label', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render file(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render file(): %s', $e->getMessage()));
$html = 'Could not render file.'; $html = 'Could not render file.';
@@ -156,15 +157,15 @@ 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 {
$html = view('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render integer(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render integer(): %s', $e->getMessage()));
$html = 'Could not render integer.'; $html = 'Could not render integer.';
@@ -189,7 +190,7 @@ class ExpandedForm
try { try {
$html = view('form.location', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.location', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render location(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render location(): %s', $e->getMessage()));
$html = 'Could not render location.'; $html = 'Could not render location.';
@@ -205,12 +206,12 @@ class ExpandedForm
$selectList[0] = '(none)'; $selectList[0] = '(none)';
$fields = ['title', 'name', 'description']; $fields = ['title', 'name', 'description'];
/** @var \Eloquent $entry */ /** @var Eloquent $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
// All Eloquent models have an ID // All Eloquent models have an ID
$entryId = $entry->id; // @phpstan-ignore-line $entryId = $entry->id; // @phpstan-ignore-line
$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];
@@ -242,7 +243,7 @@ class ExpandedForm
try { try {
$html = view('form.object_group', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.object_group', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render objectGroup(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render objectGroup(): %s', $e->getMessage()));
$html = 'Could not render objectGroup.'; $html = 'Could not render objectGroup.';
@@ -259,7 +260,7 @@ class ExpandedForm
{ {
try { try {
$html = view('form.options', compact('type', 'name'))->render(); $html = view('form.options', compact('type', 'name'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage()));
$html = 'Could not render optionsList.'; $html = 'Could not render optionsList.';
@@ -280,7 +281,7 @@ class ExpandedForm
try { try {
$html = view('form.password', compact('classes', 'name', 'label', 'options'))->render(); $html = view('form.password', compact('classes', 'name', 'label', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render password(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render password(): %s', $e->getMessage()));
$html = 'Could not render password.'; $html = 'Could not render password.';
@@ -301,7 +302,7 @@ class ExpandedForm
try { try {
$html = view('form.password', compact('classes', 'value', 'name', 'label', 'options'))->render(); $html = view('form.password', compact('classes', 'value', 'name', 'label', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render passwordWithValue(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render passwordWithValue(): %s', $e->getMessage()));
$html = 'Could not render passwordWithValue.'; $html = 'Could not render passwordWithValue.';
@@ -329,7 +330,7 @@ class ExpandedForm
try { try {
$html = view('form.percentage', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.percentage', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render percentage(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render percentage(): %s', $e->getMessage()));
$html = 'Could not render percentage.'; $html = 'Could not render percentage.';
@@ -352,7 +353,7 @@ class ExpandedForm
try { try {
$html = view('form.static', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.static', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render staticText(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render staticText(): %s', $e->getMessage()));
$html = 'Could not render staticText.'; $html = 'Could not render staticText.';
@@ -376,7 +377,7 @@ class ExpandedForm
try { try {
$html = view('form.text', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.text', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render text(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render text(): %s', $e->getMessage()));
$html = 'Could not render text.'; $html = 'Could not render text.';
@@ -405,7 +406,7 @@ class ExpandedForm
try { try {
$html = view('form.textarea', compact('classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.textarea', compact('classes', 'name', 'label', 'value', 'options'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render textarea(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render textarea(): %s', $e->getMessage()));
$html = 'Could not render textarea.'; $html = 'Could not render textarea.';

View File

@@ -82,8 +82,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;
@@ -141,7 +141,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',
@@ -162,7 +162,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 = [];
@@ -192,7 +192,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 {
@@ -230,8 +230,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',
@@ -245,7 +245,7 @@ class ExportDataGenerator
'skip', 'skip',
'active', 'active',
]; ];
$records = []; $records = [];
/** @var Bill $bill */ /** @var Bill $bill */
foreach ($bills as $bill) { foreach ($bills as $bill) {
@@ -266,7 +266,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 {
@@ -296,7 +296,7 @@ class ExportDataGenerator
*/ */
private function exportBudgets(): string private function exportBudgets(): string
{ {
$header = [ $header = [
'user_id', 'user_id',
'budget_id', 'budget_id',
'name', 'name',
@@ -310,9 +310,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) {
@@ -335,7 +335,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 {
@@ -365,10 +365,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 = [];
@@ -386,7 +386,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 {
@@ -417,14 +417,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',
@@ -440,8 +440,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) {
@@ -466,7 +466,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 {
@@ -499,7 +499,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',
@@ -508,8 +508,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->getAll(); $recurrences = $recurringRepos->getAll();
/** @var Recurrence $recurrence */ /** @var Recurrence $recurrence */
foreach ($recurrences as $recurrence) { foreach ($recurrences as $recurrence) {
@@ -542,7 +542,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,
@@ -558,7 +558,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 {
@@ -595,8 +595,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) {
@@ -635,7 +635,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 {
@@ -665,12 +665,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) {
@@ -689,7 +689,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 {
@@ -728,27 +728,26 @@ class ExportDataGenerator
private function exportTransactions(): string private function exportTransactions(): string
{ {
// TODO better place for keys? // TODO better place for keys?
$header = ['user_id', 'group_id', 'journal_id', 'created_at', 'updated_at', 'group_title', 'type', 'amount', 'foreign_amount', 'currency_code', 'foreign_currency_code', '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', 'amount', 'foreign_amount', 'currency_code', 'foreign_currency_code', '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);
$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() $collector->setRange($this->start, $this->end)->withAccountInformation()->withCategoryInformation()->withBillInformation()
->withBudgetInformation()->withTagInformation()->withNotes() ->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) {
@@ -773,7 +772,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

@@ -23,6 +23,8 @@ declare(strict_types=1);
namespace FireflyIII\Support; namespace FireflyIII\Support;
use Cache;
use Exception;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Configuration; use FireflyIII\Models\Configuration;
use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\DecryptException;
@@ -37,9 +39,9 @@ class FireflyConfig
{ {
public function delete(string $name): void public function delete(string $name): void
{ {
$fullName = 'ff-config-'.$name; $fullName = 'ff-config-' . $name;
if (\Cache::has($fullName)) { if (Cache::has($fullName)) {
\Cache::forget($fullName); Cache::forget($fullName);
} }
Configuration::where('name', $name)->forceDelete(); Configuration::where('name', $name)->forceDelete();
} }
@@ -79,20 +81,20 @@ class FireflyConfig
*/ */
public function get(string $name, $default = null): ?Configuration public function get(string $name, $default = null): ?Configuration
{ {
$fullName = 'ff-config-'.$name; $fullName = 'ff-config-' . $name;
if (\Cache::has($fullName)) { if (Cache::has($fullName)) {
return \Cache::get($fullName); return Cache::get($fullName);
} }
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);
} }
if (null !== $config) { if (null !== $config) {
\Cache::forever($fullName, $config); Cache::forever($fullName, $config);
return $config; return $config;
} }
@@ -122,13 +124,13 @@ class FireflyConfig
$item->name = $name; $item->name = $name;
$item->data = $value; $item->data = $value;
$item->save(); $item->save();
\Cache::forget('ff-config-'.$name); Cache::forget('ff-config-' . $name);
return $item; return $item;
} }
$config->data = $value; $config->data = $value;
$config->save(); $config->save();
\Cache::forget('ff-config-'.$name); Cache::forget('ff-config-' . $name);
return $config; return $config;
} }

View File

@@ -29,6 +29,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Throwable;
/** /**
* Class AccountForm * Class AccountForm
@@ -68,7 +69,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);
} }
@@ -93,9 +94,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 = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN, AccountType::EXPENSE]; $types = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN, AccountType::EXPENSE];
$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');
@@ -111,21 +112,21 @@ 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 = [AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT]; $types = [AccountType::ASSET, AccountType::DEFAULT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::DEBT];
$grouped = $this->getAccountsGrouped($types); $grouped = $this->getAccountsGrouped($types);
unset($options['class']); unset($options['class']);
try { try {
$html = view('form.assetAccountCheckList', compact('classes', 'selected', 'name', 'label', 'options', 'grouped'))->render(); $html = view('form.assetAccountCheckList', compact('classes', 'selected', 'name', 'label', 'options', 'grouped'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render assetAccountCheckList(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render assetAccountCheckList(): %s', $e->getMessage()));
$html = 'Could not render assetAccountCheckList.'; $html = 'Could not render assetAccountCheckList.';

View File

@@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Throwable;
/** /**
* Class CurrencyForm * Class CurrencyForm
@@ -61,15 +62,15 @@ class CurrencyForm
$defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency(); $defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency();
/** @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] : $defaultCurrency->id; $sentCurrencyId = array_key_exists($key, $preFilled) ? (int) $preFilled[$key] : $defaultCurrency->id;
app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId));
@@ -89,8 +90,8 @@ class CurrencyForm
} }
try { try {
$html = view('form.'.$view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.' . $view, compact('defaultCurrency', '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.';
@@ -129,16 +130,16 @@ class CurrencyForm
$defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency(); $defaultCurrency = $options['currency'] ?? app('amount')->getDefaultCurrency();
/** @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] : $defaultCurrency->id; $sentCurrencyId = array_key_exists($key, $preFilled) ? (int) $preFilled[$key] : $defaultCurrency->id;
app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId)); app('log')->debug(sprintf('Sent currency ID is %d', $sentCurrencyId));
@@ -158,8 +159,8 @@ class CurrencyForm
} }
try { try {
$html = view('form.'.$view, compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render(); $html = view('form.' . $view, compact('defaultCurrency', '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.';
@@ -180,12 +181,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);
@@ -202,14 +203,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);

View File

@@ -28,6 +28,7 @@ use Carbon\Carbon;
use Carbon\Exceptions\InvalidDateException; use Carbon\Exceptions\InvalidDateException;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Illuminate\Support\MessageBag; use Illuminate\Support\MessageBag;
use Throwable;
/** /**
* Trait FormSupport * Trait FormSupport
@@ -36,7 +37,7 @@ trait FormSupport
{ {
public function multiSelect(string $name, ?array $list = null, $selected = null, ?array $options = null): string public function multiSelect(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);
@@ -46,7 +47,7 @@ trait FormSupport
try { try {
$html = view('form.multi-select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render(); $html = view('form.multi-select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Could not render multi-select(): %s', $e->getMessage())); app('log')->debug(sprintf('Could not render multi-select(): %s', $e->getMessage()));
$html = 'Could not render multi-select.'; $html = 'Could not render multi-select.';
} }
@@ -54,28 +55,6 @@ trait FormSupport
return $html; return $html;
} }
/**
* @param mixed $selected
*/
public function select(string $name, ?array $list = null, $selected = null, ?array $options = null): string
{
$list ??= [];
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$selected = $this->fillFieldValue($name, $selected);
unset($options['autocomplete'], $options['placeholder']);
try {
$html = view('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render();
} catch (\Throwable $e) {
app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage()));
$html = 'Could not render select.';
}
return $html;
}
protected function label(string $name, ?array $options = null): string protected function label(string $name, ?array $options = null): string
{ {
$options ??= []; $options ??= [];
@@ -84,7 +63,7 @@ trait FormSupport
} }
$name = str_replace('[]', '', $name); $name = str_replace('[]', '', $name);
return (string) trans('form.'.$name); return (string) trans('form.' . $name);
} }
/** /**
@@ -92,10 +71,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($label); $options['placeholder'] = ucfirst($label);
@@ -139,6 +118,28 @@ trait FormSupport
return $value; return $value;
} }
/**
* @param mixed $selected
*/
public function select(string $name, ?array $list = null, $selected = null, ?array $options = null): string
{
$list ??= [];
$label = $this->label($name, $options);
$options = $this->expandOptionArray($name, $label, $options);
$classes = $this->getHolderClasses($name);
$selected = $this->fillFieldValue($name, $selected);
unset($options['autocomplete'], $options['placeholder']);
try {
$html = view('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render();
} catch (Throwable $e) {
app('log')->debug(sprintf('Could not render select(): %s', $e->getMessage()));
$html = 'Could not render select.';
}
return $html;
}
protected function getAccountRepository(): AccountRepositoryInterface protected function getAccountRepository(): AccountRepositoryInterface
{ {
return app(AccountRepositoryInterface::class); return app(AccountRepositoryInterface::class);

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

@@ -64,7 +64,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'],
@@ -81,7 +81,7 @@ class AccountBalanceGrouped
'entries' => [], 'entries' => [],
'native_entries' => [], 'native_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'],
@@ -101,22 +101,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] = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']); $income['entries'][$label] = app('steam')->bcround($currency[$key]['earned'] ?? '0', $currency['currency_decimal_places']);
$expense['entries'][$label] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']); $expense['entries'][$label] = app('steam')->bcround($currency[$key]['spent'] ?? '0', $currency['currency_decimal_places']);
// converted entries // converted entries
$income['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_earned'] ?? '0', $currency['native_currency_decimal_places']); $income['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_earned'] ?? '0', $currency['native_currency_decimal_places']);
$expense['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_spent'] ?? '0', $currency['native_currency_decimal_places']); $expense['native_entries'][$label] = app('steam')->bcround($currency[$key]['native_spent'] ?? '0', $currency['native_currency_decimal_places']);
// next loop // next loop
$currentStart = app('navigation')->addPeriod($currentStart, $this->preferredRange, 0); $currentStart = app('navigation')->addPeriod($currentStart, $this->preferredRange, 0);
} }
$chartData[] = $income; $chartData[] = $income;
$chartData[] = $expense; $chartData[] = $expense;
} }
return $chartData; return $chartData;
@@ -142,9 +142,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);
@@ -152,12 +152,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 ? app('steam')->negative($journal['amount']) : app('steam')->positive($journal['amount']); $amount = 'spent' === $key ? app('steam')->negative($journal['amount']) : app('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 native currency. // perhaps transaction already has the foreign amount in the native currency.
if ((int) $journal['foreign_currency_id'] === $this->default->id) { if ((int) $journal['foreign_currency_id'] === $this->default->id) {
@@ -166,7 +166,7 @@ class AccountBalanceGrouped
} }
// add normal entry // add normal entry
$this->data[$currencyId][$period][$key] = bcadd($this->data[$currencyId][$period][$key], $amount); $this->data[$currencyId][$period][$key] = bcadd($this->data[$currencyId][$period][$key], $amount);
// add converted entry // add converted entry
$convertedKey = sprintf('native_%s', $key); $convertedKey = sprintf('native_%s', $key);
@@ -185,7 +185,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'],
@@ -202,8 +202,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',

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Support\Http\Api; namespace FireflyIII\Support\Http\Api;
use Carbon\Carbon; use Carbon\Carbon;
use DateTimeInterface;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
/** /**
@@ -45,7 +46,7 @@ trait ConvertsExchangeRates
// if not enabled, return the same array but without conversion: // if not enabled, return the same array but without conversion:
return $set; return $set;
$this->enabled = false; $this->enabled = false;
if (false === $this->enabled) { if (false === $this->enabled) {
$set['converted'] = false; $set['converted'] = false;
@@ -55,8 +56,8 @@ trait ConvertsExchangeRates
$set['converted'] = true; $set['converted'] = true;
/** @var TransactionCurrency $native */ /** @var TransactionCurrency $native */
$native = app('amount')->getDefaultCurrency(); $native = app('amount')->getDefaultCurrency();
$currency = $this->getCurrency((int) $set['currency_id']); $currency = $this->getCurrency((int) $set['currency_id']);
if ($native->id === $currency->id) { if ($native->id === $currency->id) {
$set['native_currency_id'] = (string) $currency->id; $set['native_currency_id'] = (string) $currency->id;
$set['native_currency_code'] = $currency->code; $set['native_currency_code'] = $currency->code;
@@ -66,9 +67,9 @@ trait ConvertsExchangeRates
return $set; return $set;
} }
foreach ($set['entries'] as $date => $entry) { foreach ($set['entries'] as $date => $entry) {
$carbon = Carbon::createFromFormat(\DateTimeInterface::ATOM, $date); $carbon = Carbon::createFromFormat(DateTimeInterface::ATOM, $date);
$rate = $this->getRate($currency, $native, $carbon); $rate = $this->getRate($currency, $native, $carbon);
$rate = '0' === $rate ? '1' : $rate; $rate = '0' === $rate ? '1' : $rate;
app('log')->debug(sprintf('bcmul("%s", "%s")', (string) $entry, $rate)); app('log')->debug(sprintf('bcmul("%s", "%s")', (string) $entry, $rate));
$set['entries'][$date] = (float) bcmul((string) $entry, $rate); $set['entries'][$date] = (float) bcmul((string) $entry, $rate);
} }

View File

@@ -85,8 +85,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) {
@@ -96,7 +96,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 #%d to #%d on %s.', $from->id, $to->id, $date->format('Y-m-d'))); Log::debug(sprintf('ExchangeRateConverter: Return DB rate from #%d to #%d on %s.', $from->id, $to->id, $date->format('Y-m-d')));
@@ -105,7 +105,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);
@@ -143,7 +143,7 @@ class ExchangeRateConverter
if ($from === $to) { if ($from === $to) {
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;
@@ -153,7 +153,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();
@@ -166,16 +166,15 @@ class ExchangeRateConverter
} }
/** @var null|CurrencyExchangeRate $result */ /** @var null|CurrencyExchangeRate $result */
$result = auth()->user() $result = auth()->user()
?->currencyExchangeRates() ?->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));
@@ -215,13 +214,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));
@@ -250,7 +249,7 @@ class ExchangeRateConverter
if ($cache->has()) { if ($cache->has()) {
return (int) $cache->get(); return (int) $cache->get();
} }
$euro = TransactionCurrency::whereCode('EUR')->first(); $euro = TransactionCurrency::whereCode('EUR')->first();
++$this->queryCount; ++$this->queryCount;
if (null === $euro) { if (null === $euro) {
throw new FireflyException('Cannot find EUR in system, cannot do currency conversion.'); throw new FireflyException('Cannot find EUR in system, cannot do currency conversion.');
@@ -272,14 +271,13 @@ class ExchangeRateConverter
$start->startOfDay(); $start->startOfDay();
$end->endOfDay(); $end->endOfDay();
Log::debug(sprintf('Preparing for %s to %s between %s and %s', $from->code, $to->code, $start->format('Y-m-d'), $end->format('Y-m-d'))); Log::debug(sprintf('Preparing for %s to %s between %s and %s', $from->code, $to->code, $start->format('Y-m-d'), $end->format('Y-m-d')));
$set = auth()->user() $set = auth()->user()
->currencyExchangeRates() ->currencyExchangeRates()
->where('from_currency_id', $from->id) ->where('from_currency_id', $from->id)
->where('to_currency_id', $to->id) ->where('to_currency_id', $to->id)
->where('date', '<=', $end->format('Y-m-d')) ->where('date', '<=', $end->format('Y-m-d'))
->where('date', '>=', $start->format('Y-m-d')) ->where('date', '>=', $start->format('Y-m-d'))
->orderBy('date', 'DESC')->get() ->orderBy('date', 'DESC')->get();
;
++$this->queryCount; ++$this->queryCount;
if (0 === $set->count()) { if (0 === $set->count()) {
Log::debug('No prepared rates found in this period, use the fallback'); Log::debug('No prepared rates found in this period, use the fallback');
@@ -293,10 +291,10 @@ class ExchangeRateConverter
$this->isPrepared = true; $this->isPrepared = true;
// so there is a fallback just in case. Now loop the set of rates we DO have. // so there is a fallback just in case. Now loop the set of rates we DO have.
$temp = []; $temp = [];
$count = 0; $count = 0;
foreach ($set as $rate) { foreach ($set as $rate) {
$date = $rate->date->format('Y-m-d'); $date = $rate->date->format('Y-m-d');
$temp[$date] ??= [ $temp[$date] ??= [
$from->id => [ $from->id => [
$to->id => $rate->rate, $to->id => $rate->rate,
@@ -305,11 +303,11 @@ class ExchangeRateConverter
++$count; ++$count;
} }
Log::debug(sprintf('Found %d rates in this period.', $count)); Log::debug(sprintf('Found %d rates in this period.', $count));
$currentStart = clone $start; $currentStart = clone $start;
while ($currentStart->lte($end)) { while ($currentStart->lte($end)) {
$currentDate = $currentStart->format('Y-m-d'); $currentDate = $currentStart->format('Y-m-d');
$this->prepared[$currentDate] ??= []; $this->prepared[$currentDate] ??= [];
$fallback = $temp[$currentDate][$from->id][$to->id] ?? $this->fallback[$from->id][$to->id] ?? '0'; $fallback = $temp[$currentDate][$from->id][$to->id] ?? $this->fallback[$from->id][$to->id] ?? '0';
if (0 === count($this->prepared[$currentDate]) && 0 !== bccomp('0', $fallback)) { if (0 === count($this->prepared[$currentDate]) && 0 !== bccomp('0', $fallback)) {
// fill from temp or fallback or from temp (see before) // fill from temp or fallback or from temp (see before)
$this->prepared[$currentDate][$from->id][$to->id] = $fallback; $this->prepared[$currentDate][$from->id][$to->id] = $fallback;

View File

@@ -40,7 +40,7 @@ trait ParsesQueryFilters
private function dateOrToday(QueryParameters $parameters, string $field): Carbon private function dateOrToday(QueryParameters $parameters, string $field): Carbon
{ {
$date = today(); $date = today();
$value = $parameters->filter()?->value($field, date('Y-m-d')); $value = $parameters->filter()?->value($field, date('Y-m-d'));

View File

@@ -30,7 +30,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 CurrencyRepositoryInterface $currencyRepository; private CurrencyRepositoryInterface $currencyRepository;
@@ -47,9 +47,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',
@@ -108,11 +108,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($journal['amount'], $multiplier); $amount = bcmul($journal['amount'], $multiplier);
$currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId); $currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$this->currencies[$currencyId] = $currency; $this->currencies[$currencyId] = $currency;
$nativeAmount = $converter->convert($currency, $this->default, $journal['date'], $amount); $nativeAmount = $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
$nativeAmount = $journal['foreign_amount']; $nativeAmount = $journal['foreign_amount'];

View File

@@ -56,8 +56,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 = $user->user_group_id; $groupId = $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));
@@ -68,7 +68,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);
@@ -79,14 +79,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

@@ -55,10 +55,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, [AccountType::REVENUE]); $revenue = $repository->findByName($expenseAccount->name, [AccountType::REVENUE]);
if (null !== $revenue) { if (null !== $revenue) {
$collection->push($revenue); $collection->push($revenue);
} }
@@ -115,7 +115,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;
} }
@@ -135,7 +135,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;
} }
@@ -157,7 +157,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;
} }
@@ -168,14 +168,14 @@ trait AugumentData
protected function getLimits(Budget $budget, Carbon $start, Carbon $end): Collection // get data + augment with info protected function getLimits(Budget $budget, Carbon $start, Carbon $end): Collection // get data + augment with info
{ {
/** @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);
@@ -191,7 +191,7 @@ trait AugumentData
/** @var BudgetLimit $entry */ /** @var BudgetLimit $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
$currency = $entry->transactionCurrency; $currency = $entry->transactionCurrency;
if (null === $currency) { if (null === $currency) {
$currency = app('amount')->getDefaultCurrency(); $currency = app('amount')->getDefaultCurrency();
@@ -227,7 +227,7 @@ trait AugumentData
/** @var array $journal */ /** @var array $journal */
foreach ($array as $journal) { foreach ($array as $journal) {
$name = '(no name)'; $name = '(no name)';
if (TransactionType::WITHDRAWAL === $journal['transaction_type_type']) { if (TransactionType::WITHDRAWAL === $journal['transaction_type_type']) {
$name = $journal['destination_account_name']; $name = $journal['destination_account_name'];
} }
@@ -250,16 +250,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([TransactionType::WITHDRAWAL])->setAccounts($total); $collector->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL])->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

@@ -38,7 +38,7 @@ trait BasicDataSupport
*/ */
protected function isInArray(array $array, int $entryId) protected function isInArray(array $array, int $entryId)
{ {
return $array[$entryId] ?? '0'; return $array[$entryId]['balance'] ?? '0';
} }
/** /**
@@ -46,6 +46,6 @@ trait BasicDataSupport
*/ */
protected function isInArrayDate(array $array, int $entryId): ?Carbon protected function isInArrayDate(array $array, int $entryId): ?Carbon
{ {
return $array[$entryId] ?? null; return $array[$entryId]['balance'] ?? null;
} }
} }

View File

@@ -30,6 +30,7 @@ use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\CacheProperties; use FireflyIII\Support\CacheProperties;
use FireflyIII\Support\Facades\Steam;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
/** /**
@@ -45,59 +46,59 @@ trait ChartGeneration
protected function accountBalanceChart(Collection $accounts, Carbon $start, Carbon $end): array // chart helper method. protected function accountBalanceChart(Collection $accounts, Carbon $start, Carbon $end): array // chart helper method.
{ {
// chart properties for cache: // chart properties for cache:
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('chart.account.account-balance-chart'); $cache->addProperty('chart.account.account-balance-chart');
$cache->addProperty($accounts); $cache->addProperty($accounts);
$convertToNative =app('preferences')->get('convert_to_native', false)->data; $convertToNative = app('preferences')->get('convert_to_native', false)->data;
if ($cache->has()) { if ($cache->has()) {
// return $cache->get(); // return $cache->get();
} }
app('log')->debug('Regenerate chart.account.account-balance-chart from scratch.'); app('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);
$default = app('amount')->getDefaultCurrency(); $default = app('amount')->getDefaultCurrency();
$chartData = []; $chartData = [];
/** @var Account $account */ /** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
// TODO we can use getAccountCurrency instead. // TODO we can use getAccountCurrency instead.
$currency = $accountRepos->getAccountCurrency($account); $currency = $accountRepos->getAccountCurrency($account);
if (null === $currency) { if (null === $currency) {
$currency = $default; $currency = $default;
} }
// if the user prefers the native currency, overrule the currency of the account. // if the user prefers the native currency, overrule the currency of the account.
if($currency->id !== $default->id && $convertToNative) { if ($currency->id !== $default->id && $convertToNative) {
$currency = $default; $currency = $default;
} }
$currentSet = [ $currentSet = [
'label' => $account->name, 'label' => $account->name,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'entries' => [], 'entries' => [],
]; ];
$currentStart = clone $start; $currentStart = clone $start;
$range = $convertToNative ? app('steam')->balanceInRangeNative($account, $start, clone $end) : app('steam')->balanceInRange($account, $start, clone $end); $range = Steam::finalAccountBalanceInRange($account, $start, clone $end);
$previous = array_values($range)[0]; $previous = array_values($range)[0];
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; $currentSet['entries'][$label] = $balance['balance']; // TODO or native_balance
} }
$chartData[] = $currentSet; $chartData[] = $currentSet;
} }
$data = $generator->multiSet($chartData); $data = $generator->multiSet($chartData);
$cache->store($data); $cache->store($data);
return $data; return $data;

View File

@@ -102,7 +102,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

@@ -32,6 +32,7 @@ use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Throwable;
/** /**
* Trait ModelInformation * Trait ModelInformation
@@ -55,7 +56,7 @@ trait ModelInformation
'count' => 1, 'count' => 1,
] ]
)->render(); )->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Throwable was thrown in getActionsForBill(): %s', $e->getMessage())); app('log')->error(sprintf('Throwable was thrown in getActionsForBill(): %s', $e->getMessage()));
app('log')->error($e->getTraceAsString()); app('log')->error($e->getTraceAsString());
$result = 'Could not render view. See log files.'; $result = 'Could not render view. See log files.';
@@ -74,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(AccountType::DEBT); $debt = $repository->getAccountTypeByType(AccountType::DEBT);
/** @var AccountType $loan */ /** @var AccountType $loan */
$loan = $repository->getAccountTypeByType(AccountType::LOAN); $loan = $repository->getAccountTypeByType(AccountType::LOAN);
/** @var AccountType $mortgage */ /** @var AccountType $mortgage */
$mortgage = $repository->getAccountTypeByType(AccountType::MORTGAGE); $mortgage = $repository->getAccountTypeByType(AccountType::MORTGAGE);
@@ -113,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));
@@ -142,7 +143,7 @@ trait ModelInformation
'triggers' => $triggers, 'triggers' => $triggers,
] ]
)->render(); )->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Throwable was thrown in getTriggersForBill(): %s', $e->getMessage())); app('log')->debug(sprintf('Throwable was thrown in getTriggersForBill(): %s', $e->getMessage()));
app('log')->debug($e->getTraceAsString()); app('log')->debug($e->getTraceAsString());
@@ -164,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));
@@ -173,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;
} }
@@ -219,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) {
@@ -242,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_are'; $journalTriggers[$index] = 'notes_are';
$values[$index] = $notes->text; $values[$index] = $notes->text;
@@ -258,7 +259,7 @@ trait ModelInformation
'triggers' => $triggers, 'triggers' => $triggers,
]; ];
$string = view('rules.partials.trigger', $renderInfo)->render(); $string = view('rules.partials.trigger', $renderInfo)->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Throwable was thrown in getTriggersForJournal(): %s', $e->getMessage())); app('log')->debug(sprintf('Throwable was thrown in getTriggersForJournal(): %s', $e->getMessage()));
app('log')->debug($e->getTraceAsString()); app('log')->debug($e->getTraceAsString());

View File

@@ -75,11 +75,11 @@ trait PeriodOverview
*/ */
protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array
{ {
$range = app('navigation')->getViewRange(true); $range = app('navigation')->getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for cache // properties for cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty('account-show-period-entries'); $cache->addProperty('account-show-period-entries');
@@ -89,32 +89,32 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range); $dates = app('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->setAccounts(new Collection([$account])); $collector->setAccounts(new Collection([$account]));
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionType::DEPOSIT]); $collector->setTypes([TransactionType::DEPOSIT]);
$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->setAccounts(new Collection([$account])); $collector->setAccounts(new Collection([$account]));
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionType::WITHDRAWAL]); $collector->setTypes([TransactionType::WITHDRAWAL]);
$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->setAccounts(new Collection([$account])); $collector->setAccounts(new Collection([$account]));
$collector->setRange($start, $end); $collector->setRange($start, $end);
$collector->setTypes([TransactionType::TRANSFER]); $collector->setTypes([TransactionType::TRANSFER]);
$transferSet = $collector->getExtractedJournals(); $transferSet = $collector->getExtractedJournals();
// loop dates // loop dates
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
@@ -125,15 +125,15 @@ trait PeriodOverview
$transferredIn = $this->filterTransferredIn($account, $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end'])); $transferredIn = $this->filterTransferredIn($account, $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']));
$entries[] $entries[]
= [ = [
'title' => $title, 'title' => $title,
'route' => route('accounts.show', [$account->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), 'route' => route('accounts.show', [$account->id, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferredAway) + count($transferredIn), 'total_transactions' => count($spent) + count($earned) + count($transferredAway) + count($transferredIn),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred_away' => $this->groupByCurrency($transferredAway), 'transferred_away' => $this->groupByCurrency($transferredAway),
'transferred_in' => $this->groupByCurrency($transferredIn), 'transferred_in' => $this->groupByCurrency($transferredIn),
]; ];
} }
$cache->store($entries); $cache->store($entries);
@@ -197,8 +197,8 @@ trait PeriodOverview
/** @var array $journal */ /** @var array $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id']; $currencyId = (int) $journal['currency_id'];
$foreignCurrencyId = $journal['foreign_currency_id']; $foreignCurrencyId = $journal['foreign_currency_id'];
if (!array_key_exists($currencyId, $return)) { if (!array_key_exists($currencyId, $return)) {
$return[$currencyId] = [ $return[$currencyId] = [
'amount' => '0', 'amount' => '0',
@@ -240,11 +240,11 @@ trait PeriodOverview
*/ */
protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array
{ {
$range = app('navigation')->getViewRange(true); $range = app('navigation')->getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
// properties for entries with their amounts. // properties for entries with their amounts.
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
$cache->addProperty($range); $cache->addProperty($range);
@@ -256,32 +256,32 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range); $dates = app('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([TransactionType::DEPOSIT]); $collector->setTypes([TransactionType::DEPOSIT]);
$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([TransactionType::WITHDRAWAL]); $collector->setTypes([TransactionType::WITHDRAWAL]);
$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([TransactionType::TRANSFER]); $collector->setTypes([TransactionType::TRANSFER]);
$transferSet = $collector->getExtractedJournals(); $transferSet = $collector->getExtractedJournals();
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']); $spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']); $earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
@@ -289,17 +289,17 @@ trait PeriodOverview
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); $title = app('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);
@@ -315,11 +315,11 @@ trait PeriodOverview
*/ */
protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array
{ {
$range = app('navigation')->getViewRange(true); $range = app('navigation')->getViewRange(true);
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end]; [$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('no-budget-period-entries'); $cache->addProperty('no-budget-period-entries');
@@ -329,28 +329,28 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range); $dates = app('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([TransactionType::WITHDRAWAL]); $collector->setRange($start, $end)->withoutBudget()->withAccountInformation()->setTypes([TransactionType::WITHDRAWAL]);
$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 = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); $title = app('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);
@@ -367,38 +367,38 @@ trait PeriodOverview
protected function getNoCategoryPeriodOverview(Carbon $theDate): array protected function getNoCategoryPeriodOverview(Carbon $theDate): array
{ {
app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d'))); app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d')));
$range = app('navigation')->getViewRange(true); $range = app('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 = app('navigation')->endOfPeriod($end, $range); $end = app('navigation')->endOfPeriod($end, $range);
app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d'))); app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d')));
app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d'))); app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d')));
// properties for cache // properties for cache
$dates = app('navigation')->blockPeriods($start, $end, $range); $dates = app('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([TransactionType::DEPOSIT]); $collector->setTypes([TransactionType::DEPOSIT]);
$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([TransactionType::WITHDRAWAL]); $collector->setTypes([TransactionType::WITHDRAWAL]);
$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([TransactionType::TRANSFER]); $collector->setTypes([TransactionType::TRANSFER]);
@@ -412,13 +412,13 @@ trait PeriodOverview
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); $title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
$entries[] $entries[]
= [ = [
'title' => $title, 'title' => $title,
'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]), 'route' => route('categories.no-category', [$currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferred), 'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent), 'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned), 'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred), 'transferred' => $this->groupByCurrency($transferred),
]; ];
} }
app('log')->debug('End of loops'); app('log')->debug('End of loops');
@@ -432,11 +432,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 = app('navigation')->getViewRange(true); $range = app('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');
@@ -446,37 +446,37 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range); $dates = app('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([TransactionType::DEPOSIT]); $collector->setTypes([TransactionType::DEPOSIT]);
$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([TransactionType::WITHDRAWAL]); $collector->setTypes([TransactionType::WITHDRAWAL]);
$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([TransactionType::TRANSFER]); $collector->setTypes([TransactionType::TRANSFER]);
$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']);
@@ -485,17 +485,17 @@ trait PeriodOverview
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']); $title = app('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;
@@ -505,7 +505,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) {
@@ -527,12 +527,12 @@ trait PeriodOverview
*/ */
protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array
{ {
$range = app('navigation')->getViewRange(true); $range = app('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');
@@ -542,13 +542,13 @@ trait PeriodOverview
} }
/** @var array $dates */ /** @var array $dates */
$dates = app('navigation')->blockPeriods($start, $end, $range); $dates = app('navigation')->blockPeriods($start, $end, $range);
$entries = []; $entries = [];
// 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();
foreach ($dates as $currentDate) { foreach ($dates as $currentDate) {
$spent = []; $spent = [];
@@ -567,14 +567,14 @@ trait PeriodOverview
$transferred = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']); $transferred = $this->filterJournalsByDate($genericSet, $currentDate['start'], $currentDate['end']);
} }
$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),
]; ];
} }
return $entries; return $entries;

View File

@@ -37,6 +37,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Support\Search\OperatorQuerySearch; use FireflyIII\Support\Search\OperatorQuerySearch;
use Throwable;
/** /**
* Trait RenderPartialViews * Trait RenderPartialViews
@@ -51,24 +52,24 @@ 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();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Could not render: %s', $e->getMessage())); app('log')->error(sprintf('Could not render: %s', $e->getMessage()));
$view = 'Firefly III could not render the view. Please see the log files.'; $view = 'Firefly III could not render the view. Please see the log files.';
@@ -91,7 +92,7 @@ trait RenderPartialViews
try { try {
$result = view('reports.options.budget', compact('budgets'))->render(); $result = view('reports.options.budget', compact('budgets'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage()));
$result = 'Could not render view.'; $result = 'Could not render view.';
@@ -112,18 +113,18 @@ 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();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Could not render: %s', $e->getMessage())); app('log')->error(sprintf('Could not render: %s', $e->getMessage()));
$view = 'Firefly III could not render the view. Please see the log files.'; $view = 'Firefly III could not render the view. Please see the log files.';
@@ -141,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);
@@ -150,7 +151,7 @@ trait RenderPartialViews
try { try {
$view = view('popup.report.category-entry', compact('journals', 'category'))->render(); $view = view('popup.report.category-entry', compact('journals', 'category'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Could not render: %s', $e->getMessage())); app('log')->error(sprintf('Could not render: %s', $e->getMessage()));
$view = 'Firefly III could not render the view. Please see the log files.'; $view = 'Firefly III could not render the view. Please see the log files.';
@@ -173,7 +174,7 @@ trait RenderPartialViews
try { try {
$result = view('reports.options.category', compact('categories'))->render(); $result = view('reports.options.category', compact('categories'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Cannot render reports.options.category: %s', $e->getMessage())); app('log')->error(sprintf('Cannot render reports.options.category: %s', $e->getMessage()));
$result = 'Could not render view.'; $result = 'Could not render view.';
@@ -215,7 +216,7 @@ trait RenderPartialViews
try { try {
$result = view('reports.options.double', compact('set'))->render(); $result = view('reports.options.double', compact('set'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage()));
$result = 'Could not render view.'; $result = 'Could not render view.';
@@ -236,19 +237,19 @@ 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();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Could not render: %s', $e->getMessage())); app('log')->error(sprintf('Could not render: %s', $e->getMessage()));
$view = 'Firefly III could not render the view. Please see the log files.'; $view = 'Firefly III could not render the view. Please see the log files.';
@@ -265,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();
@@ -284,7 +285,7 @@ trait RenderPartialViews
'count' => $count, 'count' => $count,
] ]
)->render(); )->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage())); app('log')->debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage()));
app('log')->error($e->getTraceAsString()); app('log')->error($e->getTraceAsString());
@@ -305,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));
@@ -324,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);
} }
@@ -339,7 +340,7 @@ trait RenderPartialViews
'triggers' => $triggers, 'triggers' => $triggers,
] ]
)->render(); )->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage())); app('log')->debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage()));
app('log')->error($e->getTraceAsString()); app('log')->error($e->getTraceAsString());
@@ -364,18 +365,18 @@ 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();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Could not render: %s', $e->getMessage())); app('log')->error(sprintf('Could not render: %s', $e->getMessage()));
$view = 'Firefly III could not render the view. Please see the log files.'; $view = 'Firefly III could not render the view. Please see the log files.';
@@ -394,7 +395,7 @@ trait RenderPartialViews
{ {
try { try {
$result = view('reports.options.no-options')->render(); $result = view('reports.options.no-options')->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Cannot render reports.options.no-options: %s', $e->getMessage())); app('log')->error(sprintf('Cannot render reports.options.no-options: %s', $e->getMessage()));
$result = 'Could not render view.'; $result = 'Could not render view.';
@@ -417,7 +418,7 @@ trait RenderPartialViews
try { try {
$result = view('reports.options.tag', compact('tags'))->render(); $result = view('reports.options.tag', compact('tags'))->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage())); app('log')->error(sprintf('Cannot render reports.options.tag: %s', $e->getMessage()));
$result = 'Could not render view.'; $result = 'Could not render view.';

View File

@@ -30,6 +30,7 @@ use FireflyIII\Http\Requests\RuleFormRequest;
use FireflyIII\Http\Requests\TestRuleFormRequest; use FireflyIII\Http\Requests\TestRuleFormRequest;
use FireflyIII\Support\Binder\AccountList; use FireflyIII\Support\Binder\AccountList;
use FireflyIII\User; use FireflyIII\User;
use Hash;
use Illuminate\Contracts\Validation\Validator as ValidatorContract; use Illuminate\Contracts\Validation\Validator as ValidatorContract;
use Illuminate\Routing\Route; use Illuminate\Routing\Route;
use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Validator;
@@ -82,13 +83,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;
@@ -122,7 +123,7 @@ trait RequestInformation
final protected function notInSessionRange(Carbon $date): bool // Validate a preference final protected function notInSessionRange(Carbon $date): bool // Validate a preference
{ {
/** @var Carbon $start */ /** @var Carbon $start */
$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());
@@ -144,20 +145,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 (null === $date) { if (null === $date) {
$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 (null === $date2) { if (null === $date2) {
$date2 = today(config('app.timezone')); $date2 = today(config('app.timezone'));
} }
$date2->endOfDay(); $date2->endOfDay();
$attributes['endDate'] = $date2; $attributes['endDate'] = $date2;
return $attributes; return $attributes;
} }
@@ -169,7 +170,7 @@ trait RequestInformation
*/ */
final protected function validatePassword(User $user, string $current, string $new): bool // get request info final protected function validatePassword(User $user, string $current, string $new): bool // get request info
{ {
if (!\Hash::check($current, $user->password)) { if (!Hash::check($current, $user->password)) {
throw new ValidationException((string) trans('firefly.invalid_current_password')); throw new ValidationException((string) trans('firefly.invalid_current_password'));
} }

View File

@@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Support\Search\OperatorQuerySearch; use FireflyIII\Support\Search\OperatorQuerySearch;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Throwable;
/** /**
* Trait RuleManagement * Trait RuleManagement
@@ -54,7 +55,7 @@ trait RuleManagement
'count' => $index + 1, 'count' => $index + 1,
] ]
)->render(); )->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->error(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage())); app('log')->error(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage()));
app('log')->error($e->getTraceAsString()); app('log')->error($e->getTraceAsString());
@@ -73,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));
@@ -99,7 +100,7 @@ trait RuleManagement
'triggers' => $triggers, 'triggers' => $triggers,
] ]
)->render(); )->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); app('log')->debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()));
app('log')->error($e->getTraceAsString()); app('log')->error($e->getTraceAsString());
@@ -128,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));
@@ -145,7 +146,7 @@ trait RuleManagement
'triggers' => $triggers, 'triggers' => $triggers,
] ]
)->render(); )->render();
} catch (\Throwable $e) { } catch (Throwable $e) {
app('log')->debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); app('log')->debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()));
app('log')->error($e->getTraceAsString()); app('log')->error($e->getTraceAsString());

View File

@@ -39,15 +39,14 @@ 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([TransactionType::WITHDRAWAL]) ->setTypes([TransactionType::WITHDRAWAL]);
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -61,8 +60,7 @@ trait TransactionCalculation
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setTags($tags)->withAccountInformation() ->setTags($tags)->withAccountInformation();
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -75,8 +73,7 @@ trait TransactionCalculation
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setBudgets($budgets)->withAccountInformation() ->setBudgets($budgets)->withAccountInformation();
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -93,8 +90,7 @@ trait TransactionCalculation
->setRange($start, $end) ->setRange($start, $end)
->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER]) ->setTypes([TransactionType::WITHDRAWAL, TransactionType::TRANSFER])
->setCategories($categories) ->setCategories($categories)
->withAccountInformation() ->withAccountInformation();
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -107,8 +103,7 @@ trait TransactionCalculation
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->setCategories($categories)->withAccountInformation() ->setCategories($categories)->withAccountInformation();
;
return $collector->getExtractedJournals(); return $collector->getExtractedJournals();
} }
@@ -118,7 +113,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);
@@ -135,8 +130,7 @@ trait TransactionCalculation
/** @var GroupCollectorInterface $collector */ /** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class); $collector = app(GroupCollectorInterface::class);
$collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER]) $collector->setAccounts($accounts)->setRange($start, $end)->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])
->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

@@ -37,7 +37,7 @@ trait UserGroupDetectable
public function detectUserGroup(): ?UserGroup public function detectUserGroup(): ?UserGroup
{ {
/** @var User $user */ /** @var User $user */
$user = auth()->user(); $user = auth()->user();
app('log')->debug('Now in detectUserGroup()'); app('log')->debug('Now in detectUserGroup()');
/** @var null|UserGroup $userGroup */ /** @var null|UserGroup $userGroup */
@@ -49,7 +49,7 @@ trait UserGroupDetectable
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

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Support\JsonApi\Enrichments; namespace FireflyIII\Support\JsonApi\Enrichments;
use Carbon\Carbon; use Carbon\Carbon;
use DB;
use FireflyIII\Models\Account; use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\ObjectGroup;
@@ -36,6 +37,8 @@ use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Override;
use stdClass;
/** /**
* Class AccountEnrichment * Class AccountEnrichment
@@ -44,16 +47,16 @@ use Illuminate\Support\Facades\Log;
*/ */
class AccountEnrichment implements EnrichmentInterface class AccountEnrichment implements EnrichmentInterface
{ {
private array $balances; private array $balances;
private Collection $collection; private Collection $collection;
private array $currencies; private array $currencies;
private CurrencyRepositoryInterface $currencyRepository; private CurrencyRepositoryInterface $currencyRepository;
private TransactionCurrency $default; private TransactionCurrency $default;
private ?Carbon $end; private ?Carbon $end;
private array $grouped; private array $grouped;
private array $objectGroups; private array $objectGroups;
private AccountRepositoryInterface $repository; private AccountRepositoryInterface $repository;
private ?Carbon $start; private ?Carbon $start;
public function __construct() public function __construct()
{ {
@@ -63,7 +66,7 @@ class AccountEnrichment implements EnrichmentInterface
$this->end = null; $this->end = null;
} }
#[\Override] #[Override]
public function enrichSingle(Model $model): Account public function enrichSingle(Model $model): Account
{ {
Log::debug(__METHOD__); Log::debug(__METHOD__);
@@ -73,7 +76,7 @@ class AccountEnrichment implements EnrichmentInterface
return $collection->first(); return $collection->first();
} }
#[\Override] #[Override]
/** /**
* Do the actual enrichment. * Do the actual enrichment.
*/ */
@@ -144,7 +147,7 @@ class AccountEnrichment implements EnrichmentInterface
$metaFields = $this->repository->getMetaValues($this->collection, ['is_multi_currency', 'currency_id', 'account_role', 'account_number', 'liability_direction', 'interest', 'interest_period', 'current_debt']); $metaFields = $this->repository->getMetaValues($this->collection, ['is_multi_currency', 'currency_id', 'account_role', 'account_number', 'liability_direction', 'interest', 'interest_period', 'current_debt']);
$currencyIds = $metaFields->where('name', 'currency_id')->pluck('data')->toArray(); $currencyIds = $metaFields->where('name', 'currency_id')->pluck('data')->toArray();
$currencies = []; $currencies = [];
foreach ($this->currencyRepository->getByIds($currencyIds) as $currency) { foreach ($this->currencyRepository->getByIds($currencyIds) as $currency) {
$id = $currency->id; $id = $currency->id;
$currencies[$id] = $currency; $currencies[$id] = $currency;
@@ -174,8 +177,8 @@ class AccountEnrichment implements EnrichmentInterface
$default = $this->default; $default = $this->default;
// get start and end, so the balance difference can be generated. // get start and end, so the balance difference can be generated.
$start = null; $start = null;
$end = null; $end = null;
if (null !== $this->start) { if (null !== $this->start) {
$start = Balance::getAccountBalances($this->collection, $this->start); $start = Balance::getAccountBalances($this->collection, $this->start);
} }
@@ -197,7 +200,7 @@ class AccountEnrichment implements EnrichmentInterface
'balance_difference' => null, 'balance_difference' => null,
]; ];
if (array_key_exists($account->id, $balances)) { if (array_key_exists($account->id, $balances)) {
$set = []; $set = [];
foreach ($balances[$account->id] as $currencyId => $entry) { foreach ($balances[$account->id] as $currencyId => $entry) {
$left = $start[$account->id][$currencyId]['balance'] ?? null; $left = $start[$account->id][$currencyId]['balance'] ?? null;
$right = $end[$account->id][$currencyId]['balance'] ?? null; $right = $end[$account->id][$currencyId]['balance'] ?? null;
@@ -239,12 +242,11 @@ class AccountEnrichment implements EnrichmentInterface
private function getObjectGroups(): void private function getObjectGroups(): void
{ {
$set = \DB::table('object_groupables') $set = DB::table('object_groupables')
->where('object_groupable_type', Account::class) ->where('object_groupable_type', Account::class)
->whereIn('object_groupable_id', $this->collection->pluck('id')->toArray()) ->whereIn('object_groupable_id', $this->collection->pluck('id')->toArray())
->distinct() ->distinct()
->get(['object_groupables.object_groupable_id', 'object_groupables.object_group_id']) ->get(['object_groupables.object_groupable_id', 'object_groupables.object_group_id']);
;
// get the groups: // get the groups:
$groupIds = $set->pluck('object_group_id')->toArray(); $groupIds = $set->pluck('object_group_id')->toArray();
$groups = ObjectGroup::whereIn('id', $groupIds)->get(); $groups = ObjectGroup::whereIn('id', $groupIds)->get();
@@ -254,7 +256,7 @@ class AccountEnrichment implements EnrichmentInterface
$this->objectGroups[$group->id] = $group; $this->objectGroups[$group->id] = $group;
} }
/** @var \stdClass $entry */ /** @var stdClass $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
$this->grouped[(int) $entry->object_groupable_id] = (int) $entry->object_group_id; $this->grouped[(int) $entry->object_groupable_id] = (int) $entry->object_group_id;
} }

View File

@@ -77,7 +77,7 @@ trait ExpandsQuery
$config = config('api.valid_api_filters')[$class]; $config = config('api.valid_api_filters')[$class];
$parsed = []; $parsed = [];
foreach ($filters->all() as $filter) { foreach ($filters->all() as $filter) {
$key = $filter->key(); $key = $filter->key();
if (!in_array($key, $config, true)) { if (!in_array($key, $config, true)) {
continue; continue;
} }

View File

@@ -39,8 +39,8 @@ trait FiltersPagination
$pagination['number'] = min(65536, max($pagination['number'], 1)); $pagination['number'] = min(65536, max($pagination['number'], 1));
// clean up page size // clean up page size
$pagination['size'] = (int) ($pagination['size'] ?? $this->getPageSize()); $pagination['size'] = (int) ($pagination['size'] ?? $this->getPageSize());
$pagination['size'] = min(1337, max($pagination['size'], 1)); $pagination['size'] = min(1337, max($pagination['size'], 1));
return $pagination; return $pagination;
} }

View File

@@ -71,15 +71,14 @@ 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());
} }
@@ -88,7 +87,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']);
// the balance value is an array. // the balance value is an array.
// first entry is the balance, second is the date. // first entry is the balance, second is the date.
@@ -100,8 +99,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, $entry->amount); $after = bcadd($before, $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;
@@ -127,18 +126,17 @@ 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);
@@ -170,7 +168,7 @@ class AccountBalanceCalculator
*/ */
foreach ($currencies as $currencyId => $balance) { foreach ($currencies as $currencyId => $balance) {
/** @var TransactionCurrency $currency */ /** @var TransactionCurrency $currency */
$currency = TransactionCurrency::find($currencyId); $currency = TransactionCurrency::find($currencyId);
if (null === $currency) { if (null === $currency) {
Log::error(sprintf('Could not find currency #%d, will not save account balance.', $currencyId)); Log::error(sprintf('Could not find currency #%d, will not save account balance.', $currencyId));
@@ -198,7 +196,7 @@ 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();
// recalculate the involved accounts: // recalculate the involved accounts:
$accounts = new Collection(); $accounts = new Collection();
@@ -210,9 +208,9 @@ class AccountBalanceCalculator
private function getAccountBalanceByAccount(int $account, int $currency): AccountBalance private function getAccountBalanceByAccount(int $account, int $currency): AccountBalance
{ {
$query = AccountBalance::where('title', 'balance')->where('account_id', $account)->where('transaction_currency_id', $currency); $query = AccountBalance::where('title', 'balance')->where('account_id', $account)->where('transaction_currency_id', $currency);
$entry = $query->first(); $entry = $query->first();
if (null !== $entry) { if (null !== $entry) {
// Log::debug(sprintf('Found account balance "balance" for account #%d and currency #%d: %s', $account, $currency, $entry->balance)); // Log::debug(sprintf('Found account balance "balance" for account #%d and currency #%d: %s', $account, $currency, $entry->balance));
@@ -231,9 +229,9 @@ class AccountBalanceCalculator
private function getAccountBalanceByJournal(string $title, int $account, int $journal, int $currency): AccountBalance private function getAccountBalanceByJournal(string $title, int $account, int $journal, int $currency): AccountBalance
{ {
$query = AccountBalance::where('title', $title)->where('account_id', $account)->where('transaction_journal_id', $journal)->where('transaction_currency_id', $currency); $query = AccountBalance::where('title', $title)->where('account_id', $account)->where('transaction_journal_id', $journal)->where('transaction_currency_id', $currency);
$entry = $query->first(); $entry = $query->first();
if (null !== $entry) { if (null !== $entry) {
return $entry; return $entry;
} }

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 > 12) { if ($loop > 12) {
@@ -117,7 +117,7 @@ class BillDateCalculator
} }
} }
Log::debug('end of loop'); Log::debug('end of loop');
$simple = $set->map( $simple = $set->map(
static function (Carbon $date) { static function (Carbon $date) {
return $date->format('Y-m-d'); return $date->format('Y-m-d');
} }
@@ -142,7 +142,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

@@ -30,6 +30,7 @@ use FireflyIII\Helpers\Fiscal\FiscalHelperInterface;
use FireflyIII\Support\Calendar\Calculator; use FireflyIII\Support\Calendar\Calculator;
use FireflyIII\Support\Calendar\Periodicity; use FireflyIII\Support\Calendar\Periodicity;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Throwable;
/** /**
* Class Navigation. * Class Navigation.
@@ -76,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 +94,7 @@ class Navigation
return $this->calculator->nextDateByInterval($epoch, $periodicity, $skipInterval); return $this->calculator->nextDateByInterval($epoch, $periodicity, $skipInterval);
} catch (IntervalException $exception) { } catch (IntervalException $exception) {
Log::warning($exception->getMessage(), ['exception' => $exception]); Log::warning($exception->getMessage(), ['exception' => $exception]);
} catch (\Throwable $exception) { // @phpstan-ignore-line } catch (Throwable $exception) { // @phpstan-ignore-line
Log::error($exception->getMessage(), ['exception' => $exception]); Log::error($exception->getMessage(), ['exception' => $exception]);
} }
@@ -110,7 +111,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;
@@ -160,9 +161,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',
@@ -209,7 +210,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(),
@@ -237,7 +238,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 = [
@@ -271,7 +272,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());
@@ -291,7 +292,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(),
@@ -311,7 +312,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]); // @phpstan-ignore-line $currentEnd->{$function}($modifierMap[$repeatFreq]); // @phpstan-ignore-line
@@ -342,13 +343,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',
@@ -361,7 +362,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
@@ -376,7 +377,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));
@@ -384,11 +385,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;
@@ -413,7 +414,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];
@@ -468,7 +469,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);
@@ -485,8 +486,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);
@@ -648,7 +649,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',
@@ -687,7 +688,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());
@@ -781,7 +782,7 @@ class Navigation
return $fiscalHelper->endOfFiscalYear($end); return $fiscalHelper->endOfFiscalYear($end);
} }
$list = [ $list = [
'last7', 'last7',
'last30', 'last30',
'last90', 'last90',

View File

@@ -24,10 +24,12 @@ declare(strict_types=1);
namespace FireflyIII\Support; namespace FireflyIII\Support;
use ArrayObject;
/** /**
* Class NullArrayObject * Class NullArrayObject
*/ */
class NullArrayObject extends \ArrayObject class NullArrayObject extends ArrayObject
{ {
/** @var null|mixed */ /** @var null|mixed */
public $default; public $default;

View File

@@ -77,15 +77,15 @@ class ParseDateString
public function parseDate(string $date): Carbon public function parseDate(string $date): Carbon
{ {
app('log')->debug(sprintf('parseDate("%s")', $date)); app('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 (false !== $result && 0 !== $result) { if (false !== $result && 0 !== $result) {
return $this->parseDefaultDate($date); return $this->parseDefaultDate($date);
} }
@@ -178,11 +178,11 @@ class ParseDateString
foreach ($parts as $part) { foreach ($parts as $part) {
app('log')->debug(sprintf('Now parsing part "%s"', $part)); app('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 || false === $result) { if (0 === $result || false === $result) {
app('log')->error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part)); app('log')->error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part));
@@ -196,7 +196,7 @@ class ParseDateString
continue; continue;
} }
$func = $functions[$direction][$period]; $func = $functions[$direction][$period];
app('log')->debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d'))); app('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
app('log')->debug(sprintf('Resulting date is %s', $today->format('Y-m-d'))); app('log')->debug(sprintf('Resulting date is %s', $today->format('Y-m-d')));

View File

@@ -47,16 +47,15 @@ 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 get(string $name, null|array|bool|int|string $default = null): ?Preference public function get(string $name, null | array | bool | int | string $default = null): ?Preference
{ {
/** @var null|User $user */ /** @var null|User $user */
$user = auth()->user(); $user = auth()->user();
@@ -70,7 +69,7 @@ class Preferences
return $this->getForUser($user, $name, $default); return $this->getForUser($user, $name, $default);
} }
public function getForUser(User $user, string $name, null|array|bool|int|string $default = null): ?Preference public function getForUser(User $user, string $name, null | array | bool | int | string $default = null): ?Preference
{ {
// don't care about user group ID, except for some specific preferences. // don't care about user group ID, except for some specific preferences.
$userGroupId = $this->getUserGroupId($user, $name); $userGroupId = $this->getUserGroupId($user, $name);
@@ -122,16 +121,16 @@ class Preferences
Cache::put($key, '', 5); Cache::put($key, '', 5);
} }
public function setForUser(User $user, string $name, null|array|bool|int|string $value): Preference public function setForUser(User $user, string $name, null | array | bool | int | string $value): Preference
{ {
$fullName = sprintf('preference%s%s', $user->id, $name); $fullName = sprintf('preference%s%s', $user->id, $name);
$groupId = $this->getUserGroupId($user, $name); $groupId = $this->getUserGroupId($user, $name);
$groupId = 0 === (int)$groupId ? null : (int) $groupId; $groupId = 0 === (int) $groupId ? null : (int) $groupId;
Cache::forget($fullName); Cache::forget($fullName);
/** @var null|Preference $pref */ /** @var null|Preference $pref */
$pref = Preference::where('user_group_id', $groupId)->where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']); $pref = Preference::where('user_group_id', $groupId)->where('user_id', $user->id)->where('name', $name)->first(['id', 'name', 'data', 'updated_at', 'created_at']);
if (null !== $pref && null === $value) { if (null !== $pref && null === $value) {
$pref->delete(); $pref->delete();
@@ -174,13 +173,12 @@ 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) {
@@ -218,7 +216,7 @@ class Preferences
return $result; return $result;
} }
public function getEncryptedForUser(User $user, string $name, null|array|bool|int|string $default = null): ?Preference public function getEncryptedForUser(User $user, string $name, null | array | bool | int | string $default = null): ?Preference
{ {
$result = $this->getForUser($user, $name, $default); $result = $this->getForUser($user, $name, $default);
if ('' === $result->data) { if ('' === $result->data) {
@@ -239,7 +237,7 @@ class Preferences
return $result; return $result;
} }
public function getFresh(string $name, null|array|bool|int|string $default = null): ?Preference public function getFresh(string $name, null | array | bool | int | string $default = null): ?Preference
{ {
/** @var null|User $user */ /** @var null|User $user */
$user = auth()->user(); $user = auth()->user();
@@ -277,20 +275,7 @@ class Preferences
Session::forget('first'); Session::forget('first');
} }
public function setEncrypted(string $name, mixed $value): Preference public function set(string $name, null | array | bool | int | string $value): Preference
{
try {
$encrypted = encrypt($value);
} catch (EncryptException $e) {
Log::error(sprintf('Could not encrypt preference "%s": %s', $name, $e->getMessage()));
throw new FireflyException(sprintf('Could not encrypt preference "%s". Cowardly refuse to continue.', $name));
}
return $this->set($name, $encrypted);
}
public function set(string $name, null|array|bool|int|string $value): Preference
{ {
/** @var null|User $user */ /** @var null|User $user */
$user = auth()->user(); $user = auth()->user();
@@ -305,4 +290,17 @@ class Preferences
return $this->setForUser($user, $name, $value); return $this->setForUser($user, $name, $value);
} }
public function setEncrypted(string $name, mixed $value): Preference
{
try {
$encrypted = encrypt($value);
} catch (EncryptException $e) {
Log::error(sprintf('Could not encrypt preference "%s": %s', $name, $e->getMessage()));
throw new FireflyException(sprintf('Could not encrypt preference "%s". Cowardly refuse to continue.', $name));
}
return $this->set($name, $encrypted);
}
} }

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,
@@ -113,18 +113,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], $journal['amount']); = bcadd($this->report[$sourceAccountId]['currencies'][$currencyId]['budgets'][$budgetId], $journal['amount']);
} }
} }
@@ -162,7 +162,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,
@@ -171,7 +171,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) {
@@ -184,14 +184,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([$budget])); $expenses = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, $this->accounts, new Collection([$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,
@@ -212,17 +212,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($this->report['sums'][$currencyId]['budgeted'], $limit->amount); $this->report['sums'][$currencyId]['budgeted'] = bcadd($this->report['sums'][$currencyId]['budgeted'], $limit->amount);
$this->report['sums'][$currencyId]['spent'] = bcadd($this->report['sums'][$currencyId]['spent'], $spent); $this->report['sums'][$currencyId]['spent'] = bcadd($this->report['sums'][$currencyId]['spent'], $spent);
$this->report['sums'][$currencyId]['left'] = bcadd($this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent)); $this->report['sums'][$currencyId]['left'] = bcadd($this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent));
@@ -242,16 +242,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,
@@ -267,7 +267,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', $noBudgetEntry['sum']); $this->report['sums'][$nbCurrencyId]['spent'] = bcadd($this->report['sums'][$nbCurrencyId]['spent'] ?? '0', $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;
@@ -290,15 +290,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($spent, '0') && 0 !== bccomp($totalSpent, '0')) { if (0 !== bccomp($spent, '0') && 0 !== bccomp($totalSpent, '0')) {
$spentPct = round((float) bcmul(bcdiv($spent, $totalSpent), '100')); $spentPct = round((float) bcmul(bcdiv($spent, $totalSpent), '100'));
@@ -306,7 +306,7 @@ class BudgetReportGenerator
if (0 !== bccomp($budgeted, '0') && 0 !== bccomp($totalBudgeted, '0')) { if (0 !== bccomp($budgeted, '0') && 0 !== bccomp($totalBudgeted, '0')) {
$budgetedPct = round((float) bcmul(bcdiv($budgeted, $totalBudgeted), '100')); $budgetedPct = round((float) bcmul(bcdiv($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;
} }

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' => [],
]; ];
@@ -121,7 +121,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'],
@@ -137,9 +137,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($this->report['sums'][$currencyId]['sum'], $journal['amount']); $this->report['sums'][$currencyId]['sum'] = bcadd($this->report['sums'][$currencyId]['sum'], $journal['amount']);
// sum of spent: // sum of spent:
$this->report['sums'][$currencyId]['spent'] = -1 === bccomp($journal['amount'], '0') ? bcadd( $this->report['sums'][$currencyId]['spent'] = -1 === bccomp($journal['amount'], '0') ? bcadd(
$this->report['sums'][$currencyId]['spent'], $this->report['sums'][$currencyId]['spent'],
$journal['amount'] $journal['amount']
) : $this->report['sums'][$currencyId]['spent']; ) : $this->report['sums'][$currencyId]['spent'];
@@ -150,14 +150,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($this->report['categories'][$key]['sum'], $journal['amount']); $this->report['categories'][$key]['sum'] = bcadd($this->report['categories'][$key]['sum'], $journal['amount']);
// total spent in category // total spent in category
$this->report['categories'][$key]['spent'] = -1 === bccomp($journal['amount'], '0') ? bcadd( $this->report['categories'][$key]['spent'] = -1 === bccomp($journal['amount'], '0') ? bcadd(
$this->report['categories'][$key]['spent'], $this->report['categories'][$key]['spent'],
$journal['amount'] $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($journal['amount'], '0') ? bcadd( $this->report['categories'][$key]['earned'] = 1 === bccomp($journal['amount'], '0') ? bcadd(
$this->report['categories'][$key]['earned'], $this->report['categories'][$key]['earned'],
$journal['amount'] $journal['amount']
) : $this->report['categories'][$key]['earned']; ) : $this->report['categories'][$key]['earned'];

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

@@ -86,7 +86,7 @@ trait CalculateXOccurrencesSince
++$total; ++$total;
} }
++$attempts; ++$attempts;
$mutator = $mutator->endOfMonth()->addDay(); $mutator = $mutator->endOfMonth()->addDay();
} }
return $return; return $return;
@@ -101,10 +101,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
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); app('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?
@@ -135,15 +135,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
{ {
app('log')->debug(sprintf('Now in %s', __METHOD__)); app('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();
@@ -187,7 +187,7 @@ trait CalculateXOccurrencesSince
$date->addYear(); $date->addYear();
app('log')->debug(sprintf('Date is now %s', $date->format('Y-m-d'))); app('log')->debug(sprintf('Date is now %s', $date->format('Y-m-d')));
} }
$obj = clone $date; $obj = clone $date;
while ($total < $count) { while ($total < $count) {
app('log')->debug(sprintf('total (%d) < count (%d) so go.', $total, $count)); app('log')->debug(sprintf('total (%d) < count (%d) so go.', $total, $count));
app('log')->debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod)); app('log')->debug(sprintf('attempts (%d) %% skipmod (%d) === %d', $attempts, $skipMod, $attempts % $skipMod));

View File

@@ -44,7 +44,7 @@ trait FiltersWeekends
return $dates; return $dates;
} }
$return = []; $return = [];
/** @var Carbon $date */ /** @var Carbon $date */
foreach ($dates as $date) { foreach ($dates as $date) {
@@ -58,7 +58,7 @@ trait FiltersWeekends
// is weekend and must set back to Friday? // is weekend and must set back to Friday?
if (RecurrenceRepetition::WEEKEND_TO_FRIDAY === $repetition->weekend) { if (RecurrenceRepetition::WEEKEND_TO_FRIDAY === $repetition->weekend) {
$clone = clone $date; $clone = clone $date;
$clone->addDays(5 - $date->dayOfWeekIso); $clone->addDays(5 - $date->dayOfWeekIso);
app('log')->debug( app('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'))
@@ -70,7 +70,7 @@ trait FiltersWeekends
// postpone to Monday? // postpone to Monday?
if (RecurrenceRepetition::WEEKEND_TO_MONDAY === $repetition->weekend) { if (RecurrenceRepetition::WEEKEND_TO_MONDAY === $repetition->weekend) {
$clone = clone $date; $clone = clone $date;
$clone->addDays(8 - $date->dayOfWeekIso); $clone->addDays(8 - $date->dayOfWeekIso);
app('log')->debug( app('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

@@ -54,10 +54,10 @@ trait UserGroupTrait
/** /**
* @throws FireflyException * @throws FireflyException
*/ */
public function setUser(null|Authenticatable|User $user): void public function setUser(null | Authenticatable | User $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 has no user group.', $user->id)); throw new FireflyException(sprintf('User #%d has no user group.', $user->id));
} }
@@ -70,16 +70,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

@@ -82,12 +82,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) {
@@ -245,9 +245,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

@@ -27,4 +27,6 @@ namespace FireflyIII\Support\Request;
/** /**
* Trait ConvertAPIDataTypes * Trait ConvertAPIDataTypes
*/ */
trait ConvertAPIDataTypes {} trait ConvertAPIDataTypes
{
}

View File

@@ -96,24 +96,6 @@ trait ConvertsDataTypes
return Steam::filterSpaces($string); return Steam::filterSpaces($string);
} }
public function convertIban(string $field): string
{
return Steam::filterSpaces($this->convertString($field));
}
/**
* Return string value.
*/
public function convertString(string $field, string $default = ''): string
{
$entry = $this->get($field);
if (!is_scalar($entry)) {
return $default;
}
return (string) $this->clearString((string) $entry);
}
public function clearString(?string $string): ?string public function clearString(?string $string): ?string
{ {
$string = $this->clearStringKeepNewlines($string); $string = $this->clearStringKeepNewlines($string);
@@ -147,12 +129,22 @@ trait ConvertsDataTypes
return trim((string) $string); return trim((string) $string);
} }
/** public function convertIban(string $field): string
* Return integer value.
*/
public function convertInteger(string $field): int
{ {
return (int) $this->get($field); return Steam::filterSpaces($this->convertString($field));
}
/**
* Return string value.
*/
public function convertString(string $field, string $default = ''): string
{
$entry = $this->get($field);
if (!is_scalar($entry)) {
return $default;
}
return (string) $this->clearString((string) $entry);
} }
/** /**
@@ -161,6 +153,14 @@ trait ConvertsDataTypes
*/ */
abstract public function get(string $key, mixed $default = null): mixed; abstract public function get(string $key, mixed $default = null): mixed;
/**
* Return integer value.
*/
public function convertInteger(string $field): int
{
return (int) $this->get($field);
}
/** /**
* TODO duplicate, see SelectTransactionsRequest * TODO duplicate, see SelectTransactionsRequest
* *

View File

@@ -38,13 +38,13 @@ trait GetFilterInstructions
return []; return [];
} }
foreach ($set as $info) { foreach ($set as $info) {
$column = $info['column'] ?? 'NOPE'; $column = $info['column'] ?? 'NOPE';
$filterValue = (string) ($info['filter'] ?? self::INVALID_FILTER); $filterValue = (string) ($info['filter'] ?? self::INVALID_FILTER);
if (false === in_array($column, $allowed, true)) { if (false === in_array($column, $allowed, true)) {
// skip invalid column // skip invalid column
continue; continue;
} }
$filterType = $config[$column] ?? false; $filterType = $config[$column] ?? false;
switch ($filterType) { switch ($filterType) {
default: default:

View File

@@ -35,8 +35,8 @@ trait GetSortInstructions
return []; return [];
} }
foreach ($set as $info) { foreach ($set as $info) {
$column = $info['column'] ?? 'NOPE'; $column = $info['column'] ?? 'NOPE';
$direction = $info['direction'] ?? 'NOPE'; $direction = $info['direction'] ?? 'NOPE';
if ('asc' !== $direction && 'desc' !== $direction) { if ('asc' !== $direction && 'desc' !== $direction) {
// skip invalid direction // skip invalid direction
continue; continue;

View File

@@ -35,16 +35,16 @@ use Illuminate\Support\Collection;
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';
@@ -61,10 +61,9 @@ 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 +134,7 @@ class AccountSearch implements GenericSearchInterface
$this->types = $types; $this->types = $types;
} }
public function setUser(null|Authenticatable|User $user): void public function setUser(null | Authenticatable | User $user): void
{ {
if ($user instanceof User) { if ($user instanceof User) {
$this->user = $user; $this->user = $user;

View File

@@ -57,6 +57,8 @@ use Gdbots\QueryParser\Node\Word;
use Gdbots\QueryParser\QueryParser; use Gdbots\QueryParser\QueryParser;
use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use LogicException;
use TypeError;
/** /**
* Class OperatorQuerySearch * Class OperatorQuerySearch
@@ -149,7 +151,7 @@ class OperatorQuerySearch implements SearchInterface
try { try {
$query1 = $parser->parse($query); $query1 = $parser->parse($query);
} catch (\LogicException|\TypeError $e) { } catch (LogicException | TypeError $e) {
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));
@@ -201,7 +203,7 @@ class OperatorQuerySearch implements SearchInterface
case Emoticon::class: case Emoticon::class:
case Emoji::class: case Emoji::class:
case Mention::class: case Mention::class:
$allWords = (string) $searchNode->getValue(); $allWords = (string) $searchNode->getValue();
app('log')->debug(sprintf('Add words "%s" to search string, because Node class is "%s"', $allWords, $class)); app('log')->debug(sprintf('Add words "%s" to search string, because Node class is "%s"', $allWords, $class));
$this->words[] = $allWords; $this->words[] = $allWords;
@@ -212,10 +214,10 @@ class OperatorQuerySearch implements SearchInterface
/** @var Field $searchNode */ /** @var Field $searchNode */
// used to search for x:y // used to search for x:y
$operator = strtolower($searchNode->getValue()); $operator = strtolower($searchNode->getValue());
$value = $searchNode->getNode()->getValue(); $value = $searchNode->getNode()->getValue();
$prohibited = BoolOperator::PROHIBITED === $searchNode->getBoolOperator(); $prohibited = BoolOperator::PROHIBITED === $searchNode->getBoolOperator();
$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) {
@@ -275,15 +277,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':
app('log')->info(sprintf('Ignore search operator "%s"', $operator)); app('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);
@@ -445,7 +447,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([$account])); $this->collector->setSourceAccounts(new Collection([$account]));
} }
@@ -457,7 +459,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([$account])); $this->collector->excludeSourceAccounts(new Collection([$account]));
} }
@@ -469,25 +471,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;
@@ -573,7 +575,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([$account])); $this->collector->setDestinationAccounts(new Collection([$account]));
} }
@@ -584,7 +586,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([$account])); $this->collector->excludeDestinationAccounts(new Collection([$account]));
} }
@@ -595,8 +597,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) {
@@ -613,8 +615,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) {
@@ -630,48 +632,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([$account])); $this->collector->setSourceAccounts(new Collection([$account]));
break; break;
case '-source_is_cash': case '-source_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->excludeSourceAccounts(new Collection([$account])); $this->collector->excludeSourceAccounts(new Collection([$account]));
break; break;
case 'destination_is_cash': case 'destination_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->setDestinationAccounts(new Collection([$account])); $this->collector->setDestinationAccounts(new Collection([$account]));
break; break;
case '-destination_is_cash': case '-destination_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->excludeDestinationAccounts(new Collection([$account])); $this->collector->excludeDestinationAccounts(new Collection([$account]));
break; break;
case 'account_is_cash': case 'account_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->setAccounts(new Collection([$account])); $this->collector->setAccounts(new Collection([$account]));
break; break;
case '-account_is_cash': case '-account_is_cash':
$account = $this->getCashAccount(); $account = $this->getCashAccount();
$this->collector->excludeAccounts(new Collection([$account])); $this->collector->excludeAccounts(new Collection([$account]));
break; break;
// //
// description // description
// //
case 'description_starts': case 'description_starts':
$this->collector->descriptionStarts([$value]); $this->collector->descriptionStarts([$value]);
@@ -693,7 +695,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'description_contains': case 'description_contains':
$this->words[] = $value; $this->words[] = $value;
return false; return false;
@@ -712,11 +714,11 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// currency // currency
// //
case 'currency_is': case 'currency_is':
$currency = $this->findCurrency($value); $currency = $this->findCurrency($value);
if (null !== $currency) { if (null !== $currency) {
$this->collector->setCurrency($currency); $this->collector->setCurrency($currency);
} }
@@ -727,7 +729,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-currency_is': case '-currency_is':
$currency = $this->findCurrency($value); $currency = $this->findCurrency($value);
if (null !== $currency) { if (null !== $currency) {
$this->collector->excludeCurrency($currency); $this->collector->excludeCurrency($currency);
} }
@@ -738,7 +740,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case 'foreign_currency_is': case 'foreign_currency_is':
$currency = $this->findCurrency($value); $currency = $this->findCurrency($value);
if (null !== $currency) { if (null !== $currency) {
$this->collector->setForeignCurrency($currency); $this->collector->setForeignCurrency($currency);
} }
@@ -749,7 +751,7 @@ class OperatorQuerySearch implements SearchInterface
break; break;
case '-foreign_currency_is': case '-foreign_currency_is':
$currency = $this->findCurrency($value); $currency = $this->findCurrency($value);
if (null !== $currency) { if (null !== $currency) {
$this->collector->excludeForeignCurrency($currency); $this->collector->excludeForeignCurrency($currency);
} }
@@ -759,9 +761,9 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// attachments // attachments
// //
case 'has_attachments': case 'has_attachments':
case '-has_no_attachments': case '-has_no_attachments':
app('log')->debug('Set collector to filter on attachments.'); app('log')->debug('Set collector to filter on attachments.');
@@ -776,8 +778,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();
@@ -791,7 +793,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);
@@ -802,7 +804,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);
@@ -812,7 +814,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);
} }
@@ -823,7 +825,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);
} }
@@ -834,7 +836,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);
} }
@@ -845,7 +847,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);
} }
@@ -856,7 +858,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);
} }
@@ -867,7 +869,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);
} }
@@ -877,9 +879,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();
@@ -893,7 +895,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);
} }
@@ -904,7 +906,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);
} }
@@ -915,7 +917,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);
@@ -926,7 +928,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);
@@ -937,7 +939,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);
} }
@@ -948,7 +950,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);
} }
@@ -959,7 +961,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);
} }
@@ -970,7 +972,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);
} }
@@ -980,9 +982,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();
@@ -996,7 +998,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);
@@ -1007,7 +1009,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);
@@ -1018,7 +1020,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);
@@ -1029,7 +1031,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([$bill])); $this->collector->excludeBills(new Collection([$bill]));
@@ -1040,7 +1042,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);
} }
@@ -1051,7 +1053,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);
} }
@@ -1062,7 +1064,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);
} }
@@ -1073,7 +1075,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);
} }
@@ -1083,9 +1085,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();
@@ -1100,7 +1102,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);
@@ -1114,7 +1116,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()) {
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
$this->collector->findNothing(); $this->collector->findNothing();
@@ -1128,7 +1130,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()) {
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
$this->collector->findNothing(); $this->collector->findNothing();
@@ -1142,7 +1144,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()) {
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
$this->collector->findNothing(); $this->collector->findNothing();
@@ -1155,7 +1157,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()) {
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
$this->collector->findNothing(); $this->collector->findNothing();
@@ -1168,7 +1170,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()) {
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
$this->collector->findNothing(); $this->collector->findNothing();
@@ -1181,7 +1183,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()) {
app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator)); app('log')->info(sprintf('No valid tags in "%s"-operator, so search will not return ANY results.', $operator));
@@ -1196,7 +1198,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);
@@ -1204,9 +1206,9 @@ class OperatorQuerySearch implements SearchInterface
break; break;
// //
// notes // notes
// //
case 'notes_contains': case 'notes_contains':
$this->collector->notesContain($value); $this->collector->notesContain($value);
@@ -1269,14 +1271,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.
app('log')->debug(sprintf('Original value "%s"', $value)); app('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);
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->amountIs($amount); $this->collector->amountIs($amount);
@@ -1285,8 +1287,8 @@ class OperatorQuerySearch implements SearchInterface
case '-amount_is': case '-amount_is':
// strip comma's, make dots. // strip comma's, make dots.
app('log')->debug(sprintf('Original value "%s"', $value)); app('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);
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->amountIsNot($amount); $this->collector->amountIsNot($amount);
@@ -1294,9 +1296,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);
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->foreignAmountIs($amount); $this->collector->foreignAmountIs($amount);
@@ -1304,9 +1306,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);
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->foreignAmountIsNot($amount); $this->collector->foreignAmountIsNot($amount);
@@ -1315,9 +1317,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);
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->amountLess($amount); $this->collector->amountLess($amount);
@@ -1326,9 +1328,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);
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->foreignAmountLess($amount); $this->collector->foreignAmountLess($amount);
@@ -1338,8 +1340,8 @@ class OperatorQuerySearch implements SearchInterface
case 'amount_more': case 'amount_more':
app('log')->debug(sprintf('Now handling operator "%s"', $operator)); app('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);
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount));
$this->collector->amountMore($amount); $this->collector->amountMore($amount);
@@ -1349,16 +1351,16 @@ class OperatorQuerySearch implements SearchInterface
case 'foreign_amount_more': case 'foreign_amount_more':
app('log')->debug(sprintf('Now handling operator "%s"', $operator)); app('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);
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $amount)); app('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)]);
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value));
@@ -1371,152 +1373,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;
@@ -1524,7 +1526,7 @@ class OperatorQuerySearch implements SearchInterface
case 'created_at_on': case 'created_at_on':
case '-created_at_on': case '-created_at_on':
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); app('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;
@@ -1532,7 +1534,7 @@ class OperatorQuerySearch implements SearchInterface
case 'created_at_before': case 'created_at_before':
case '-created_at_after': case '-created_at_after':
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); app('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;
@@ -1540,7 +1542,7 @@ class OperatorQuerySearch implements SearchInterface
case 'created_at_after': case 'created_at_after':
case '-created_at_before': case '-created_at_before':
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); app('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;
@@ -1548,7 +1550,7 @@ class OperatorQuerySearch implements SearchInterface
case 'updated_at_on': case 'updated_at_on':
case '-updated_at_on': case '-updated_at_on':
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); app('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;
@@ -1556,7 +1558,7 @@ class OperatorQuerySearch implements SearchInterface
case 'updated_at_before': case 'updated_at_before':
case '-updated_at_after': case '-updated_at_after':
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); app('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;
@@ -1564,14 +1566,14 @@ class OperatorQuerySearch implements SearchInterface
case 'updated_at_after': case 'updated_at_after':
case '-updated_at_before': case '-updated_at_before':
app('log')->debug(sprintf('Set "%s" using collector with value "%s"', $operator, $value)); app('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();
@@ -1636,9 +1638,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);
@@ -1840,7 +1842,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));
} }
@@ -1894,7 +1896,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) {
@@ -1908,7 +1910,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) {
app('log')->debug('Found zero accounts, search for non existing account, NO results will be returned.'); app('log')->debug('Found zero accounts, search for non existing account, NO results will be returned.');
$this->collector->findNothing(); $this->collector->findNothing();
@@ -1921,7 +1923,7 @@ class OperatorQuerySearch implements SearchInterface
return; return;
} }
app('log')->debug(sprintf('Found %d accounts, will filter.', $accounts->count())); app('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) {
return $stringMethod(strtolower($account->name), strtolower($value)); return $stringMethod(strtolower($account->name), strtolower($value));
} }
@@ -1976,7 +1978,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) {
@@ -1990,7 +1992,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()) {
app('log')->debug('Found zero accounts, search for invalid account.'); app('log')->debug('Found zero accounts, search for invalid account.');
$this->collector->findNothing(); $this->collector->findNothing();
@@ -2000,7 +2002,7 @@ class OperatorQuerySearch implements SearchInterface
// if found, do filter // if found, do filter
app('log')->debug(sprintf('Found %d accounts, will filter.', $accounts->count())); app('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));

View File

@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace FireflyIII\Support; namespace FireflyIII\Support;
use Carbon\Carbon; use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use DB; use DB;
use Exception; use Exception;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
@@ -32,7 +31,6 @@ use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use stdClass; use stdClass;
@@ -44,468 +42,72 @@ use ValueError;
*/ */
class Steam class Steam
{ {
/** public function getAccountCurrency(Account $account): ?TransactionCurrency
* @throws FireflyException
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @deprecated
*/
public function balanceInRangeConverted(Account $account, Carbon $start, Carbon $end, TransactionCurrency $native): array
{ {
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__)); $type = $account->accountType->type;
$list = config('firefly.valid_currency_account_types');
// return null if not in this list.
if (!in_array($type, $list, true)) {
return null;
}
$result = $account->accountMeta->where('name', 'currency_id')->first();
if (null === $result) {
return null;
}
return TransactionCurrency::find((int) $result->data);
}
private function sumTransactions(array $transactions, string $key): string
{
$sum = '0';
/** @var array $transaction */
foreach ($transactions as $transaction) {
$value = (string) ($transaction[$key] ?? '0');
$value = '' === $value ? '0' : $value;
$sum = bcadd($sum, $value);
}
return $sum;
}
public function finalAccountBalanceInRange(Account $account, Carbon $start, Carbon $end): array
{
// expand period.
$start->subDay()->startOfDay();
$end->addDay()->endOfDay();
// set up cache
$cache = new CacheProperties(); $cache = new CacheProperties();
$cache->addProperty($account->id); $cache->addProperty($account->id);
$cache->addProperty('balance-in-range-converted'); $cache->addProperty('final-balance-in-range');
$cache->addProperty($native->id);
$cache->addProperty($start); $cache->addProperty($start);
$cache->addProperty($end); $cache->addProperty($end);
if ($cache->has()) { if ($cache->has()) {
//return $cache->get();
}
Log::debug(sprintf('balanceInRangeConverted for account #%d to %s', $account->id, $native->code));
$start->subDay();
$end->addDay();
$balances = [];
$formatted = $start->format('Y-m-d');
$currencies = [];
$startBalance = $this->balanceConverted($account, $start, $native); // already converted to native amount
$balances[$formatted] = $startBalance;
Log::debug(sprintf('Start balance on %s is %s', $formatted, $startBalance));
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$converter = new ExchangeRateConverter();
// not sure why this is happening:
$start->addDay();
// grab all transactions between start and end:
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[
'transaction_journals.date',
'transactions.transaction_currency_id',
'transactions.amount',
'transactions.foreign_currency_id',
'transactions.foreign_amount',
]
)->toArray();
// loop the set and convert if necessary:
$currentBalance = $startBalance;
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$day = false;
try {
$day = Carbon::parse($transaction['date'], config('app.timezone'));
} catch (InvalidFormatException $e) {
Log::error(sprintf('Could not parse date "%s" in %s: %s', $transaction['date'], __METHOD__, $e->getMessage()));
}
if (false === $day) {
$day = today(config('app.timezone'));
}
$format = $day->format('Y-m-d');
// if the transaction is in the expected currency, change nothing.
if ((int) $transaction['transaction_currency_id'] === $native->id) {
// change the current balance, set it to today, continue the loop.
$currentBalance = bcadd($currentBalance, $transaction['amount']);
$balances[$format] = $currentBalance;
Log::debug(sprintf('%s: transaction in %s, new balance is %s.', $format, $native->code, $currentBalance));
continue;
}
// if foreign currency is in the expected currency, do nothing:
if ((int) $transaction['foreign_currency_id'] === $native->id) {
$currentBalance = bcadd($currentBalance, $transaction['foreign_amount']);
$balances[$format] = $currentBalance;
Log::debug(sprintf('%s: transaction in %s (foreign), new balance is %s.', $format, $native->code, $currentBalance));
continue;
}
// otherwise, convert 'amount' to the necessary currency:
$currencyId = (int) $transaction['transaction_currency_id'];
$currency = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$currencies[$currencyId] = $currency;
$rate = $converter->getCurrencyRate($currency, $native, $day);
$convertedAmount = bcmul($transaction['amount'], $rate);
$currentBalance = bcadd($currentBalance, $convertedAmount);
$balances[$format] = $currentBalance;
Log::debug(sprintf(
'%s: transaction in %s(!). Conversion rate is %s. %s %s = %s %s',
$format,
$currency->code,
$rate,
$currency->code,
$transaction['amount'],
$native->code,
$convertedAmount
));
}
$cache->store($balances);
$converter->summarize();
return $balances;
}
/**
* @throws FireflyException
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @deprecated
* selection of transactions
* 1: all normal transactions. No foreign currency info. In $currency. Need conversion.
* 2: all normal transactions. No foreign currency info. In $native. Need NO conversion.
* 3: all normal transactions. No foreign currency info. In neither currency. Need conversion.
* Then, select everything with foreign currency info:
* 4. All transactions with foreign currency info in $native. Normal currency value is ignored. Do not need
* conversion.
* 5. All transactions with foreign currency info NOT in $native, but currency info in $currency. Need conversion.
* 6. All transactions with foreign currency info NOT in $native, and currency info NOT in $currency. Need
* conversion.
*
* Gets balance at the end of current month by default. Returns the balance converted
* to the indicated currency ($native).
*
*/
private function balanceConverted(Account $account, Carbon $date, TransactionCurrency $native): string
{
Log::debug(sprintf('Now in balanceConverted (%s) for account #%d, converting to %s', $date->format('Y-m-d'), $account->id, $native->code));
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-converted');
$cache->addProperty($date);
$cache->addProperty($native->id);
if ($cache->has()) {
Log::debug('Cached!');
// return $cache->get(); // return $cache->get();
} }
/** @var AccountRepositoryInterface $repository */ $balances = [];
$repository = app(AccountRepositoryInterface::class); $formatted = $start->format('Y-m-d');
$currency = $repository->getAccountCurrency($account); $startBalance = $this->finalAccountBalance($account, $start);
$currency = null === $currency ? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup) : $currency; $defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
if ($native->id === $currency->id) { $currency = $this->getAccountCurrency($account) ?? $defaultCurrency;
Log::debug('No conversion necessary!'); $currencies = [
$currency->id => $currency,
return $this->balance($account, $date); $defaultCurrency->id => $defaultCurrency,
} ];
$startBalance[$defaultCurrency->code] ??= '0';
$new = []; $startBalance[$currency->code] ??= '0';
$existing = []; $balances[$formatted] = $startBalance;
$new[] = $account->transactions() // 1
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currency->id)
->whereNull('transactions.foreign_currency_id')
->get(['transaction_journals.date', 'transactions.amount'])->toArray();
Log::debug(sprintf('%d transaction(s) in set #1', count($new[0])));
$existing[] = $account->transactions() // 2
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $native->id)
->whereNull('transactions.foreign_currency_id')
->get(['transactions.amount'])->toArray();
Log::debug(sprintf('%d transaction(s) in set #2', count($existing[0])));
$new[] = $account->transactions() // 3
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', '!=', $currency->id)
->where('transactions.transaction_currency_id', '!=', $native->id)
->whereNull('transactions.foreign_currency_id')
->get(['transaction_journals.date', 'transactions.amount'])->toArray();
Log::debug(sprintf('%d transactions in set #3', count($new[1])));
$existing[] = $account->transactions() // 4
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $native->id)
->whereNotNull('transactions.foreign_amount')
->get(['transactions.foreign_amount'])->toArray();
Log::debug(sprintf('%d transactions in set #4', count($existing[1])));
$new[] = $account->transactions()// 5
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currency->id)
->where('transactions.foreign_currency_id', '!=', $native->id)
->whereNotNull('transactions.foreign_amount')
->get(['transaction_journals.date', 'transactions.amount'])->toArray();
Log::debug(sprintf('%d transactions in set #5', count($new[2])));
$new[] = $account->transactions()// 6
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', '!=', $currency->id)
->where('transactions.foreign_currency_id', '!=', $native->id)
->whereNotNull('transactions.foreign_amount')
->get(['transaction_journals.date', 'transactions.amount'])->toArray();
Log::debug(sprintf('%d transactions in set #6', count($new[3])));
// process both sets of transactions. Of course, no need to convert set "existing".
$balance = $this->sumTransactions($existing[0], 'amount');
$balance = bcadd($balance, $this->sumTransactions($existing[1], 'foreign_amount'));
Log::debug(sprintf('Balance from set #2 and #4 is %f', $balance));
// need to convert the others. All sets use the "amount" value as their base (that's easy)
// but we need to convert each transaction separately because the date difference may
// incur huge currency changes.
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
$start = clone $date;
$end = clone $date;
$converter = new ExchangeRateConverter();
foreach ($new as $set) {
foreach ($set as $transaction) {
$currentDate = false;
try {
$currentDate = Carbon::parse($transaction['date'], config('app.timezone'));
} catch (InvalidFormatException $e) {
Log::error(sprintf('Could not parse date "%s" in %s', $transaction['date'], __METHOD__));
}
if (false === $currentDate) {
$currentDate = today(config('app.timezone'));
}
if ($currentDate->lte($start)) {
$start = clone $currentDate;
}
}
}
unset($currentDate);
$converter->prepare($currency, $native, $start, $end);
foreach ($new as $set) {
foreach ($set as $transaction) {
$currentDate = false;
try {
$currentDate = Carbon::parse($transaction['date'], config('app.timezone'));
} catch (InvalidFormatException $e) {
Log::error(sprintf('Could not parse date "%s" in %s', $transaction['date'], __METHOD__));
}
if (false === $currentDate) {
$currentDate = today(config('app.timezone'));
}
$rate = $converter->getCurrencyRate($currency, $native, $currentDate);
$convertedAmount = bcmul($transaction['amount'], $rate);
$balance = bcadd($balance, $convertedAmount);
}
}
// add virtual balance (also needs conversion)
$virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance;
$virtual = $converter->convert($currency, $native, $account->created_at, $virtual);
$balance = bcadd($balance, $virtual);
$converter->summarize();
$cache->store($balance);
$converter->summarize();
return $balance;
}
//
/**
* Gets balance at the end of current month by default
*
* @throws FireflyException
* @deprecated
*/
private function balance(Account $account, Carbon $date, ?TransactionCurrency $currency = null): string
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
// abuse chart properties:
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance');
$cache->addProperty($date);
$cache->addProperty(null !== $currency ? $currency->id : 0);
if ($cache->has()) {
return $cache->get();
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
if (null === $currency) {
$currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
}
// first part: get all balances in own currency:
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.transaction_currency_id', $currency->id)
->get(['transactions.amount'])->toArray();
$nativeBalance = $this->sumTransactions($transactions, 'amount');
// get all balances in foreign currency:
$transactions = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $currency->id)
->where('transactions.transaction_currency_id', '!=', $currency->id)
->get(['transactions.foreign_amount'])->toArray();
$foreignBalance = $this->sumTransactions($transactions, 'foreign_amount');
$balance = bcadd($nativeBalance, $foreignBalance);
$virtual = null === $account->virtual_balance ? '0' : $account->virtual_balance;
$balance = bcadd($balance, $virtual);
$cache->store($balance);
return $balance;
}
/**
* @throws FireflyException
* @deprecated
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
public function balanceInRangeNative(Account $account, Carbon $start, Carbon $end): array
{
$native = app('amount')->getDefaultCurrency();
Log::debug(sprintf('balanceInRangeNative for account #%d, to %s', $account->id, $native->code));
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($account->user);
$currency = $repository->getAccountCurrency($account) ?? $native;
if ($native->id === $currency->id) {
Log::debug('No need to get native balance, account prefers this currency.');
return $this->balanceInRange($account, $start, $end, $native);
}
$cache = new CacheProperties(); // sums up the balance changes per day, for foreign, native and normal amounts.
$cache->addProperty($account->id);
$cache->addProperty('balance-in-range-native');
$cache->addProperty($native->id);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
$value = $cache->get();
Log::debug('Return cached values');
//return $value;
}
$start->subDay();
$end->addDay();
$balances = [];
$formatted = $start->format('Y-m-d');
$startBalance = $this->balanceNative($account, $start); // already converted to native amount
$balances[$formatted] = $startBalance;
Log::debug(sprintf('Start balance on %s is %s', $formatted, $startBalance));
// not sure why this is happening:
$start->addDay();
// grab all transactions between start and end:
$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 00:00:00')) ->where('transaction_journals.date', '>=', $start->format('Y-m-d H:i:s'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59')) ->where('transaction_journals.date', '<=', $end->format('Y-m-d H:i:s'))
->orderBy('transaction_journals.date', 'ASC')
->whereNull('transaction_journals.deleted_at')
->get(
[
'transaction_journals.date',
'transactions.transaction_currency_id',
'transactions.amount',
'transactions.native_amount',
'transactions.foreign_currency_id',
'transactions.foreign_amount',
]
)->toArray();
// loop the set
$currentBalance = $startBalance;
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$day = false;
try {
$day = Carbon::parse($transaction['date'], config('app.timezone'));
} catch (InvalidFormatException $e) {
Log::error(sprintf('Could not parse date "%s" in %s: %s', $transaction['date'], __METHOD__, $e->getMessage()));
}
if (false === $day) {
$day = today(config('app.timezone'));
}
$format = $day->format('Y-m-d');
// first, check the native amount. If not NULL, add it, and continue.
if (null !== $transaction['native_amount']) {
$currentBalance = bcadd($currentBalance, $transaction['native_amount']);
$balances[$format] = $currentBalance;
Log::debug(sprintf('%s: transaction in %s (native), new balance is %s.', $format, $native->code, $currentBalance));
continue;
}
// if the foreign amount is in the native currency, add it and continue.
if ((int) $transaction['foreign_currency_id'] === $native->id) {
$currentBalance = bcadd($currentBalance, $transaction['foreign_amount']);
$balances[$format] = $currentBalance;
Log::debug(sprintf('%s: transaction in %s (foreign), new balance is %s.', $format, $native->code, $currentBalance));
continue;
}
// anything else is added as is. Warning in logs.
Log::warning(sprintf('Account "%s" (#%d) has transactions that are not converted in the native currency. Please run "php artisan firefly-iii:recalculate-native-amounts"', $account->name, $account->id));
$currentBalance = bcadd($currentBalance, $transaction['amount']);
$balances[$format] = $currentBalance;
Log::debug(sprintf('%s: transaction BAD currency, new balance is %s.', $format, $currentBalance));
}
$cache->store($balances);
return $balances;
}
/**
* Gets the balance for the given account during the whole range, using this format:.
*
* [yyyy-mm-dd] => 123,2
*
* @throws FireflyException
* @deprecated
*/
public function balanceInRange(Account $account, Carbon $start, Carbon $end, ?TransactionCurrency $currency = null): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-in-range');
$cache->addProperty(null !== $currency ? $currency->id : 0);
$cache->addProperty($start);
$cache->addProperty($end);
if ($cache->has()) {
return $cache->get();
}
$start->subDay();
$end->addDay();
$balances = [];
$formatted = $start->format('Y-m-d');
$startBalance = $this->balance($account, $start, $currency);
$balances[$formatted] = $startBalance;
if (null === $currency) {
$repository = app(AccountRepositoryInterface::class);
$repository->setUser($account->user);
$currency = $repository->getAccountCurrency($account) ?? app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
}
$currencyId = $currency->id;
$start->addDay();
// query!
$set = $account->transactions()
->leftJoin('transaction_journals', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 23:59:59'))
->groupBy('transaction_journals.date') ->groupBy('transaction_journals.date')
->groupBy('transactions.transaction_currency_id') ->groupBy('transactions.transaction_currency_id')
->groupBy('transactions.foreign_currency_id') ->groupBy('transactions.foreign_currency_id')
@@ -518,6 +120,7 @@ class Steam
DB::raw('SUM(transactions.amount) AS modified'), DB::raw('SUM(transactions.amount) AS modified'),
'transactions.foreign_currency_id', 'transactions.foreign_currency_id',
DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'), DB::raw('SUM(transactions.foreign_amount) AS modified_foreign'),
DB::raw('SUM(transactions.native_amount) AS modified_native'),
] ]
); );
@@ -525,235 +128,43 @@ class Steam
/** @var Transaction $entry */ /** @var Transaction $entry */
foreach ($set as $entry) { foreach ($set as $entry) {
// normal amount and foreign amount
// normal, native and foreign amount
$carbon = new Carbon($entry->date, $entry->date_tz);
$modified = (string) (null === $entry->modified ? '0' : $entry->modified); $modified = (string) (null === $entry->modified ? '0' : $entry->modified);
$foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign); $foreignModified = (string) (null === $entry->modified_foreign ? '0' : $entry->modified_foreign);
$amount = '0'; $nativeModified = (string) (null === $entry->modified_native ? '0' : $entry->modified_native);
if ($currencyId === (int) $entry->transaction_currency_id || 0 === $currencyId) {
// use normal amount:
$amount = $modified;
}
if ($currencyId === (int) $entry->foreign_currency_id) {
// use foreign amount:
$amount = $foreignModified;
}
// Log::debug(sprintf('Trying to add %s and %s.', var_export($currentBalance, true), var_export($amount, true)));
$currentBalance = bcadd($currentBalance, $amount);
$carbon = new Carbon($entry->date, config('app.timezone'));
$date = $carbon->format('Y-m-d');
$balances[$date] = $currentBalance;
}
// add "modified" to amount if the currency id matches the account currency id.
if ($entry->transaction_currency_id === $currency->id) {
$currentBalance['balance'] = bcadd($currentBalance['balance'], $modified);
$currentBalance[$currency->code] = bcadd($currentBalance[$currency->code], $modified);
}
// always add the native balance, even if it ends up at zero.
$currentBalance['native_balance'] = bcadd($currentBalance['native_balance'], $nativeModified);
// add modified foreign to the array
if (null !== $entry->foreign_currency_id) {
$foreignId = $entry->foreign_currency_id;
$currencies[$foreignId] ??= TransactionCurrency::find($foreignId);
$foreignCurrency = $currencies[$foreignId];
$currentBalance[$foreignCurrency->code] ??= '0';
$currentBalance[$foreignCurrency->code] = bcadd($currentBalance[$foreignCurrency->code], $foreignModified);
}
$balances[$carbon->format('Y-m-d')] = $currentBalance;
}
$cache->store($balances); $cache->store($balances);
return $balances; return $balances;
} }
/** public function finalAccountsBalance(Collection $accounts, Carbon $date): array {
* Balance of an (asset) account in the user's native currency. $balances = [];
* Is calculated by summing up three numbers.
*
* - Transactions in foreign amount that happen to be in the native currency.
* - The rest of the transactions in the native currency.
* - Where both are zero or NULL, the normal amount converted (and stored!)
*
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @deprecated
*/
private function balanceNative(Account $account, Carbon $date): string
{
$native = app('amount')->getDefaultCurrency();
Log::debug(sprintf('Now in balanceNative (%s) for account #%d, converting to %s', $date->format('Y-m-d'), $account->id, $native->code));
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-native');
$cache->addProperty($date);
$cache->addProperty($native->id);
if ($cache->has()) {
$value = $cache->get();
Log::debug(sprintf('Return cached value %s', $value));
return $value;
}
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$currency = $repository->getAccountCurrency($account);
$currency = null === $currency ? $native : $currency;
if ($native->id === $currency->id) {
Log::debug('No conversion necessary!');
return $this->balance($account, $date);
}
$balance = '0';
// transactions in foreign amount that happen to be in the native currency:
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->where('transactions.foreign_currency_id', $native->id)
->get(['transactions.foreign_amount'])->toArray();
$balance = bcadd($this->sumTransactions($set, 'foreign_amount'), $balance);
Log::debug(sprintf('The balance is now %s', $balance));
// transactions in the native amount.
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->whereNull('transactions.foreign_currency_id')
->whereNotNull('transactions.native_amount')
->get(['transactions.native_amount'])->toArray();
$balance = bcadd($this->sumTransactions($set, 'native_amount'), $balance);
Log::debug(sprintf('The balance is now %s', $balance));
// transactions in the normal amount with no native amount set.
$set = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->whereNull('transactions.foreign_currency_id')
->whereNull('transactions.native_amount')
->get(['transactions.amount'])->toArray();
$balance = bcadd($this->sumTransactions($set, 'amount'), $balance);
Log::debug(sprintf('The balance is now %s', $balance));
// add virtual balance (also needs conversion)
$virtualNative = null === $account->native_virtual_balance ? '0' : $account->native_virtual_balance;
$final = bcadd($virtualNative, $balance);
Log::debug(sprintf('Final balance is %s', $final));
$cache->store($final);
return $final;
}
/**
* This method always ignores the virtual balance.
*
* @throws FireflyException
* @deprecated
*/
public function balancesByAccounts(Collection $accounts, Carbon $date): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$ids = $accounts->pluck('id')->toArray();
// cache this property.
$cache = new CacheProperties();
$cache->addProperty($ids);
$cache->addProperty('balances');
$cache->addProperty($date);
if ($cache->has()) {
return $cache->get();
}
// need to do this per account.
$result = [];
/** @var Account $account */
foreach ($accounts as $account) { foreach ($accounts as $account) {
$result[$account->id] = $this->balance($account, $date); $balances[$account->id] = $this->finalAccountBalance($account, $date);
} }
return $balances;
$cache->store($result);
return $result;
}
/**
* This method always ignores the virtual balance.
*
* @throws FireflyException
* @deprecated
*/
public function balancesByAccountsConverted(Collection $accounts, Carbon $date): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$ids = $accounts->pluck('id')->toArray();
// cache this property.
$cache = new CacheProperties();
$cache->addProperty($ids);
$cache->addProperty('balances-converted');
$cache->addProperty($date);
if ($cache->has()) {
return $cache->get();
}
// need to do this per account.
$result = [];
/** @var Account $account */
foreach ($accounts as $account) {
$default = app('amount')->getDefaultCurrencyByUserGroup($account->user->userGroup);
$result[$account->id]
= [
'balance' => $this->balance($account, $date),
'native_balance' => $this->balanceConverted($account, $date, $default),
];
}
$cache->store($result);
return $result;
}
/**
* Same as above, but also groups per currency.
*
* @deprecated
*/
public function balancesPerCurrencyByAccounts(Collection $accounts, Carbon $date): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
$ids = $accounts->pluck('id')->toArray();
// cache this property.
$cache = new CacheProperties();
$cache->addProperty($ids);
$cache->addProperty('balances-per-currency');
$cache->addProperty($date);
if ($cache->has()) {
return $cache->get();
}
// need to do this per account.
$result = [];
/** @var Account $account */
foreach ($accounts as $account) {
$result[$account->id] = $this->balancePerCurrency($account, $date);
}
$cache->store($result);
return $result;
}
/**
* @param Account $account
* @param Carbon $date
*
* @return array
* @deprecated
*/
private function balancePerCurrency(Account $account, Carbon $date): array
{
// Log::warning(sprintf('Deprecated method %s, do not use.', __METHOD__));
// abuse chart properties:
$cache = new CacheProperties();
$cache->addProperty($account->id);
$cache->addProperty('balance-per-currency');
$cache->addProperty($date);
if ($cache->has()) {
return $cache->get();
}
$query = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->where('transaction_journals.date', '<=', $date->format('Y-m-d 23:59:59'))
->groupBy('transactions.transaction_currency_id');
$balances = $query->get(['transactions.transaction_currency_id', DB::raw('SUM(transactions.amount) as sum_for_currency')]); // @phpstan-ignore-line
$return = [];
/** @var stdClass $entry */
foreach ($balances as $entry) {
$return[(int) $entry->transaction_currency_id] = (string) $entry->sum_for_currency;
}
$cache->store($return);
return $return;
} }
/** /**
@@ -899,36 +310,6 @@ class Steam
return array_merge($return, $others); return array_merge($return, $others);
} }
public function getAccountCurrency(Account $account): ?TransactionCurrency
{
$type = $account->accountType->type;
$list = config('firefly.valid_currency_account_types');
// return null if not in this list.
if (!in_array($type, $list, true)) {
return null;
}
$result = $account->accountMeta->where('name', 'currency_id')->first();
if (null === $result) {
return null;
}
return TransactionCurrency::find((int) $result->data);
}
private function sumTransactions(array $transactions, string $key): string
{
$sum = '0';
/** @var array $transaction */
foreach ($transactions as $transaction) {
$value = (string) ($transaction[$key] ?? '0');
$value = '' === $value ? '0' : $value;
$sum = bcadd($sum, $value);
}
return $sum;
}
private function groupAndSumTransactions(array $array, string $group, string $field): array private function groupAndSumTransactions(array $array, string $group, string $field): array
{ {
$return = []; $return = [];

View File

@@ -24,6 +24,8 @@ declare(strict_types=1);
namespace FireflyIII\Support\System; namespace FireflyIII\Support\System;
use Artisan;
use Crypt;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use Illuminate\Contracts\Encryption\DecryptException; use Illuminate\Contracts\Encryption\DecryptException;
use Laravel\Passport\Console\KeysCommand; use Laravel\Passport\Console\KeysCommand;
@@ -65,7 +67,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());
} }
@@ -87,16 +89,16 @@ class OAuthKeys
public static function generateKeys(): void public static function generateKeys(): void
{ {
\Artisan::registerCommand(new KeysCommand()); Artisan::registerCommand(new KeysCommand());
\Artisan::call('firefly-iii:laravel-passport-keys'); Artisan::call('firefly-iii:laravel-passport-keys');
} }
public static function storeKeysInDB(): void public static function storeKeysInDB(): void
{ {
$private = storage_path('oauth-private.key'); $private = storage_path('oauth-private.key');
$public = storage_path('oauth-public.key'); $public = storage_path('oauth-public.key');
app('fireflyconfig')->set(self::PRIVATE_KEY, \Crypt::encrypt(file_get_contents($private))); app('fireflyconfig')->set(self::PRIVATE_KEY, Crypt::encrypt(file_get_contents($private)));
app('fireflyconfig')->set(self::PUBLIC_KEY, \Crypt::encrypt(file_get_contents($public))); app('fireflyconfig')->set(self::PUBLIC_KEY, Crypt::encrypt(file_get_contents($public)));
} }
/** /**
@@ -108,8 +110,8 @@ class OAuthKeys
$publicKey = (string) app('fireflyconfig')->get(self::PUBLIC_KEY)?->data; $publicKey = (string) app('fireflyconfig')->get(self::PUBLIC_KEY)?->data;
try { try {
$privateContent = \Crypt::decrypt($privateKey); $privateContent = Crypt::decrypt($privateKey);
$publicContent = \Crypt::decrypt($publicKey); $publicContent = Crypt::decrypt($publicKey);
} catch (DecryptException $e) { } catch (DecryptException $e) {
app('log')->error('Could not decrypt pub/private keypair.'); app('log')->error('Could not decrypt pub/private keypair.');
app('log')->error($e->getMessage()); app('log')->error($e->getMessage());
@@ -120,8 +122,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

@@ -108,8 +108,8 @@ class AmountFormat extends AbstractExtension
return new TwigFunction( return new TwigFunction(
'formatAmountBySymbol', 'formatAmountBySymbol',
static function (string $amount, string $symbol, ?int $decimalPlaces = null, ?bool $coloured = null): string { static function (string $amount, string $symbol, ?int $decimalPlaces = null, ?bool $coloured = null): string {
$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

@@ -82,7 +82,7 @@ class General extends AbstractExtension
} }
if ('native_balance' === $key) { if ('native_balance' === $key) {
// balance in native currency. // balance in native currency.
if($convertToNative) { if ($convertToNative) {
$strings[] = app('amount')->formatAnything($native, $balance, false); $strings[] = app('amount')->formatAnything($native, $balance, false);
} }
continue; continue;
@@ -282,7 +282,7 @@ class General extends AbstractExtension
$args = func_get_args(); $args = func_get_args();
$route = $args[0]; // name of the route. $route = $args[0]; // name of the route.
if (\Route::getCurrentRoute()->getName() === $route) { if (Route::getCurrentRoute()->getName() === $route) {
return 'active'; return 'active';
} }
@@ -302,7 +302,7 @@ class General extends AbstractExtension
static function (): string { static function (): string {
$args = func_get_args(); $args = func_get_args();
$route = $args[0]; // name of the route. $route = $args[0]; // name of the route.
$name = \Route::getCurrentRoute()->getName() ?? ''; $name = Route::getCurrentRoute()->getName() ?? '';
if (str_contains($name, $route)) { if (str_contains($name, $route)) {
return 'active'; return 'active';
} }
@@ -326,7 +326,7 @@ class General extends AbstractExtension
if ($objectType === $activeObjectType if ($objectType === $activeObjectType
&& false !== stripos( && false !== stripos(
\Route::getCurrentRoute()->getName(), Route::getCurrentRoute()->getName(),
$route $route
)) { )) {
return 'active'; return 'active';
@@ -349,7 +349,7 @@ class General extends AbstractExtension
static function (): string { static function (): string {
$args = func_get_args(); $args = func_get_args();
$route = $args[0]; // name of the route. $route = $args[0]; // name of the route.
$name = \Route::getCurrentRoute()->getName() ?? ''; $name = Route::getCurrentRoute()->getName() ?? '';
if (str_contains($name, $route)) { if (str_contains($name, $route)) {
return 'menu-open'; return 'menu-open';
} }

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Support\Twig; namespace FireflyIII\Support\Twig;
use Config;
use Twig\Extension\AbstractExtension; use Twig\Extension\AbstractExtension;
use Twig\TwigFunction; use Twig\TwigFunction;
@@ -63,7 +64,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);
@@ -80,10 +81,10 @@ class Rule extends AbstractExtension
'allRuleActions', 'allRuleActions',
static function () { static function () {
// array of valid values for actions // array of valid values for actions
$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);

View File

@@ -25,6 +25,7 @@ declare(strict_types=1);
namespace FireflyIII\Support\Twig; namespace FireflyIII\Support\Twig;
use Carbon\Carbon; use Carbon\Carbon;
use DB;
use FireflyIII\Models\AccountType; use FireflyIII\Models\AccountType;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
@@ -86,7 +87,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 (TransactionType::TRANSFER === $type) { if (TransactionType::TRANSFER === $type) {
$result = sprintf('<span class="text-info money-transfer">%s</span>', $result); $result = sprintf('<span class="text-info money-transfer">%s</span>', $result);
} }
@@ -119,9 +120,9 @@ class TransactionGroupTwig extends AbstractExtension
*/ */
private function foreignJournalArrayAmount(array $array): string private function foreignJournalArrayAmount(array $array): string
{ {
$type = $array['transaction_type_type'] ?? TransactionType::WITHDRAWAL; $type = $array['transaction_type_type'] ?? TransactionType::WITHDRAWAL;
$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);
@@ -129,7 +130,7 @@ class TransactionGroupTwig extends AbstractExtension
if (TransactionType::TRANSFER === $type) { if (TransactionType::TRANSFER === $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 (TransactionType::TRANSFER === $type) { if (TransactionType::TRANSFER === $type) {
$result = sprintf('<span class="text-info money-transfer">%s</span>', $result); $result = sprintf('<span class="text-info money-transfer">%s</span>', $result);
} }
@@ -170,12 +171,12 @@ class TransactionGroupTwig extends AbstractExtension
$colored = true; $colored = true;
$sourceType = $first->account()->first()->accountType()->first()->type; $sourceType = $first->account()->first()->accountType()->first()->type;
$amount = $this->signAmount($amount, $type, $sourceType); $amount = $this->signAmount($amount, $type, $sourceType);
if (TransactionType::TRANSFER === $type) { if (TransactionType::TRANSFER === $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 (TransactionType::TRANSFER === $type) { if (TransactionType::TRANSFER === $type) {
$result = sprintf('<span class="text-info money-transfer">%s</span>', $result); $result = sprintf('<span class="text-info money-transfer">%s</span>', $result);
} }
@@ -196,7 +197,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();
@@ -205,12 +206,12 @@ class TransactionGroupTwig extends AbstractExtension
$colored = true; $colored = true;
$sourceType = $first->account()->first()->accountType()->first()->type; $sourceType = $first->account()->first()->accountType()->first()->type;
$amount = $this->signAmount($amount, $type, $sourceType); $amount = $this->signAmount($amount, $type, $sourceType);
if (TransactionType::TRANSFER === $type) { if (TransactionType::TRANSFER === $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 (TransactionType::TRANSFER === $type) { if (TransactionType::TRANSFER === $type) {
$result = sprintf('<span class="text-info money-transfer">%s</span>', $result); $result = sprintf('<span class="text-info money-transfer">%s</span>', $result);
} }
@@ -223,12 +224,11 @@ class TransactionGroupTwig extends AbstractExtension
return new TwigFunction( return new TwigFunction(
'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;
} }
@@ -241,12 +241,11 @@ class TransactionGroupTwig extends AbstractExtension
'journalGetMetaDate', 'journalGetMetaDate',
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'));
} }
@@ -262,12 +261,11 @@ class TransactionGroupTwig extends AbstractExtension
'journalGetMetaField', 'journalGetMetaField',
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 '';
} }

View File

@@ -115,7 +115,7 @@ class AccountTransformer extends AbstractTransformer
private function getMetaBalances(Collection $accounts): void private function getMetaBalances(Collection $accounts): void
{ {
try { try {
$this->convertedBalances = app('steam')->balancesByAccountsConverted($accounts, $this->getDate()); $this->convertedBalances = app('steam')->finalAccountsBalance($accounts, $this->getDate());
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
} }
@@ -172,14 +172,15 @@ class AccountTransformer extends AbstractTransformer
private function getBalanceDifference(Collection $accounts, Carbon $start, Carbon $end): void private function getBalanceDifference(Collection $accounts, Carbon $start, Carbon $end): void
{ {
throw new FireflyException('Used deprecated method, rethink this.');
// collect balances, start and end for both native and converted. // collect balances, start and end for both native and converted.
// yes the b is usually used for boolean by idiots but here it's for balance. // yes the b is usually used for boolean by idiots but here it's for balance.
$bStart = []; $bStart = [];
$bEnd = []; $bEnd = [];
try { try {
$bStart = app('steam')->balancesByAccountsConverted($accounts, $start); $bStart = app('steam')->finalAccountsBalance($accounts, $start);
$bEnd = app('steam')->balancesByAccountsConverted($accounts, $end); $bEnd = app('steam')->finalAccountsBalance($accounts, $end);
} catch (FireflyException $e) { } catch (FireflyException $e) {
Log::error($e->getMessage()); Log::error($e->getMessage());
} }