PHPStorm can order methods by alphabet, who knew.

This commit is contained in:
James Cole
2024-02-22 20:11:09 +01:00
parent f9d4a43e05
commit 68c9c4ec3c
221 changed files with 5840 additions and 5843 deletions

View File

@@ -63,7 +63,7 @@ class Amount
$fmt->setSymbol(\NumberFormatter::CURRENCY_SYMBOL, $symbol);
$fmt->setAttribute(\NumberFormatter::MIN_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 (1 === bccomp($rounded, '0')) {
@@ -157,6 +157,41 @@ class Amount
];
}
/**
* @throws FireflyException
*
* @SuppressWarnings(PHPMD.MissingImport)
*/
private function getLocaleInfo(): array
{
// get config from preference, not from translation:
$locale = app('steam')->getLocale();
$array = app('steam')->getLocaleArray($locale);
setlocale(LC_MONETARY, $array);
$info = localeconv();
// correct variables
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_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['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space');
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$info['mon_decimal_point'] = $fmt->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL);
$info['mon_thousands_sep'] = $fmt->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL);
return $info;
}
private function getLocaleField(array $info, string $field): bool
{
return (is_bool($info[$field]) && true === $info[$field])
|| (is_int($info[$field]) && 1 === $info[$field]);
}
/**
* bool $sepBySpace is $localeconv['n_sep_by_space']
* int $signPosn = $localeconv['n_sign_posn']
@@ -230,39 +265,4 @@ class Amount
return $format;
}
/**
* @throws FireflyException
*
* @SuppressWarnings(PHPMD.MissingImport)
*/
private function getLocaleInfo(): array
{
// get config from preference, not from translation:
$locale = app('steam')->getLocale();
$array = app('steam')->getLocaleArray($locale);
setlocale(LC_MONETARY, $array);
$info = localeconv();
// correct variables
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_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['p_sep_by_space'] = $this->getLocaleField($info, 'p_sep_by_space');
$fmt = new \NumberFormatter($locale, \NumberFormatter::CURRENCY);
$info['mon_decimal_point'] = $fmt->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL);
$info['mon_thousands_sep'] = $fmt->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL);
return $info;
}
private function getLocaleField(array $info, string $field): bool
{
return (is_bool($info[$field]) && true === $info[$field])
|| (is_int($info[$field]) && 1 === $info[$field]);
}
}

View File

@@ -38,7 +38,7 @@ class UserGroupTransaction implements BinderInterface
if (auth()->check()) {
/** @var User $user */
$user = auth()->user();
$group = TransactionGroup::where('id', (int) $value)
$group = TransactionGroup::where('id', (int)$value)
->where('user_group_id', $user->user_group_id)
->first()
;

View File

@@ -73,14 +73,6 @@ class CacheProperties
return \Cache::has($this->hash);
}
/**
* @param mixed $data
*/
public function store($data): void
{
\Cache::forever($this->hash, $data);
}
private function hash(): void
{
$content = '';
@@ -94,4 +86,12 @@ class CacheProperties
}
$this->hash = substr(hash('sha256', $content), 0, 16);
}
/**
* @param mixed $data
*/
public function store($data): void
{
\Cache::forever($this->hash, $data);
}
}

View File

@@ -34,7 +34,7 @@ class Calculator
{
public const int DEFAULT_INTERVAL = 1;
private static ?\SplObjectStorage $intervalMap = null;
private static array $intervals = [];
private static array $intervals = [];
/**
* @throws IntervalException

View File

@@ -78,29 +78,6 @@ class FrontpageChartGenerator
return $data;
}
public function setEnd(Carbon $end): void
{
$this->end = $end;
}
public function setStart(Carbon $start): void
{
$this->start = $start;
}
/**
* A basic setter for the user. Also updates the repositories with the right user.
*/
public function setUser(User $user): void
{
$this->budgetRepository->setUser($user);
$this->blRepository->setUser($user);
$this->opsRepository->setUser($user);
$locale = app('steam')->getLocale();
$this->monthAndDayFormat = (string)trans('config.month_and_day_js', [], $locale);
}
/**
* For each budget, gets all budget limits for the current time range.
* When no limits are present, the time range is used to collect information on money spent.
@@ -196,4 +173,27 @@ class FrontpageChartGenerator
return $data;
}
public function setEnd(Carbon $end): void
{
$this->end = $end;
}
public function setStart(Carbon $start): void
{
$this->start = $start;
}
/**
* A basic setter for the user. Also updates the repositories with the right user.
*/
public function setUser(User $user): void
{
$this->budgetRepository->setUser($user);
$this->blRepository->setUser($user);
$this->opsRepository->setUser($user);
$locale = app('steam')->getLocale();
$this->monthAndDayFormat = (string)trans('config.month_and_day_js', [], $locale);
}
}

View File

@@ -134,87 +134,6 @@ class ExportDataGenerator
return $return;
}
public function setUser(User $user): void
{
$this->user = $user;
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function get(string $key, mixed $default = null): mixed
{
return null;
}
public function setAccounts(Collection $accounts): void
{
$this->accounts = $accounts;
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function has(mixed $key): mixed
{
return null;
}
public function setEnd(Carbon $end): void
{
$this->end = $end;
}
public function setExportAccounts(bool $exportAccounts): void
{
$this->exportAccounts = $exportAccounts;
}
public function setExportBills(bool $exportBills): void
{
$this->exportBills = $exportBills;
}
public function setExportBudgets(bool $exportBudgets): void
{
$this->exportBudgets = $exportBudgets;
}
public function setExportCategories(bool $exportCategories): void
{
$this->exportCategories = $exportCategories;
}
public function setExportPiggies(bool $exportPiggies): void
{
$this->exportPiggies = $exportPiggies;
}
public function setExportRecurring(bool $exportRecurring): void
{
$this->exportRecurring = $exportRecurring;
}
public function setExportRules(bool $exportRules): void
{
$this->exportRules = $exportRules;
}
public function setExportTags(bool $exportTags): void
{
$this->exportTags = $exportTags;
}
public function setExportTransactions(bool $exportTransactions): void
{
$this->exportTransactions = $exportTransactions;
}
public function setStart(Carbon $start): void
{
$this->start = $start;
}
/**
* @throws CannotInsertRecord
* @throws Exception
@@ -296,6 +215,11 @@ class ExportDataGenerator
return $string;
}
public function setUser(User $user): void
{
$this->user = $user;
}
/**
* @throws CannotInsertRecord
* @throws Exception
@@ -788,6 +712,14 @@ class ExportDataGenerator
return $string;
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function get(string $key, mixed $default = null): mixed
{
return null;
}
/**
* @throws CannotInsertRecord
* @throws Exception
@@ -864,6 +796,11 @@ class ExportDataGenerator
return $string;
}
public function setAccounts(Collection $accounts): void
{
$this->accounts = $accounts;
}
private function mergeTags(array $tags): string
{
if (0 === count($tags)) {
@@ -876,4 +813,67 @@ class ExportDataGenerator
return implode(',', $smol);
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function has(mixed $key): mixed
{
return null;
}
public function setEnd(Carbon $end): void
{
$this->end = $end;
}
public function setExportAccounts(bool $exportAccounts): void
{
$this->exportAccounts = $exportAccounts;
}
public function setExportBills(bool $exportBills): void
{
$this->exportBills = $exportBills;
}
public function setExportBudgets(bool $exportBudgets): void
{
$this->exportBudgets = $exportBudgets;
}
public function setExportCategories(bool $exportCategories): void
{
$this->exportCategories = $exportCategories;
}
public function setExportPiggies(bool $exportPiggies): void
{
$this->exportPiggies = $exportPiggies;
}
public function setExportRecurring(bool $exportRecurring): void
{
$this->exportRecurring = $exportRecurring;
}
public function setExportRules(bool $exportRules): void
{
$this->exportRules = $exportRules;
}
public function setExportTags(bool $exportTags): void
{
$this->exportTags = $exportTags;
}
public function setExportTransactions(bool $exportTransactions): void
{
$this->exportTransactions = $exportTransactions;
}
public function setStart(Carbon $start): void
{
$this->start = $start;
}
}

View File

@@ -55,6 +55,37 @@ class AccountForm
return $this->select($name, $grouped, $value, $options);
}
private function getAccountsGrouped(array $types, AccountRepositoryInterface $repository = null): array
{
if (null === $repository) {
$repository = $this->getAccountRepository();
}
$accountList = $repository->getActiveAccountsByType($types);
$liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN];
$grouped = [];
/** @var Account $account */
foreach ($accountList as $account) {
$role = (string)$repository->getMetaValue($account, 'account_role');
if (in_array($account->accountType->type, $liabilityTypes, true)) {
$role = sprintf('l_%s', $account->accountType->type);
}
if ('' === $role) {
$role = 'no_account_type';
if (AccountType::EXPENSE === $account->accountType->type) {
$role = 'expense_account';
}
if (AccountType::REVENUE === $account->accountType->type) {
$role = 'revenue_account';
}
}
$key = (string)trans(sprintf('firefly.opt_group_%s', $role));
$grouped[$key][$account->id] = $account->name;
}
return $grouped;
}
/**
* Grouped dropdown list of all accounts that are valid as the destination of a withdrawal.
*/
@@ -127,35 +158,4 @@ class AccountForm
return $this->select($name, $grouped, $value, $options);
}
private function getAccountsGrouped(array $types, AccountRepositoryInterface $repository = null): array
{
if (null === $repository) {
$repository = $this->getAccountRepository();
}
$accountList = $repository->getActiveAccountsByType($types);
$liabilityTypes = [AccountType::MORTGAGE, AccountType::DEBT, AccountType::CREDITCARD, AccountType::LOAN];
$grouped = [];
/** @var Account $account */
foreach ($accountList as $account) {
$role = (string)$repository->getMetaValue($account, 'account_role');
if (in_array($account->accountType->type, $liabilityTypes, true)) {
$role = sprintf('l_%s', $account->accountType->type);
}
if ('' === $role) {
$role = 'no_account_type';
if (AccountType::EXPENSE === $account->accountType->type) {
$role = 'expense_account';
}
if (AccountType::REVENUE === $account->accountType->type) {
$role = 'revenue_account';
}
}
$key = (string)trans(sprintf('firefly.opt_group_%s', $role));
$grouped[$key][$account->id] = $account->name;
}
return $grouped;
}
}

View File

@@ -47,64 +47,6 @@ class CurrencyForm
return $this->currencyField($name, 'amount', $value, $options);
}
/**
* TODO describe and cleanup.
*
* @param mixed $value
*
* @throws FireflyException
*/
public function balanceAll(string $name, $value = null, array $options = null): string
{
return $this->allCurrencyField($name, 'balance', $value, $options);
}
/**
* TODO cleanup and describe
*
* @param mixed $value
*/
public function currencyList(string $name, $value = null, array $options = null): string
{
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
// get all currencies:
$list = $currencyRepos->get();
$array = [];
/** @var TransactionCurrency $currency */
foreach ($list as $currency) {
$array[$currency->id] = $currency->name.' ('.$currency->symbol.')';
}
return $this->select($name, $array, $value, $options);
}
/**
* TODO cleanup and describe
*
* @param mixed $value
*/
public function currencyListEmpty(string $name, $value = null, array $options = null): string
{
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
// get all currencies:
$list = $currencyRepos->get();
$array = [
0 => (string)trans('firefly.no_currency'),
];
/** @var TransactionCurrency $currency */
foreach ($list as $currency) {
$array[$currency->id] = $currency->name.' ('.$currency->symbol.')';
}
return $this->select($name, $array, $value, $options);
}
/**
* @throws FireflyException
*/
@@ -157,6 +99,18 @@ class CurrencyForm
return $html;
}
/**
* TODO describe and cleanup.
*
* @param mixed $value
*
* @throws FireflyException
*/
public function balanceAll(string $name, $value = null, array $options = null): string
{
return $this->allCurrencyField($name, 'balance', $value, $options);
}
/**
* TODO describe and cleanup
*
@@ -213,4 +167,50 @@ class CurrencyForm
return $html;
}
/**
* TODO cleanup and describe
*
* @param mixed $value
*/
public function currencyList(string $name, $value = null, array $options = null): string
{
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
// get all currencies:
$list = $currencyRepos->get();
$array = [];
/** @var TransactionCurrency $currency */
foreach ($list as $currency) {
$array[$currency->id] = $currency->name.' ('.$currency->symbol.')';
}
return $this->select($name, $array, $value, $options);
}
/**
* TODO cleanup and describe
*
* @param mixed $value
*/
public function currencyListEmpty(string $name, $value = null, array $options = null): string
{
/** @var CurrencyRepositoryInterface $currencyRepos */
$currencyRepos = app(CurrencyRepositoryInterface::class);
// get all currencies:
$list = $currencyRepos->get();
$array = [
0 => (string)trans('firefly.no_currency'),
];
/** @var TransactionCurrency $currency */
foreach ($list as $currency) {
$array[$currency->id] = $currency->name.' ('.$currency->symbol.')';
}
return $this->select($name, $array, $value, $options);
}
}

View File

@@ -58,11 +58,11 @@ class AccountBalanceGrouped
// income and expense array prepped:
$income = [
'label' => 'earned',
'currency_id' => (string) $currency['currency_id'],
'currency_id' => (string)$currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'native_currency_id' => (string) $currency['native_currency_id'],
'native_currency_id' => (string)$currency['native_currency_id'],
'native_currency_symbol' => $currency['native_currency_symbol'],
'native_currency_code' => $currency['native_currency_code'],
'native_currency_decimal_places' => $currency['native_currency_decimal_places'],
@@ -74,11 +74,11 @@ class AccountBalanceGrouped
];
$expense = [
'label' => 'spent',
'currency_id' => (string) $currency['currency_id'],
'currency_id' => (string)$currency['currency_id'],
'currency_symbol' => $currency['currency_symbol'],
'currency_code' => $currency['currency_code'],
'currency_decimal_places' => $currency['currency_decimal_places'],
'native_currency_id' => (string) $currency['native_currency_id'],
'native_currency_id' => (string)$currency['native_currency_id'],
'native_currency_symbol' => $currency['native_currency_symbol'],
'native_currency_code' => $currency['native_currency_code'],
'native_currency_decimal_places' => $currency['native_currency_decimal_places'],
@@ -126,19 +126,19 @@ class AccountBalanceGrouped
foreach ($this->journals as $journal) {
// format the date according to the period
$period = $journal['date']->format($this->carbonFormat);
$currencyId = (int) $journal['currency_id'];
$currencyId = (int)$journal['currency_id'];
$currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$this->currencies[$currencyId] = $currency; // may just re-assign itself, don't mind.
// set the array with monetary info, if it does not exist.
$this->data[$currencyId] ??= [
'currency_id' => (string) $currencyId,
'currency_id' => (string)$currencyId,
'currency_symbol' => $journal['currency_symbol'],
'currency_code' => $journal['currency_code'],
'currency_name' => $journal['currency_name'],
'currency_decimal_places' => $journal['currency_decimal_places'],
// native currency info (could be the same)
'native_currency_id' => (string) $this->default->id,
'native_currency_id' => (string)$this->default->id,
'native_currency_code' => $this->default->code,
'native_currency_symbol' => $this->default->symbol,
'native_currency_decimal_places' => $this->default->decimal_places,
@@ -183,7 +183,7 @@ class AccountBalanceGrouped
$amountConverted = bcmul($amount, $rate);
// 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) {
$amountConverted = $journal['foreign_amount'] ?? '0';
$amountConverted = 'earned' === $key ? app('steam')->positive($amountConverted) : app('steam')->negative($amountConverted);
}
@@ -209,12 +209,12 @@ class AccountBalanceGrouped
$defaultCurrencyId = $default->id;
$this->currencies = [$default->id => $default]; // currency cache
$this->data[$defaultCurrencyId] = [
'currency_id' => (string) $defaultCurrencyId,
'currency_id' => (string)$defaultCurrencyId,
'currency_symbol' => $default->symbol,
'currency_code' => $default->code,
'currency_name' => $default->name,
'currency_decimal_places' => $default->decimal_places,
'native_currency_id' => (string) $defaultCurrencyId,
'native_currency_id' => (string)$defaultCurrencyId,
'native_currency_symbol' => $default->symbol,
'native_currency_code' => $default->code,
'native_currency_name' => $default->name,

View File

@@ -76,6 +76,27 @@ trait ConvertsExchangeRates
return $set;
}
/**
* @deprecated
*/
private function getPreference(): void
{
$this->enabled = config('cer.currency_conversion');
}
/**
* @deprecated
*/
private function getCurrency(int $currencyId): TransactionCurrency
{
$result = TransactionCurrency::find($currencyId);
if (null === $result) {
return app('amount')->getDefaultCurrency();
}
return $result;
}
/**
* For a sum of entries, get the exchange rate to the native currency of
* the user.
@@ -133,27 +154,6 @@ trait ConvertsExchangeRates
return $return;
}
/**
* @deprecated
*/
private function getPreference(): void
{
$this->enabled = config('cer.currency_conversion');
}
/**
* @deprecated
*/
private function getCurrency(int $currencyId): TransactionCurrency
{
$result = TransactionCurrency::find($currencyId);
if (null === $result) {
return app('amount')->getDefaultCurrency();
}
return $result;
}
/**
* @deprecated
*/

View File

@@ -37,11 +37,11 @@ use Illuminate\Support\Facades\Log;
class ExchangeRateConverter
{
// use ConvertsExchangeRates;
private int $queryCount = 0;
private array $prepared = [];
private array $fallback = [];
private bool $isPrepared = false;
private bool $noPreparedRates = false;
private array $prepared = [];
private int $queryCount = 0;
/**
* @throws FireflyException
@@ -54,61 +54,6 @@ class ExchangeRateConverter
return bcmul($amount, $rate);
}
/**
* @throws FireflyException
*/
public function prepare(TransactionCurrency $from, TransactionCurrency $to, Carbon $start, Carbon $end): void
{
Log::debug('prepare()');
$start->startOfDay();
$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')));
$set = auth()->user()
->currencyExchangeRates()
->where('from_currency_id', $from->id)
->where('to_currency_id', $to->id)
->where('date', '<=', $end->format('Y-m-d'))
->where('date', '>=', $start->format('Y-m-d'))
->orderBy('date', 'DESC')->get()
;
++$this->queryCount;
if (0 === $set->count()) {
Log::debug('No prepared rates found in this period, use the fallback');
$this->fallback($from, $to, $start);
$this->noPreparedRates = true;
$this->isPrepared = true;
Log::debug('prepare DONE()');
return;
}
$this->isPrepared = true;
// so there is a fallback just in case. Now loop the set of rates we DO have.
$temp = [];
$count = 0;
foreach ($set as $rate) {
$date = $rate->date->format('Y-m-d');
$temp[$date] ??= [
$from->id => [
$to->id => $rate->rate,
],
];
++$count;
}
Log::debug(sprintf('Found %d rates in this period.', $count));
$currentStart = clone $start;
while ($currentStart->lte($end)) {
$currentDate = $currentStart->format('Y-m-d');
$this->prepared[$currentDate] ??= [];
$fallback = $temp[$currentDate][$from->id][$to->id] ?? $this->fallback[$from->id][$to->id] ?? '0';
if (0 === count($this->prepared[$currentDate]) && 0 !== bccomp('0', $fallback)) {
// fill from temp or fallback or from temp (see before)
$this->prepared[$currentDate][$from->id][$to->id] = $fallback;
}
$currentStart->addDay();
}
}
/**
* @throws FireflyException
*/
@@ -120,11 +65,6 @@ class ExchangeRateConverter
return '0' === $rate ? '1' : $rate;
}
public function summarize(): void
{
Log::debug(sprintf('ExchangeRateConverter ran %d queries.', $this->queryCount));
}
/**
* @throws FireflyException
*/
@@ -202,7 +142,7 @@ class ExchangeRateConverter
->first()
;
++$this->queryCount;
$rate = (string) $result?->rate;
$rate = (string)$result?->rate;
if ('' === $rate) {
app('log')->debug(sprintf('Found no rate for #%d->#%d (%s) in the DB.', $from, $to, $date));
@@ -258,7 +198,7 @@ class ExchangeRateConverter
// grab backup values from config file:
$backup = config(sprintf('cer.rates.%s', $currency->code));
if (null !== $backup) {
return bcdiv('1', (string) $backup);
return bcdiv('1', (string)$backup);
// app('log')->debug(sprintf('Backup rate for %s to EUR is %s.', $currency->code, $backup));
// return $backup;
}
@@ -276,7 +216,7 @@ class ExchangeRateConverter
$cache = new CacheProperties();
$cache->addProperty('cer-euro-id');
if ($cache->has()) {
return (int) $cache->get();
return (int)$cache->get();
}
$euro = TransactionCurrency::whereCode('EUR')->first();
++$this->queryCount;
@@ -288,6 +228,61 @@ class ExchangeRateConverter
return $euro->id;
}
/**
* @throws FireflyException
*/
public function prepare(TransactionCurrency $from, TransactionCurrency $to, Carbon $start, Carbon $end): void
{
Log::debug('prepare()');
$start->startOfDay();
$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')));
$set = auth()->user()
->currencyExchangeRates()
->where('from_currency_id', $from->id)
->where('to_currency_id', $to->id)
->where('date', '<=', $end->format('Y-m-d'))
->where('date', '>=', $start->format('Y-m-d'))
->orderBy('date', 'DESC')->get()
;
++$this->queryCount;
if (0 === $set->count()) {
Log::debug('No prepared rates found in this period, use the fallback');
$this->fallback($from, $to, $start);
$this->noPreparedRates = true;
$this->isPrepared = true;
Log::debug('prepare DONE()');
return;
}
$this->isPrepared = true;
// so there is a fallback just in case. Now loop the set of rates we DO have.
$temp = [];
$count = 0;
foreach ($set as $rate) {
$date = $rate->date->format('Y-m-d');
$temp[$date] ??= [
$from->id => [
$to->id => $rate->rate,
],
];
++$count;
}
Log::debug(sprintf('Found %d rates in this period.', $count));
$currentStart = clone $start;
while ($currentStart->lte($end)) {
$currentDate = $currentStart->format('Y-m-d');
$this->prepared[$currentDate] ??= [];
$fallback = $temp[$currentDate][$from->id][$to->id] ?? $this->fallback[$from->id][$to->id] ?? '0';
if (0 === count($this->prepared[$currentDate]) && 0 !== bccomp('0', $fallback)) {
// fill from temp or fallback or from temp (see before)
$this->prepared[$currentDate][$from->id][$to->id] = $fallback;
}
$currentStart->addDay();
}
}
/**
* If there are no exchange rate in the "prepare" array, future searches for any exchange rate
* will result in nothing: otherwise the preparation had been unnecessary. So, to fix this Firefly III
@@ -307,4 +302,9 @@ class ExchangeRateConverter
Log::debug(sprintf('Fallback rate %s > %s = %s', $from->code, $to->code, $fallback));
Log::debug(sprintf('Fallback rate %s > %s = %s', $to->code, $from->code, bcdiv('1', $fallback)));
}
public function summarize(): void
{
Log::debug(sprintf('ExchangeRateConverter ran %d queries.', $this->queryCount));
}
}

View File

@@ -29,12 +29,12 @@ use Illuminate\Support\Facades\Log;
class SummaryBalanceGrouped
{
private const string SUM = 'sum';
private TransactionCurrency $default;
private array $amounts = [];
private array $keys;
private array $currencies;
private const string SUM = 'sum';
private array $amounts = [];
private array $currencies;
private CurrencyRepositoryInterface $currencyRepository;
private TransactionCurrency $default;
private array $keys;
public function __construct()
{
@@ -43,42 +43,6 @@ class SummaryBalanceGrouped
$this->currencyRepository = app(CurrencyRepositoryInterface::class);
}
public function groupTransactions(string $key, array $journals): void
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
Log::debug(sprintf('Now in groupTransactions with key "%s" and %d journal(s)', $key, count($journals)));
$converter = new ExchangeRateConverter();
$this->keys[] = $key;
$multiplier = 'income' === $key ? '-1' : '1';
/** @var array $journal */
foreach ($journals as $journal) {
// transaction info:
$currencyId = (int)$journal['currency_id'];
$amount = bcmul($journal['amount'], $multiplier);
$currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$this->currencies[$currencyId] = $currency;
$nativeAmount = $converter->convert($currency, $this->default, $journal['date'], $amount);
if ((int)$journal['foreign_currency_id'] === $this->default->id) {
// use foreign amount instead
$nativeAmount = $journal['foreign_amount'];
}
// prep the arrays
$this->amounts[$key] ??= [];
$this->amounts[$key][$currencyId] ??= '0';
$this->amounts[$key]['native'] ??= '0';
$this->amounts[self::SUM][$currencyId] ??= '0';
$this->amounts[self::SUM]['native'] ??= '0';
// add values:
$this->amounts[$key][$currencyId] = bcadd($this->amounts[$key][$currencyId], $amount);
$this->amounts[self::SUM][$currencyId] = bcadd($this->amounts[self::SUM][$currencyId], $amount);
$this->amounts[$key]['native'] = bcadd($this->amounts[$key]['native'], $nativeAmount);
$this->amounts[self::SUM]['native'] = bcadd($this->amounts[self::SUM]['native'], $nativeAmount);
}
$converter->summarize();
}
public function groupData(): array
{
Log::debug('Now going to group data.');
@@ -132,6 +96,42 @@ class SummaryBalanceGrouped
return $return;
}
public function groupTransactions(string $key, array $journals): void
{
Log::debug(sprintf('Created new ExchangeRateConverter in %s', __METHOD__));
Log::debug(sprintf('Now in groupTransactions with key "%s" and %d journal(s)', $key, count($journals)));
$converter = new ExchangeRateConverter();
$this->keys[] = $key;
$multiplier = 'income' === $key ? '-1' : '1';
/** @var array $journal */
foreach ($journals as $journal) {
// transaction info:
$currencyId = (int)$journal['currency_id'];
$amount = bcmul($journal['amount'], $multiplier);
$currency = $this->currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
$this->currencies[$currencyId] = $currency;
$nativeAmount = $converter->convert($currency, $this->default, $journal['date'], $amount);
if ((int)$journal['foreign_currency_id'] === $this->default->id) {
// use foreign amount instead
$nativeAmount = $journal['foreign_amount'];
}
// prep the arrays
$this->amounts[$key] ??= [];
$this->amounts[$key][$currencyId] ??= '0';
$this->amounts[$key]['native'] ??= '0';
$this->amounts[self::SUM][$currencyId] ??= '0';
$this->amounts[self::SUM]['native'] ??= '0';
// add values:
$this->amounts[$key][$currencyId] = bcadd($this->amounts[$key][$currencyId], $amount);
$this->amounts[self::SUM][$currencyId] = bcadd($this->amounts[self::SUM][$currencyId], $amount);
$this->amounts[$key]['native'] = bcadd($this->amounts[$key]['native'], $nativeAmount);
$this->amounts[self::SUM]['native'] = bcadd($this->amounts[self::SUM]['native'], $nativeAmount);
}
$converter->summarize();
}
public function setDefault(TransactionCurrency $default): void
{
$this->default = $default;

View File

@@ -84,9 +84,9 @@ trait ModelInformation
/** @var AccountType $mortgage */
$mortgage = $repository->getAccountTypeByType(AccountType::MORTGAGE);
$liabilityTypes = [
$debt->id => (string) trans(sprintf('firefly.account_type_%s', AccountType::DEBT)),
$loan->id => (string) trans(sprintf('firefly.account_type_%s', AccountType::LOAN)),
$mortgage->id => (string) trans(sprintf('firefly.account_type_%s', AccountType::MORTGAGE)),
$debt->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::DEBT)),
$loan->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::LOAN)),
$mortgage->id => (string)trans(sprintf('firefly.account_type_%s', AccountType::MORTGAGE)),
];
asort($liabilityTypes);
@@ -97,7 +97,7 @@ trait ModelInformation
{
$roles = [];
foreach (config('firefly.accountRoles') as $role) {
$roles[$role] = (string) trans(sprintf('firefly.account_role_%s', $role));
$roles[$role] = (string)trans(sprintf('firefly.account_role_%s', $role));
}
return $roles;
@@ -115,7 +115,7 @@ trait ModelInformation
$triggers = [];
foreach ($operators as $key => $operator) {
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));
}
}
asort($triggers);
@@ -166,7 +166,7 @@ trait ModelInformation
$triggers = [];
foreach ($operators as $key => $operator) {
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));
}
}
asort($triggers);

View File

@@ -139,6 +139,99 @@ trait PeriodOverview
return $entries;
}
/**
* Filter a list of journals by a set of dates, and then group them by currency.
*/
private function filterJournalsByDate(array $array, Carbon $start, Carbon $end): array
{
$result = [];
/** @var array $journal */
foreach ($array as $journal) {
if ($journal['date'] <= $end && $journal['date'] >= $start) {
$result[] = $journal;
}
}
return $result;
}
/**
* Return only transactions where $account is the source.
*/
private function filterTransferredAway(Account $account, array $journals): array
{
$return = [];
/** @var array $journal */
foreach ($journals as $journal) {
if ($account->id === (int)$journal['source_account_id']) {
$return[] = $journal;
}
}
return $return;
}
/**
* Return only transactions where $account is the source.
*/
private function filterTransferredIn(Account $account, array $journals): array
{
$return = [];
/** @var array $journal */
foreach ($journals as $journal) {
if ($account->id === (int)$journal['destination_account_id']) {
$return[] = $journal;
}
}
return $return;
}
private function groupByCurrency(array $journals): array
{
$return = [];
/** @var array $journal */
foreach ($journals as $journal) {
$currencyId = (int)$journal['currency_id'];
$foreignCurrencyId = $journal['foreign_currency_id'];
if (!array_key_exists($currencyId, $return)) {
$return[$currencyId] = [
'amount' => '0',
'count' => 0,
'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'],
'currency_code' => $journal['currency_code'],
'currency_symbol' => $journal['currency_symbol'],
'currency_decimal_places' => $journal['currency_decimal_places'],
];
}
$return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $journal['amount'] ?? '0');
++$return[$currencyId]['count'];
if (null !== $foreignCurrencyId && null !== $journal['foreign_amount']) {
if (!array_key_exists($foreignCurrencyId, $return)) {
$return[$foreignCurrencyId] = [
'amount' => '0',
'count' => 0,
'currency_id' => (int)$foreignCurrencyId,
'currency_name' => $journal['foreign_currency_name'],
'currency_code' => $journal['foreign_currency_code'],
'currency_symbol' => $journal['foreign_currency_symbol'],
'currency_decimal_places' => $journal['foreign_currency_decimal_places'],
];
}
++$return[$foreignCurrencyId]['count'];
$return[$foreignCurrencyId]['amount'] = bcadd($return[$foreignCurrencyId]['amount'], $journal['foreign_amount']);
}
}
return $return;
}
/**
* Overview for single category. Has been refactored recently.
*
@@ -406,6 +499,27 @@ trait PeriodOverview
return $entries;
}
private function filterJournalsByTag(array $set, Tag $tag): array
{
$return = [];
foreach ($set as $entry) {
$found = false;
/** @var array $localTag */
foreach ($entry['tags'] as $localTag) {
if ($localTag['id'] === $tag->id) {
$found = true;
}
}
if (false === $found) {
continue;
}
$return[] = $entry;
}
return $return;
}
/**
* @throws FireflyException
*/
@@ -452,129 +566,15 @@ trait PeriodOverview
}
$entries[]
= [
'title' => $title,
'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
'title' => $title,
'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
'total_transactions' => count($spent) + count($earned) + count($transferred),
'spent' => $this->groupByCurrency($spent),
'earned' => $this->groupByCurrency($earned),
'transferred' => $this->groupByCurrency($transferred),
];
}
return $entries;
}
/**
* Filter a list of journals by a set of dates, and then group them by currency.
*/
private function filterJournalsByDate(array $array, Carbon $start, Carbon $end): array
{
$result = [];
/** @var array $journal */
foreach ($array as $journal) {
if ($journal['date'] <= $end && $journal['date'] >= $start) {
$result[] = $journal;
}
}
return $result;
}
/**
* Return only transactions where $account is the source.
*/
private function filterTransferredAway(Account $account, array $journals): array
{
$return = [];
/** @var array $journal */
foreach ($journals as $journal) {
if ($account->id === (int) $journal['source_account_id']) {
$return[] = $journal;
}
}
return $return;
}
/**
* Return only transactions where $account is the source.
*/
private function filterTransferredIn(Account $account, array $journals): array
{
$return = [];
/** @var array $journal */
foreach ($journals as $journal) {
if ($account->id === (int) $journal['destination_account_id']) {
$return[] = $journal;
}
}
return $return;
}
private function groupByCurrency(array $journals): array
{
$return = [];
/** @var array $journal */
foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id'];
$foreignCurrencyId = $journal['foreign_currency_id'];
if (!array_key_exists($currencyId, $return)) {
$return[$currencyId] = [
'amount' => '0',
'count' => 0,
'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'],
'currency_code' => $journal['currency_code'],
'currency_symbol' => $journal['currency_symbol'],
'currency_decimal_places' => $journal['currency_decimal_places'],
];
}
$return[$currencyId]['amount'] = bcadd($return[$currencyId]['amount'], $journal['amount'] ?? '0');
++$return[$currencyId]['count'];
if (null !== $foreignCurrencyId && null !== $journal['foreign_amount']) {
if (!array_key_exists($foreignCurrencyId, $return)) {
$return[$foreignCurrencyId] = [
'amount' => '0',
'count' => 0,
'currency_id' => (int) $foreignCurrencyId,
'currency_name' => $journal['foreign_currency_name'],
'currency_code' => $journal['foreign_currency_code'],
'currency_symbol' => $journal['foreign_currency_symbol'],
'currency_decimal_places' => $journal['foreign_currency_decimal_places'],
];
}
++$return[$foreignCurrencyId]['count'];
$return[$foreignCurrencyId]['amount'] = bcadd($return[$foreignCurrencyId]['amount'], $journal['foreign_amount']);
}
}
return $return;
}
private function filterJournalsByTag(array $set, Tag $tag): array
{
$return = [];
foreach ($set as $entry) {
$found = false;
/** @var array $localTag */
foreach ($entry['tags'] as $localTag) {
if ($localTag['id'] === $tag->id) {
$found = true;
}
}
if (false === $found) {
continue;
}
$return[] = $entry;
}
return $return;
}
}

View File

@@ -111,58 +111,13 @@ class ParseDateString
return new Carbon('1984-09-17');
}
// maybe a year, nothing else?
if (4 === strlen($date) && is_numeric($date) && (int) $date > 1000 && (int) $date <= 3000) {
if (4 === strlen($date) && is_numeric($date) && (int)$date > 1000 && (int)$date <= 3000) {
return new Carbon(sprintf('%d-01-01', $date));
}
throw new FireflyException(sprintf('[d] Not a recognised date format: "%s"', $date));
}
public function parseRange(string $date): array
{
// several types of range can be submitted
$result = [
'exact' => new Carbon('1984-09-17'),
];
switch (true) {
default:
break;
case $this->isDayRange($date):
$result = $this->parseDayRange($date);
break;
case $this->isMonthRange($date):
$result = $this->parseMonthRange($date);
break;
case $this->isYearRange($date):
$result = $this->parseYearRange($date);
break;
case $this->isMonthDayRange($date):
$result = $this->parseMonthDayRange($date);
break;
case $this->isDayYearRange($date):
$result = $this->parseDayYearRange($date);
break;
case $this->isMonthYearRange($date):
$result = $this->parseMonthYearRange($date);
break;
}
return $result;
}
protected function parseKeyword(string $keyword): Carbon
{
$today = today(config('app.timezone'))->startOfDay();
@@ -234,7 +189,7 @@ class ParseDateString
}
$direction = str_starts_with($part, '+') ? 1 : 0;
$period = $part[strlen($part) - 1];
$number = (int) substr($part, 1, -1);
$number = (int)substr($part, 1, -1);
if (!array_key_exists($period, $functions[$direction])) {
app('log')->error(sprintf('No method for direction %d and period "%s".', $direction, $period));
@@ -249,6 +204,51 @@ class ParseDateString
return $today;
}
public function parseRange(string $date): array
{
// several types of range can be submitted
$result = [
'exact' => new Carbon('1984-09-17'),
];
switch (true) {
default:
break;
case $this->isDayRange($date):
$result = $this->parseDayRange($date);
break;
case $this->isMonthRange($date):
$result = $this->parseMonthRange($date);
break;
case $this->isYearRange($date):
$result = $this->parseYearRange($date);
break;
case $this->isMonthDayRange($date):
$result = $this->parseMonthDayRange($date);
break;
case $this->isDayYearRange($date):
$result = $this->parseDayYearRange($date);
break;
case $this->isMonthYearRange($date):
$result = $this->parseMonthYearRange($date);
break;
}
return $result;
}
/**
* Returns true if this matches regex for xxxx-xx-DD:
*/
@@ -349,6 +349,20 @@ class ParseDateString
return false;
}
/**
* format of string is xxxx-MM-DD
*/
private function parseMonthDayRange(string $date): array
{
app('log')->debug(sprintf('parseMonthDayRange: Parsed "%s".', $date));
$parts = explode('-', $date);
return [
'month' => $parts[1],
'day' => $parts[2],
];
}
protected function isDayYearRange(string $date): bool
{
// if regex for YYYY-xx-DD:
@@ -364,6 +378,20 @@ class ParseDateString
return false;
}
/**
* format of string is YYYY-xx-DD
*/
private function parseDayYearRange(string $date): array
{
app('log')->debug(sprintf('parseDayYearRange: Parsed "%s".', $date));
$parts = explode('-', $date);
return [
'year' => $parts[0],
'day' => $parts[2],
];
}
protected function isMonthYearRange(string $date): bool
{
// if regex for YYYY-MM-xx:
@@ -392,32 +420,4 @@ class ParseDateString
'month' => $parts[1],
];
}
/**
* format of string is xxxx-MM-DD
*/
private function parseMonthDayRange(string $date): array
{
app('log')->debug(sprintf('parseMonthDayRange: Parsed "%s".', $date));
$parts = explode('-', $date);
return [
'month' => $parts[1],
'day' => $parts[2],
];
}
/**
* format of string is YYYY-xx-DD
*/
private function parseDayYearRange(string $date): array
{
app('log')->debug(sprintf('parseDayYearRange: Parsed "%s".', $date));
$parts = explode('-', $date);
return [
'year' => $parts[0],
'day' => $parts[2],
];
}
}

View File

@@ -91,59 +91,6 @@ class BudgetReportGenerator
}
}
/**
* Generates the data necessary to create the card that displays
* the budget overview in the general report.
*/
public function general(): void
{
$this->report = [
'budgets' => [],
'sums' => [],
];
$this->generalBudgetReport();
$this->noBudgetReport();
$this->percentageReport();
}
public function getReport(): array
{
return $this->report;
}
public function setAccounts(Collection $accounts): void
{
$this->accounts = $accounts;
}
public function setBudgets(Collection $budgets): void
{
$this->budgets = $budgets;
}
public function setEnd(Carbon $end): void
{
$this->end = $end;
}
public function setStart(Carbon $start): void
{
$this->start = $start;
}
/**
* @throws FireflyException
*/
public function setUser(User $user): void
{
$this->repository->setUser($user);
$this->blRepository->setUser($user);
$this->opsRepository->setUser($user);
$this->nbRepository->setUser($user);
$this->currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup);
}
/**
* Process each row of expenses collected for the "Account per budget" partial
*/
@@ -181,6 +128,22 @@ class BudgetReportGenerator
}
}
/**
* Generates the data necessary to create the card that displays
* the budget overview in the general report.
*/
public function general(): void
{
$this->report = [
'budgets' => [],
'sums' => [],
];
$this->generalBudgetReport();
$this->noBudgetReport();
$this->percentageReport();
}
/**
* Start the budgets block on the default report by processing every budget.
*/
@@ -250,16 +213,16 @@ class BudgetReportGenerator
// make sum information:
$this->report['sums'][$currencyId]
??= [
'budgeted' => '0',
'spent' => '0',
'left' => '0',
'overspent' => '0',
'currency_id' => $currencyId,
'currency_code' => $limitCurrency->code,
'currency_name' => $limitCurrency->name,
'currency_symbol' => $limitCurrency->symbol,
'currency_decimal_places' => $limitCurrency->decimal_places,
];
'budgeted' => '0',
'spent' => '0',
'left' => '0',
'overspent' => '0',
'currency_id' => $currencyId,
'currency_code' => $limitCurrency->code,
'currency_name' => $limitCurrency->name,
'currency_symbol' => $limitCurrency->symbol,
'currency_decimal_places' => $limitCurrency->decimal_places,
];
$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]['left'] = bcadd($this->report['sums'][$currencyId]['left'], bcadd($limit->amount, $spent));
@@ -349,4 +312,41 @@ class BudgetReportGenerator
}
}
}
public function getReport(): array
{
return $this->report;
}
public function setAccounts(Collection $accounts): void
{
$this->accounts = $accounts;
}
public function setBudgets(Collection $budgets): void
{
$this->budgets = $budgets;
}
public function setEnd(Carbon $end): void
{
$this->end = $end;
}
public function setStart(Carbon $start): void
{
$this->start = $start;
}
/**
* @throws FireflyException
*/
public function setUser(User $user): void
{
$this->repository->setUser($user);
$this->blRepository->setUser($user);
$this->opsRepository->setUser($user);
$this->nbRepository->setUser($user);
$this->currency = app('amount')->getDefaultCurrencyByUserGroup($user->userGroup);
}
}

View File

@@ -82,27 +82,6 @@ class CategoryReportGenerator
}
}
public function setAccounts(Collection $accounts): void
{
$this->accounts = $accounts;
}
public function setEnd(Carbon $end): void
{
$this->end = $end;
}
public function setStart(Carbon $start): void
{
$this->start = $start;
}
public function setUser(User $user): void
{
$this->noCatRepository->setUser($user);
$this->opsRepository->setUser($user);
}
/**
* Process one of the spent arrays from the operations method.
*/
@@ -183,4 +162,25 @@ class CategoryReportGenerator
) : $this->report['categories'][$key]['earned'];
}
}
public function setAccounts(Collection $accounts): void
{
$this->accounts = $accounts;
}
public function setEnd(Carbon $end): void
{
$this->end = $end;
}
public function setStart(Carbon $start): void
{
$this->start = $start;
}
public function setUser(User $user): void
{
$this->noCatRepository->setUser($user);
$this->opsRepository->setUser($user);
}
}

View File

@@ -28,43 +28,6 @@ namespace FireflyIII\Support\Request;
*/
trait AppendsLocationData
{
/**
* Abstract method.
*
* @param mixed $key
*
* @return bool
*/
abstract public function has($key);
/**
* Abstract method.
*
* @return string
*/
abstract public function method();
/**
* Abstract method.
*
* @param mixed ...$patterns
*
* @return mixed
*/
abstract public function routeIs(...$patterns);
/**
* Abstract method stolen from "InteractsWithInput".
*
* @param null $key
* @param bool $default
*
* @return mixed
*
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
*/
abstract public function boolean($key = null, $default = false);
public function addFromromTransactionStore(array $information, array $return): array
{
$return['store_location'] = false;
@@ -82,6 +45,29 @@ trait AppendsLocationData
return $return;
}
private function validLongitude(string $longitude): bool
{
$number = (float)$longitude;
return $number >= -180 && $number <= 180;
}
private function validLatitude(string $latitude): bool
{
$number = (float)$latitude;
return $number >= -90 && $number <= 90;
}
/**
* Abstract method.
*
* @param mixed $key
*
* @return bool
*/
abstract public function has($key);
/**
* Read the submitted Request data and add new or updated Location data to the array.
*/
@@ -136,20 +122,6 @@ trait AppendsLocationData
return $data;
}
private function validLongitude(string $longitude): bool
{
$number = (float) $longitude;
return $number >= -180 && $number <= 180;
}
private function validLatitude(string $latitude): bool
{
$number = (float) $latitude;
return $number >= -90 && $number <= 90;
}
private function getLocationKey(?string $prefix, string $key): string
{
if (null === $prefix) {
@@ -197,6 +169,34 @@ trait AppendsLocationData
return false;
}
/**
* Abstract method.
*
* @return string
*/
abstract public function method();
/**
* Abstract method.
*
* @param mixed ...$patterns
*
* @return mixed
*/
abstract public function routeIs(...$patterns);
/**
* Abstract method stolen from "InteractsWithInput".
*
* @param null $key
* @param bool $default
*
* @return mixed
*
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
*/
abstract public function boolean($key = null, $default = false);
private function isValidPUT(?string $prefix): bool
{
$longitudeKey = $this->getLocationKey($prefix, 'longitude');

View File

@@ -189,16 +189,6 @@ trait ConvertsDataTypes
return (string)$this->clearStringKeepNewlines((string)($this->get($field) ?? ''));
}
/**
* Abstract method that always exists in the Request classes that use this
* trait, OR a stub needs to be added by any other class that uses this train.
*
* @param mixed $key
*
* @return mixed
*/
abstract public function has($key);
/**
* @param mixed $array
*/
@@ -340,6 +330,16 @@ trait ConvertsDataTypes
return $return;
}
/**
* Abstract method that always exists in the Request classes that use this
* trait, OR a stub needs to be added by any other class that uses this train.
*
* @param mixed $key
*
* @return mixed
*/
abstract public function has($key);
/**
* Return date or NULL.
*/

View File

@@ -37,12 +37,12 @@ trait GetRecurrenceData
foreach ($stringKeys as $key) {
if (array_key_exists($key, $transaction)) {
$return[$key] = (string) $transaction[$key];
$return[$key] = (string)$transaction[$key];
}
}
foreach ($intKeys as $key) {
if (array_key_exists($key, $transaction)) {
$return[$key] = (int) $transaction[$key];
$return[$key] = (int)$transaction[$key];
}
}
foreach ($keys as $key) {

View File

@@ -160,87 +160,6 @@ class OperatorQuerySearch implements SearchInterface
$this->collector->excludeSearchWords($this->prohibitedWords);
}
/**
* @throws FireflyException
*/
public static function getRootOperator(string $operator): string
{
$original = $operator;
// if the string starts with "-" (not), we can remove it and recycle
// the configuration from the original operator.
if (str_starts_with($operator, '-')) {
$operator = substr($operator, 1);
}
$config = config(sprintf('search.operators.%s', $operator));
if (null === $config) {
throw new FireflyException(sprintf('No configuration for search operator "%s"', $operator));
}
if (true === $config['alias']) {
$return = $config['alias_for'];
if (str_starts_with($original, '-')) {
$return = sprintf('-%s', $config['alias_for']);
}
app('log')->debug(sprintf('"%s" is an alias for "%s", so return that instead.', $original, $return));
return $return;
}
app('log')->debug(sprintf('"%s" is not an alias.', $operator));
return $original;
}
public function searchTime(): float
{
return microtime(true) - $this->startTime;
}
public function searchTransactions(): LengthAwarePaginator
{
$this->parseTagInstructions();
if (0 === count($this->getWords()) && 0 === count($this->getOperators())) {
return new LengthAwarePaginator([], 0, 5, 1);
}
return $this->collector->getPaginatedGroups();
}
public function getWords(): array
{
return $this->words;
}
public function setDate(Carbon $date): void
{
$this->date = $date;
}
public function setPage(int $page): void
{
$this->page = $page;
$this->collector->setPage($this->page);
}
public function setUser(User $user): void
{
$this->accountRepository->setUser($user);
$this->billRepository->setUser($user);
$this->categoryRepository->setUser($user);
$this->budgetRepository->setUser($user);
$this->tagRepository->setUser($user);
$this->collector = app(GroupCollectorInterface::class);
$this->collector->setUser($user);
$this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation();
$this->setLimit((int)app('preferences')->getForUser($user, 'listPageSize', 50)->data);
}
public function setLimit(int $limit): void
{
$this->limit = $limit;
$this->collector->setLimit($this->limit);
}
/**
* @throws FireflyException
*
@@ -1899,6 +1818,36 @@ class OperatorQuerySearch implements SearchInterface
return true;
}
/**
* @throws FireflyException
*/
public static function getRootOperator(string $operator): string
{
$original = $operator;
// if the string starts with "-" (not), we can remove it and recycle
// the configuration from the original operator.
if (str_starts_with($operator, '-')) {
$operator = substr($operator, 1);
}
$config = config(sprintf('search.operators.%s', $operator));
if (null === $config) {
throw new FireflyException(sprintf('No configuration for search operator "%s"', $operator));
}
if (true === $config['alias']) {
$return = $config['alias_for'];
if (str_starts_with($original, '-')) {
$return = sprintf('-%s', $config['alias_for']);
}
app('log')->debug(sprintf('"%s" is an alias for "%s", so return that instead.', $original, $return));
return $return;
}
app('log')->debug(sprintf('"%s" is not an alias.', $operator));
return $original;
}
/**
* searchDirection: 1 = source (default), 2 = destination, 3 = both
* stringPosition: 1 = start (default), 2 = end, 3 = contains, 4 = is
@@ -2731,6 +2680,21 @@ class OperatorQuerySearch implements SearchInterface
}
}
public function searchTime(): float
{
return microtime(true) - $this->startTime;
}
public function searchTransactions(): LengthAwarePaginator
{
$this->parseTagInstructions();
if (0 === count($this->getWords()) && 0 === count($this->getOperators())) {
return new LengthAwarePaginator([], 0, 5, 1);
}
return $this->collector->getPaginatedGroups();
}
private function parseTagInstructions(): void
{
app('log')->debug('Now in parseTagInstructions()');
@@ -2762,4 +2726,40 @@ class OperatorQuerySearch implements SearchInterface
$this->collector->setAllTags($collection);
}
}
public function getWords(): array
{
return $this->words;
}
public function setDate(Carbon $date): void
{
$this->date = $date;
}
public function setPage(int $page): void
{
$this->page = $page;
$this->collector->setPage($this->page);
}
public function setUser(User $user): void
{
$this->accountRepository->setUser($user);
$this->billRepository->setUser($user);
$this->categoryRepository->setUser($user);
$this->budgetRepository->setUser($user);
$this->tagRepository->setUser($user);
$this->collector = app(GroupCollectorInterface::class);
$this->collector->setUser($user);
$this->collector->withAccountInformation()->withCategoryInformation()->withBudgetInformation();
$this->setLimit((int)app('preferences')->getForUser($user, 'listPageSize', 50)->data);
}
public function setLimit(int $limit): void
{
$this->limit = $limit;
$this->collector->setLimit($this->limit);
}
}

View File

@@ -25,6 +25,7 @@ namespace FireflyIII\Support;
use Carbon\Carbon;
use Carbon\Exceptions\InvalidFormatException;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;

View File

@@ -43,15 +43,6 @@ class AmountFormat extends AbstractExtension
];
}
public function getFunctions(): array
{
return [
$this->formatAmountByAccount(),
$this->formatAmountBySymbol(),
$this->formatAmountByCurrency(),
];
}
protected function formatAmount(): TwigFilter
{
return new TwigFilter(
@@ -78,6 +69,15 @@ class AmountFormat extends AbstractExtension
);
}
public function getFunctions(): array
{
return [
$this->formatAmountByAccount(),
$this->formatAmountBySymbol(),
$this->formatAmountByCurrency(),
];
}
/**
* Will format the amount by the currency related to the given account.
*

View File

@@ -29,6 +29,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Support\Search\OperatorQuerySearch;
use League\CommonMark\GithubFlavoredMarkdownConverter;
use Route;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use Twig\TwigFunction;
@@ -49,22 +50,6 @@ class General extends AbstractExtension
];
}
public function getFunctions(): array
{
return [
$this->phpdate(),
$this->activeRouteStrict(),
$this->activeRoutePartial(),
$this->activeRoutePartialObjectType(),
$this->menuOpenRoutePartial(),
$this->formatDate(),
$this->getMetaField(),
$this->hasRole(),
$this->getRootSearchOperator(),
$this->carbonize(),
];
}
/**
* Show account balance. Only used on the front page of Firefly III.
*/
@@ -206,7 +191,7 @@ class General extends AbstractExtension
]
);
return (string) $converter->convert($text);
return (string)$converter->convert($text);
},
['is_safe' => ['html']]
);
@@ -220,14 +205,30 @@ class General extends AbstractExtension
return new TwigFilter(
'phphost',
static function (string $string): string {
$proto = (string) parse_url($string, PHP_URL_SCHEME);
$host = (string) parse_url($string, PHP_URL_HOST);
$proto = (string)parse_url($string, PHP_URL_SCHEME);
$host = (string)parse_url($string, PHP_URL_HOST);
return e(sprintf('%s://%s', $proto, $host));
}
);
}
public function getFunctions(): array
{
return [
$this->phpdate(),
$this->activeRouteStrict(),
$this->activeRoutePartial(),
$this->activeRoutePartialObjectType(),
$this->menuOpenRoutePartial(),
$this->formatDate(),
$this->getMetaField(),
$this->hasRole(),
$this->getRootSearchOperator(),
$this->carbonize(),
];
}
/**
* Basic example thing for some views.
*/

View File

@@ -70,86 +70,6 @@ class TransactionGroupTwig extends AbstractExtension
);
}
/**
* Shows the amount for a single journal object.
*/
public function journalObjectAmount(): TwigFunction
{
return new TwigFunction(
'journalObjectAmount',
function (TransactionJournal $journal): string {
$result = $this->normalJournalObjectAmount($journal);
// now append foreign amount, if any.
if ($this->journalObjectHasForeign($journal)) {
$foreign = $this->foreignJournalObjectAmount($journal);
$result = sprintf('%s (%s)', $result, $foreign);
}
return $result;
},
['is_safe' => ['html']]
);
}
public function journalHasMeta(): TwigFunction
{
return new TwigFunction(
'journalHasMeta',
static function (int $journalId, string $metaField) {
$count = \DB::table('journal_meta')
->where('name', $metaField)
->where('transaction_journal_id', $journalId)
->whereNull('deleted_at')
->count()
;
return 1 === $count;
}
);
}
public function journalGetMetaDate(): TwigFunction
{
return new TwigFunction(
'journalGetMetaDate',
static function (int $journalId, string $metaField) {
/** @var null|TransactionJournalMeta $entry */
$entry = \DB::table('journal_meta')
->where('name', $metaField)
->where('transaction_journal_id', $journalId)
->whereNull('deleted_at')
->first()
;
if (null === $entry) {
return today(config('app.timezone'));
}
return new Carbon(json_decode($entry->data, false));
}
);
}
public function journalGetMetaField(): TwigFunction
{
return new TwigFunction(
'journalGetMetaField',
static function (int $journalId, string $metaField) {
/** @var null|TransactionJournalMeta $entry */
$entry = \DB::table('journal_meta')
->where('name', $metaField)
->where('transaction_journal_id', $journalId)
->whereNull('deleted_at')
->first()
;
if (null === $entry) {
return '';
}
return json_decode($entry->data, true);
}
);
}
/**
* Generate normal amount for transaction from a transaction group.
*/
@@ -216,6 +136,27 @@ class TransactionGroupTwig extends AbstractExtension
return $result;
}
/**
* Shows the amount for a single journal object.
*/
public function journalObjectAmount(): TwigFunction
{
return new TwigFunction(
'journalObjectAmount',
function (TransactionJournal $journal): string {
$result = $this->normalJournalObjectAmount($journal);
// now append foreign amount, if any.
if ($this->journalObjectHasForeign($journal)) {
$foreign = $this->foreignJournalObjectAmount($journal);
$result = sprintf('%s (%s)', $result, $foreign);
}
return $result;
},
['is_safe' => ['html']]
);
}
/**
* Generate normal amount for transaction from a transaction group.
*/
@@ -275,4 +216,63 @@ class TransactionGroupTwig extends AbstractExtension
return $result;
}
public function journalHasMeta(): TwigFunction
{
return new TwigFunction(
'journalHasMeta',
static function (int $journalId, string $metaField) {
$count = \DB::table('journal_meta')
->where('name', $metaField)
->where('transaction_journal_id', $journalId)
->whereNull('deleted_at')
->count()
;
return 1 === $count;
}
);
}
public function journalGetMetaDate(): TwigFunction
{
return new TwigFunction(
'journalGetMetaDate',
static function (int $journalId, string $metaField) {
/** @var null|TransactionJournalMeta $entry */
$entry = \DB::table('journal_meta')
->where('name', $metaField)
->where('transaction_journal_id', $journalId)
->whereNull('deleted_at')
->first()
;
if (null === $entry) {
return today(config('app.timezone'));
}
return new Carbon(json_decode($entry->data, false));
}
);
}
public function journalGetMetaField(): TwigFunction
{
return new TwigFunction(
'journalGetMetaField',
static function (int $journalId, string $metaField) {
/** @var null|TransactionJournalMeta $entry */
$entry = \DB::table('journal_meta')
->where('name', $metaField)
->where('transaction_journal_id', $journalId)
->whereNull('deleted_at')
->first()
;
if (null === $entry) {
return '';
}
return json_decode($entry->data, true);
}
);
}
}

View File

@@ -38,30 +38,30 @@ trait ValidatesAmountsTrait
return is_numeric($value);
}
final protected function scientificNumber(string $value): bool
{
return str_contains(strtoupper($value), 'E');
}
final protected function lessOrEqualToZero(string $value): bool
{
return -1 === bccomp($value, '0') || 0 === bccomp($value, '0');
}
final protected function zeroOrMore(string $value): bool
{
return 1 === bccomp($value, '0') || 0 === bccomp($value, '0');
}
final protected function moreThanLots(string $value): bool
{
return 1 === bccomp($value, self::BIG_AMOUNT) || 0 === bccomp($value, self::BIG_AMOUNT);
}
final protected function lessThanLots(string $value): bool
{
$amount = bcmul('-1', self::BIG_AMOUNT);
return -1 === bccomp($value, $amount) || 0 === bccomp($value, $amount);
}
final protected function moreThanLots(string $value): bool
{
return 1 === bccomp($value, self::BIG_AMOUNT) || 0 === bccomp($value, self::BIG_AMOUNT);
}
final protected function scientificNumber(string $value): bool
{
return str_contains(strtoupper($value), 'E');
}
final protected function zeroOrMore(string $value): bool
{
return 1 === bccomp($value, '0') || 0 === bccomp($value, '0');
}
}