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

@@ -389,7 +389,7 @@ class AccountRepository implements AccountRepositoryInterface
if (!in_array($type, $list, true)) {
return null;
}
$currencyId = (int) $this->getMetaValue($account, 'currency_id');
$currencyId = (int)$this->getMetaValue($account, 'currency_id');
if ($currencyId > 0) {
return TransactionCurrency::find($currencyId);
}
@@ -411,7 +411,7 @@ class AccountRepository implements AccountRepositoryInterface
return null;
}
if (1 === $result->count()) {
return (string) $result->first()->data;
return (string)$result->first()->data;
}
return null;
@@ -432,8 +432,8 @@ class AccountRepository implements AccountRepositoryInterface
$info = $account->transactions()->get(['transaction_currency_id', 'foreign_currency_id'])->toArray();
$currencyIds = [];
foreach ($info as $entry) {
$currencyIds[] = (int) $entry['transaction_currency_id'];
$currencyIds[] = (int) $entry['foreign_currency_id'];
$currencyIds[] = (int)$entry['transaction_currency_id'];
$currencyIds[] = (int)$entry['foreign_currency_id'];
}
$currencyIds = array_unique($currencyIds);
@@ -456,14 +456,14 @@ class AccountRepository implements AccountRepositoryInterface
AccountType::MORTGAGE => [AccountType::LOAN, AccountType::DEBT, AccountType::CREDITCARD, AccountType::MORTGAGE],
];
if (array_key_exists(ucfirst($type), $sets)) {
$order = (int) $this->getAccountsByType($sets[ucfirst($type)])->max('order');
$order = (int)$this->getAccountsByType($sets[ucfirst($type)])->max('order');
app('log')->debug(sprintf('Return max order of "%s" set: %d', $type, $order));
return $order;
}
$specials = [AccountType::CASH, AccountType::INITIAL_BALANCE, AccountType::IMPORT, AccountType::RECONCILIATION];
$order = (int) $this->getAccountsByType($specials)->max('order');
$order = (int)$this->getAccountsByType($specials)->max('order');
app('log')->debug(sprintf('Return max order of "%s" set (specials!): %d', $type, $order));
return $order;
@@ -544,7 +544,7 @@ class AccountRepository implements AccountRepositoryInterface
continue;
}
if ($index !== (int) $account->order) {
if ($index !== (int)$account->order) {
app('log')->debug(sprintf('Account #%d ("%s"): order should %d be but is %d.', $account->id, $account->name, $index, $account->order));
$account->order = $index;
$account->save();

View File

@@ -138,41 +138,6 @@ class AccountTasker implements AccountTaskerInterface
return $report;
}
/**
* @throws FireflyException
*/
public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): array
{
// get all incomes for the given accounts in the given period!
// also transfers!
// get all transactions:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setDestinationAccounts($accounts)->setRange($start, $end);
$collector->excludeSourceAccounts($accounts);
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])->withAccountInformation();
$report = $this->groupIncomeBySource($collector->getExtractedJournals());
// sort the result
// Obtain a list of columns
$sum = [];
foreach ($report['accounts'] as $accountId => $row) {
$sum[$accountId] = (float)$row['sum']; // intentional float
}
array_multisort($sum, SORT_DESC, $report['accounts']);
return $report;
}
public function setUser(null|Authenticatable|User $user): void
{
if ($user instanceof User) {
$this->user = $user;
}
}
/**
* @throws FireflyException
*/
@@ -233,6 +198,34 @@ class AccountTasker implements AccountTaskerInterface
return $report;
}
/**
* @throws FireflyException
*/
public function getIncomeReport(Carbon $start, Carbon $end, Collection $accounts): array
{
// get all incomes for the given accounts in the given period!
// also transfers!
// get all transactions:
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setDestinationAccounts($accounts)->setRange($start, $end);
$collector->excludeSourceAccounts($accounts);
$collector->setTypes([TransactionType::DEPOSIT, TransactionType::TRANSFER])->withAccountInformation();
$report = $this->groupIncomeBySource($collector->getExtractedJournals());
// sort the result
// Obtain a list of columns
$sum = [];
foreach ($report['accounts'] as $accountId => $row) {
$sum[$accountId] = (float)$row['sum']; // intentional float
}
array_multisort($sum, SORT_DESC, $report['accounts']);
return $report;
}
/**
* @throws FireflyException
*/
@@ -291,4 +284,11 @@ class AccountTasker implements AccountTaskerInterface
return $report;
}
public function setUser(null|Authenticatable|User $user): void
{
if ($user instanceof User) {
$this->user = $user;
}
}
}

View File

@@ -50,122 +50,6 @@ class OperationsRepository implements OperationsRepositoryInterface
return $this->sortByCurrency($journals, 'negative');
}
public function setUser(null|Authenticatable|User $user): void
{
if ($user instanceof User) {
$this->user = $user;
}
}
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified accounts. It's grouped per currency, with as few details in the array
* as possible. Amounts are always positive.
*/
public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array
{
$journals = $this->getTransactions($start, $end, $accounts, TransactionType::DEPOSIT);
return $this->sortByCurrency($journals, 'positive');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumExpenses(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $expense = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByCurrency($journals, 'negative');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumExpensesByDestination(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $expense = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByDirection($journals, 'destination', 'negative');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumExpensesBySource(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $expense = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByDirection($journals, 'source', 'negative');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumIncome(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $revenue = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByCurrency($journals, 'positive');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumIncomeByDestination(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $revenue = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByDirection($journals, 'destination', 'positive');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumIncomeBySource(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $revenue = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByDirection($journals, 'source', 'positive');
}
public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array
{
$journals = $this->getTransactionsForSum(TransactionType::TRANSFER, $start, $end, $accounts, null, $currency);
return $this->groupByEither($journals);
}
/**
* Collect transactions with some parameters
*/
@@ -180,6 +64,13 @@ class OperationsRepository implements OperationsRepositoryInterface
return $collector->getExtractedJournals();
}
public function setUser(null|Authenticatable|User $user): void
{
if ($user instanceof User) {
$this->user = $user;
}
}
private function sortByCurrency(array $journals, string $direction): array
{
$array = [];
@@ -216,6 +107,33 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified accounts. It's grouped per currency, with as few details in the array
* as possible. Amounts are always positive.
*/
public function listIncome(Carbon $start, Carbon $end, ?Collection $accounts = null): array
{
$journals = $this->getTransactions($start, $end, $accounts, TransactionType::DEPOSIT);
return $this->sortByCurrency($journals, 'positive');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumExpenses(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $expense = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByCurrency($journals, 'negative');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
* @SuppressWarnings(PHPMD.NPathComplexity)
@@ -329,6 +247,21 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumExpensesByDestination(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $expense = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByDirection($journals, 'destination', 'negative');
}
private function groupByDirection(array $journals, string $direction, string $method): array
{
$array = [];
@@ -369,6 +302,73 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumExpensesBySource(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $expense = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::WITHDRAWAL, $start, $end, $accounts, $expense, $currency);
return $this->groupByDirection($journals, 'source', 'negative');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumIncome(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $revenue = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByCurrency($journals, 'positive');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumIncomeByDestination(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $revenue = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByDirection($journals, 'destination', 'positive');
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function sumIncomeBySource(
Carbon $start,
Carbon $end,
?Collection $accounts = null,
?Collection $revenue = null,
?TransactionCurrency $currency = null
): array {
$journals = $this->getTransactionsForSum(TransactionType::DEPOSIT, $start, $end, $accounts, $revenue, $currency);
return $this->groupByDirection($journals, 'source', 'positive');
}
public function sumTransfers(Carbon $start, Carbon $end, ?Collection $accounts = null, ?TransactionCurrency $currency = null): array
{
$journals = $this->getTransactionsForSum(TransactionType::TRANSFER, $start, $end, $accounts, null, $currency);
return $this->groupByEither($journals);
}
private function groupByEither(array $journals): array
{
$return = [];

View File

@@ -240,7 +240,7 @@ class BillRepository implements BillRepositoryInterface
/** @var null|Note $note */
$note = $bill->notes()->first();
return (string) $note?->text;
return (string)$note?->text;
}
public function getOverallAverage(Bill $bill): array
@@ -257,7 +257,7 @@ class BillRepository implements BillRepositoryInterface
foreach ($journals as $journal) {
/** @var Transaction $transaction */
$transaction = $journal->transactions()->where('amount', '<', 0)->first();
$currencyId = (int) $journal->transaction_currency_id;
$currencyId = (int)$journal->transaction_currency_id;
$currency = $journal->transactionCurrency;
$result[$currencyId] ??= [
'sum' => '0',
@@ -278,7 +278,7 @@ class BillRepository implements BillRepositoryInterface
* @var array $arr
*/
foreach ($result as $currencyId => $arr) {
$result[$currencyId]['avg'] = bcdiv($arr['sum'], (string) $arr['count']);
$result[$currencyId]['avg'] = bcdiv($arr['sum'], (string)$arr['count']);
}
return $result;
@@ -380,7 +380,7 @@ class BillRepository implements BillRepositoryInterface
if (null === $transaction) {
continue;
}
$currencyId = (int) $journal->transaction_currency_id;
$currencyId = (int)$journal->transaction_currency_id;
$currency = $journal->transactionCurrency;
$result[$currencyId] ??= [
'sum' => '0',
@@ -401,7 +401,7 @@ class BillRepository implements BillRepositoryInterface
* @var array $arr
*/
foreach ($result as $currencyId => $arr) {
$result[$currencyId]['avg'] = bcdiv($arr['sum'], (string) $arr['count']);
$result[$currencyId]['avg'] = bcdiv($arr['sum'], (string)$arr['count']);
}
return $result;
@@ -414,7 +414,7 @@ class BillRepository implements BillRepositoryInterface
{
/** @var Transaction $transaction */
foreach ($transactions as $transaction) {
$journal = $bill->user->transactionJournals()->find((int) $transaction['transaction_journal_id']);
$journal = $bill->user->transactionJournals()->find((int)$transaction['transaction_journal_id']);
$journal->bill_id = $bill->id;
$journal->save();
app('log')->debug(sprintf('Linked journal #%d to bill #%d', $journal->id, $bill->id));
@@ -516,7 +516,7 @@ class BillRepository implements BillRepositoryInterface
$currency = $bill->transactionCurrency;
$return[$currency->id] ??= [
'id' => (string) $currency->id,
'id' => (string)$currency->id,
'name' => $currency->name,
'symbol' => $currency->symbol,
'code' => $currency->code,
@@ -530,9 +530,9 @@ class BillRepository implements BillRepositoryInterface
$sourceTransaction = $transactionJournal->transactions()->where('amount', '<', 0)->first();
if (null !== $sourceTransaction) {
$amount = $sourceTransaction->amount;
if ((int) $sourceTransaction->foreign_currency_id === $currency->id) {
if ((int)$sourceTransaction->foreign_currency_id === $currency->id) {
// use foreign amount instead!
$amount = (string) $sourceTransaction->foreign_amount;
$amount = (string)$sourceTransaction->foreign_amount;
}
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount);
}
@@ -570,14 +570,14 @@ class BillRepository implements BillRepositoryInterface
$currency = $bill->transactionCurrency;
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
$return[$currency->id] ??= [
'id' => (string) $currency->id,
'id' => (string)$currency->id,
'name' => $currency->name,
'symbol' => $currency->symbol,
'code' => $currency->code,
'decimal_places' => $currency->decimal_places,
'sum' => '0',
];
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], bcmul($average, (string) $total));
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], bcmul($average, (string)$total));
}
}

View File

@@ -39,7 +39,6 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Services\Internal\Destroy\BudgetDestroyService;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use FireflyIII\User;
use Illuminate\Contracts\Auth\Authenticatable;
@@ -104,12 +103,12 @@ class BudgetRepository implements BudgetRepositoryInterface
$rate = $converter->getCurrencyRate($currency, $defaultCurrency, $end);
$currencyCode = $currency->code;
$return[$currencyCode] ??= [
'currency_id' => (string) $currency->id,
'currency_id' => (string)$currency->id,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'native_currency_id' => (string) $defaultCurrency->id,
'native_currency_id' => (string)$defaultCurrency->id,
'native_currency_name' => $defaultCurrency->name,
'native_currency_symbol' => $defaultCurrency->symbol,
'native_currency_code' => $defaultCurrency->code,
@@ -135,13 +134,13 @@ class BudgetRepository implements BudgetRepositoryInterface
}
$total = $limit->start_date->diffInDays($limit->end_date) + 1; // include the day itself.
$days = $this->daysInOverlap($limit, $start, $end);
$amount = bcmul(bcdiv($limit->amount, (string) $total), (string) $days);
$amount = bcmul(bcdiv($limit->amount, (string)$total), (string)$days);
$return[$currencyCode]['sum'] = bcadd($return[$currencyCode]['sum'], $amount);
$return[$currencyCode]['native_sum'] = bcmul($rate, $return[$currencyCode]['sum']);
app('log')->debug(
sprintf(
'Amount per day: %s (%s over %d days). Total amount for %d days: %s',
bcdiv($limit->amount, (string) $total),
bcdiv($limit->amount, (string)$total),
$limit->amount,
$total,
$days,
@@ -170,6 +169,40 @@ class BudgetRepository implements BudgetRepositoryInterface
;
}
/**
* How many days of this budget limit are between start and end?
*/
private function daysInOverlap(BudgetLimit $limit, Carbon $start, Carbon $end): int
{
// start1 = $start
// start2 = $limit->start_date
// start1 = $end
// start2 = $limit->end_date
// limit is larger than start and end (inclusive)
// |-----------|
// |----------------|
if ($start->gte($limit->start_date) && $end->lte($limit->end_date)) {
return $start->diffInDays($end) + 1; // add one day
}
// limit starts earlier and limit ends first:
// |-----------|
// |-------|
if ($limit->start_date->lte($start) && $limit->end_date->lte($end)) {
// return days in the range $start-$limit_end
return $start->diffInDays($limit->end_date) + 1; // add one day, the day itself
}
// limit starts later and limit ends earlier
// |-----------|
// |-------|
if ($limit->start_date->gte($start) && $limit->end_date->gte($end)) {
// return days in the range $limit_start - $end
return $limit->start_date->diffInDays($end) + 1; // add one day, the day itself
}
return 0;
}
public function budgetedInPeriodForBudget(Budget $budget, Carbon $start, Carbon $end): array
{
app('log')->debug(sprintf('Now in budgetedInPeriod(#%d, "%s", "%s")', $budget->id, $start->format('Y-m-d'), $end->format('Y-m-d')));
@@ -187,7 +220,7 @@ class BudgetRepository implements BudgetRepositoryInterface
app('log')->debug(sprintf('Budget limit #%d', $limit->id));
$currency = $limit->transactionCurrency;
$return[$currency->id] ??= [
'id' => (string) $currency->id,
'id' => (string)$currency->id,
'name' => $currency->name,
'symbol' => $currency->symbol,
'code' => $currency->code,
@@ -210,12 +243,12 @@ class BudgetRepository implements BudgetRepositoryInterface
}
$total = $limit->start_date->diffInDays($limit->end_date) + 1; // include the day itself.
$days = $this->daysInOverlap($limit, $start, $end);
$amount = bcmul(bcdiv($limit->amount, (string) $total), (string) $days);
$amount = bcmul(bcdiv($limit->amount, (string)$total), (string)$days);
$return[$currency->id]['sum'] = bcadd($return[$currency->id]['sum'], $amount);
app('log')->debug(
sprintf(
'Amount per day: %s (%s over %d days). Total amount for %d days: %s',
bcdiv($limit->amount, (string) $total),
bcdiv($limit->amount, (string)$total),
$limit->amount,
$total,
$days,
@@ -264,7 +297,7 @@ class BudgetRepository implements BudgetRepositoryInterface
$budget->active = $data['active'];
}
if (array_key_exists('notes', $data)) {
$this->setNoteText($budget, (string) $data['notes']);
$this->setNoteText($budget, (string)$data['notes']);
}
$budget->save();
@@ -291,11 +324,115 @@ class BudgetRepository implements BudgetRepositoryInterface
return $budget;
}
private function updateRuleActions(string $oldName, string $newName): void
{
$types = ['set_budget'];
$actions = RuleAction::leftJoin('rules', 'rules.id', '=', 'rule_actions.rule_id')
->where('rules.user_id', $this->user->id)
->whereIn('rule_actions.action_type', $types)
->where('rule_actions.action_value', $oldName)
->get(['rule_actions.*'])
;
app('log')->debug(sprintf('Found %d actions to update.', $actions->count()));
/** @var RuleAction $action */
foreach ($actions as $action) {
$action->action_value = $newName;
$action->save();
app('log')->debug(sprintf('Updated action %d: %s', $action->id, $action->action_value));
}
}
private function updateRuleTriggers(string $oldName, string $newName): void
{
$types = ['budget_is'];
$triggers = RuleTrigger::leftJoin('rules', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rules.user_id', $this->user->id)
->whereIn('rule_triggers.trigger_type', $types)
->where('rule_triggers.trigger_value', $oldName)
->get(['rule_triggers.*'])
;
app('log')->debug(sprintf('Found %d triggers to update.', $triggers->count()));
/** @var RuleTrigger $trigger */
foreach ($triggers as $trigger) {
$trigger->trigger_value = $newName;
$trigger->save();
app('log')->debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value));
}
}
private function setNoteText(Budget $budget, string $text): void
{
$dbNote = $budget->notes()->first();
if ('' !== $text) {
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable()->associate($budget);
}
$dbNote->text = trim($text);
$dbNote->save();
return;
}
if (null !== $dbNote) {
$dbNote->delete();
}
}
public function getAutoBudget(Budget $budget): ?AutoBudget
{
return $budget->autoBudgets()->first();
}
/**
* @throws FireflyException
*/
private function updateAutoBudget(Budget $budget, array $data): void
{
// update or create auto-budget:
$autoBudget = $this->getAutoBudget($budget);
// grab default currency:
$currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup);
if (null === $autoBudget) {
// at this point it's a blind assumption auto_budget_type is 1 or 2.
$autoBudget = new AutoBudget();
$autoBudget->auto_budget_type = $data['auto_budget_type'];
$autoBudget->budget_id = $budget->id;
$autoBudget->transaction_currency_id = $currency->id;
}
// set or update the currency.
if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) {
/** @var CurrencyRepositoryInterface $repos */
$repos = app(CurrencyRepositoryInterface::class);
$currencyId = (int)($data['currency_id'] ?? 0);
$currencyCode = (string)($data['currency_code'] ?? '');
$currency = $repos->find($currencyId);
if (null === $currency) {
$currency = $repos->findByCode($currencyCode);
}
if (null !== $currency) {
$autoBudget->transaction_currency_id = $currency->id;
}
}
// change values if submitted or presented:
if (array_key_exists('auto_budget_type', $data)) {
$autoBudget->auto_budget_type = $data['auto_budget_type'];
}
if (array_key_exists('auto_budget_amount', $data)) {
$autoBudget->amount = $data['auto_budget_amount'];
}
if (array_key_exists('auto_budget_period', $data)) {
$autoBudget->period = $data['auto_budget_period'];
}
$autoBudget->save();
}
/**
* Find a budget or return NULL
*
@@ -326,8 +463,8 @@ class BudgetRepository implements BudgetRepositoryInterface
foreach ($budgets as $budget) {
\DB::table('budget_transaction')->where('budget_id', $budget->id)->delete();
\DB::table('budget_transaction_journal')->where('budget_id', $budget->id)->delete();
RecurrenceTransactionMeta::where('name', 'budget_id')->where('value', (string) $budget->id)->delete();
RuleAction::where('action_type', 'set_budget')->where('action_value', (string) $budget->id)->delete();
RecurrenceTransactionMeta::where('name', 'budget_id')->where('value', (string)$budget->id)->delete();
RuleAction::where('action_type', 'set_budget')->where('action_value', (string)$budget->id)->delete();
$budget->delete();
}
Log::channel('audit')->info('Delete all budgets through destroyAll');
@@ -352,7 +489,7 @@ class BudgetRepository implements BudgetRepositoryInterface
{
app('log')->debug('Now in findBudget()');
app('log')->debug(sprintf('Searching for budget with ID #%d...', $budgetId));
$result = $this->find((int) $budgetId);
$result = $this->find((int)$budgetId);
if (null === $result && null !== $budgetName && '' !== $budgetName) {
app('log')->debug(sprintf('Searching for budget with name %s...', $budgetName));
$result = $this->findByName($budgetName);
@@ -488,9 +625,9 @@ class BudgetRepository implements BudgetRepositoryInterface
$array = [];
foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id'];
$currencyId = (int)$journal['currency_id'];
$array[$currencyId] ??= [
'id' => (string) $currencyId,
'id' => (string)$currencyId,
'name' => $journal['currency_name'],
'symbol' => $journal['currency_symbol'],
'code' => $journal['currency_code'],
@@ -500,10 +637,10 @@ class BudgetRepository implements BudgetRepositoryInterface
$array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal['amount']));
// also do foreign amount:
$foreignId = (int) $journal['foreign_currency_id'];
$foreignId = (int)$journal['foreign_currency_id'];
if (0 !== $foreignId) {
$array[$foreignId] ??= [
'id' => (string) $foreignId,
'id' => (string)$foreignId,
'name' => $journal['foreign_currency_name'],
'symbol' => $journal['foreign_currency_symbol'],
'code' => $journal['foreign_currency_code'],
@@ -550,9 +687,9 @@ class BudgetRepository implements BudgetRepositoryInterface
$array = [];
foreach ($journals as $journal) {
$currencyId = (int) $journal['currency_id'];
$currencyId = (int)$journal['currency_id'];
$array[$currencyId] ??= [
'id' => (string) $currencyId,
'id' => (string)$currencyId,
'name' => $journal['currency_name'],
'symbol' => $journal['currency_symbol'],
'code' => $journal['currency_code'],
@@ -562,10 +699,10 @@ class BudgetRepository implements BudgetRepositoryInterface
$array[$currencyId]['sum'] = bcadd($array[$currencyId]['sum'], app('steam')->negative($journal['amount']));
// also do foreign amount:
$foreignId = (int) $journal['foreign_currency_id'];
$foreignId = (int)$journal['foreign_currency_id'];
if (0 !== $foreignId) {
$array[$foreignId] ??= [
'id' => (string) $foreignId,
'id' => (string)$foreignId,
'name' => $journal['foreign_currency_name'],
'symbol' => $journal['foreign_currency_symbol'],
'code' => $journal['foreign_currency_code'],
@@ -607,7 +744,7 @@ class BudgetRepository implements BudgetRepositoryInterface
// set notes
if (array_key_exists('notes', $data)) {
$this->setNoteText($newBudget, (string) $data['notes']);
$this->setNoteText($newBudget, (string)$data['notes']);
}
if (!array_key_exists('auto_budget_type', $data) || !array_key_exists('auto_budget_amount', $data) || !array_key_exists('auto_budget_period', $data)) {
@@ -635,10 +772,10 @@ class BudgetRepository implements BudgetRepositoryInterface
$repos = app(CurrencyRepositoryInterface::class);
$currency = null;
if (array_key_exists('currency_id', $data)) {
$currency = $repos->find((int) $data['currency_id']);
$currency = $repos->find((int)$data['currency_id']);
}
if (array_key_exists('currency_code', $data)) {
$currency = $repos->findByCode((string) $data['currency_code']);
$currency = $repos->findByCode((string)$data['currency_code']);
}
if (null === $currency) {
$currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup);
@@ -674,144 +811,6 @@ class BudgetRepository implements BudgetRepositoryInterface
public function getMaxOrder(): int
{
return (int) $this->user->budgets()->max('order');
}
/**
* How many days of this budget limit are between start and end?
*/
private function daysInOverlap(BudgetLimit $limit, Carbon $start, Carbon $end): int
{
// start1 = $start
// start2 = $limit->start_date
// start1 = $end
// start2 = $limit->end_date
// limit is larger than start and end (inclusive)
// |-----------|
// |----------------|
if ($start->gte($limit->start_date) && $end->lte($limit->end_date)) {
return $start->diffInDays($end) + 1; // add one day
}
// limit starts earlier and limit ends first:
// |-----------|
// |-------|
if ($limit->start_date->lte($start) && $limit->end_date->lte($end)) {
// return days in the range $start-$limit_end
return $start->diffInDays($limit->end_date) + 1; // add one day, the day itself
}
// limit starts later and limit ends earlier
// |-----------|
// |-------|
if ($limit->start_date->gte($start) && $limit->end_date->gte($end)) {
// return days in the range $limit_start - $end
return $limit->start_date->diffInDays($end) + 1; // add one day, the day itself
}
return 0;
}
private function updateRuleActions(string $oldName, string $newName): void
{
$types = ['set_budget'];
$actions = RuleAction::leftJoin('rules', 'rules.id', '=', 'rule_actions.rule_id')
->where('rules.user_id', $this->user->id)
->whereIn('rule_actions.action_type', $types)
->where('rule_actions.action_value', $oldName)
->get(['rule_actions.*'])
;
app('log')->debug(sprintf('Found %d actions to update.', $actions->count()));
/** @var RuleAction $action */
foreach ($actions as $action) {
$action->action_value = $newName;
$action->save();
app('log')->debug(sprintf('Updated action %d: %s', $action->id, $action->action_value));
}
}
private function updateRuleTriggers(string $oldName, string $newName): void
{
$types = ['budget_is'];
$triggers = RuleTrigger::leftJoin('rules', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rules.user_id', $this->user->id)
->whereIn('rule_triggers.trigger_type', $types)
->where('rule_triggers.trigger_value', $oldName)
->get(['rule_triggers.*'])
;
app('log')->debug(sprintf('Found %d triggers to update.', $triggers->count()));
/** @var RuleTrigger $trigger */
foreach ($triggers as $trigger) {
$trigger->trigger_value = $newName;
$trigger->save();
app('log')->debug(sprintf('Updated trigger %d: %s', $trigger->id, $trigger->trigger_value));
}
}
private function setNoteText(Budget $budget, string $text): void
{
$dbNote = $budget->notes()->first();
if ('' !== $text) {
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable()->associate($budget);
}
$dbNote->text = trim($text);
$dbNote->save();
return;
}
if (null !== $dbNote) {
$dbNote->delete();
}
}
/**
* @throws FireflyException
*/
private function updateAutoBudget(Budget $budget, array $data): void
{
// update or create auto-budget:
$autoBudget = $this->getAutoBudget($budget);
// grab default currency:
$currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup);
if (null === $autoBudget) {
// at this point it's a blind assumption auto_budget_type is 1 or 2.
$autoBudget = new AutoBudget();
$autoBudget->auto_budget_type = $data['auto_budget_type'];
$autoBudget->budget_id = $budget->id;
$autoBudget->transaction_currency_id = $currency->id;
}
// set or update the currency.
if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) {
/** @var CurrencyRepositoryInterface $repos */
$repos = app(CurrencyRepositoryInterface::class);
$currencyId = (int) ($data['currency_id'] ?? 0);
$currencyCode = (string) ($data['currency_code'] ?? '');
$currency = $repos->find($currencyId);
if (null === $currency) {
$currency = $repos->findByCode($currencyCode);
}
if (null !== $currency) {
$autoBudget->transaction_currency_id = $currency->id;
}
}
// change values if submitted or presented:
if (array_key_exists('auto_budget_type', $data)) {
$autoBudget->auto_budget_type = $data['auto_budget_type'];
}
if (array_key_exists('auto_budget_amount', $data)) {
$autoBudget->amount = $data['auto_budget_amount'];
}
if (array_key_exists('auto_budget_period', $data)) {
$autoBudget->period = $data['auto_budget_period'];
}
$autoBudget->save();
return (int)$this->user->budgets()->max('order');
}
}

View File

@@ -189,6 +189,14 @@ class OperationsRepository implements OperationsRepositoryInterface
}
}
private function getBudgets(): Collection
{
/** @var BudgetRepositoryInterface $repos */
$repos = app(BudgetRepositoryInterface::class);
return $repos->getActiveBudgets();
}
/**
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
@@ -288,12 +296,4 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
private function getBudgets(): Collection
{
/** @var BudgetRepositoryInterface $repos */
$repos = app(BudgetRepositoryInterface::class);
return $repos->getActiveBudgets();
}
}

View File

@@ -210,6 +210,34 @@ class CategoryRepository implements CategoryRepositoryInterface
return $firstJournalDate;
}
private function getFirstJournalDate(Category $category): ?Carbon
{
$query = $category->transactionJournals()->orderBy('date', 'ASC');
$result = $query->first(['transaction_journals.*']);
if (null !== $result) {
return $result->date;
}
return null;
}
private function getFirstTransactionDate(Category $category): ?Carbon
{
// check transactions:
$query = $category->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'ASC')
;
$lastTransaction = $query->first(['transaction_journals.*']);
if (null !== $lastTransaction) {
return new Carbon($lastTransaction->date);
}
return null;
}
public function getAttachments(Category $category): Collection
{
$set = $category->attachments()->get();
@@ -271,56 +299,6 @@ class CategoryRepository implements CategoryRepositoryInterface
return $lastJournalDate;
}
public function searchCategory(string $query, int $limit): Collection
{
$search = $this->user->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
}
return $search->take($limit)->get();
}
/**
* @throws \Exception
*/
public function update(Category $category, array $data): Category
{
/** @var CategoryUpdateService $service */
$service = app(CategoryUpdateService::class);
$service->setUser($this->user);
return $service->update($category, $data);
}
private function getFirstJournalDate(Category $category): ?Carbon
{
$query = $category->transactionJournals()->orderBy('date', 'ASC');
$result = $query->first(['transaction_journals.*']);
if (null !== $result) {
return $result->date;
}
return null;
}
private function getFirstTransactionDate(Category $category): ?Carbon
{
// check transactions:
$query = $category->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'ASC')
;
$lastTransaction = $query->first(['transaction_journals.*']);
if (null !== $lastTransaction) {
return new Carbon($lastTransaction->date);
}
return null;
}
private function getLastJournalDate(Category $category, Collection $accounts): ?Carbon
{
$query = $category->transactionJournals()->orderBy('date', 'DESC');
@@ -361,4 +339,26 @@ class CategoryRepository implements CategoryRepositoryInterface
return null;
}
public function searchCategory(string $query, int $limit): Collection
{
$search = $this->user->categories();
if ('' !== $query) {
$search->where('name', 'LIKE', sprintf('%%%s%%', $query));
}
return $search->take($limit)->get();
}
/**
* @throws \Exception
*/
public function update(Category $category, array $data): Category
{
/** @var CategoryUpdateService $service */
$service = app(CategoryUpdateService::class);
$service->setUser($this->user);
return $service->update($category, $data);
}
}

View File

@@ -75,9 +75,9 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface
$journalId = (int)$journal['transaction_journal_id'];
$array[$currencyId]['categories'][0]['transaction_journals'][$journalId]
= [
'amount' => app('steam')->negative($journal['amount']),
'date' => $journal['date'],
];
'amount' => app('steam')->negative($journal['amount']),
'date' => $journal['date'],
];
}
return $array;
@@ -128,9 +128,9 @@ class NoCategoryRepository implements NoCategoryRepositoryInterface
$journalId = (int)$journal['transaction_journal_id'];
$array[$currencyId]['categories'][0]['transaction_journals'][$journalId]
= [
'amount' => app('steam')->positive($journal['amount']),
'date' => $journal['date'],
];
'amount' => app('steam')->positive($journal['amount']),
'date' => $journal['date'],
];
}
return $array;

View File

@@ -115,6 +115,14 @@ class OperationsRepository implements OperationsRepositoryInterface
}
}
/**
* Returns a list of all the categories belonging to a user.
*/
private function getCategories(): Collection
{
return $this->user->categories()->get();
}
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified category set to them. It's grouped per currency, with as few details in the array
@@ -420,12 +428,4 @@ class OperationsRepository implements OperationsRepositoryInterface
return $array;
}
/**
* Returns a list of all the categories belonging to a user.
*/
private function getCategories(): Collection
{
return $this->user->categories()->get();
}
}

View File

@@ -245,6 +245,25 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface
;
}
/**
* @throws \Exception
*/
private function setNoteText(TransactionJournalLink $link, string $text): void
{
$dbNote = $link->notes()->first();
if ('' !== $text) {
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable()->associate($link);
}
$dbNote->text = trim($text);
$dbNote->save();
return;
}
$dbNote?->delete();
}
public function switchLinkById(int $linkId): bool
{
/** @var null|TransactionJournalLink $link */
@@ -293,23 +312,4 @@ class LinkTypeRepository implements LinkTypeRepositoryInterface
return $journalLink;
}
/**
* @throws \Exception
*/
private function setNoteText(TransactionJournalLink $link, string $text): void
{
$dbNote = $link->notes()->first();
if ('' !== $text) {
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable()->associate($link);
}
$dbNote->text = trim($text);
$dbNote->save();
return;
}
$dbNote?->delete();
}
}

View File

@@ -279,6 +279,25 @@ trait ModifiesPiggyBanks
return true;
}
private function updateNote(PiggyBank $piggyBank, string $note): bool
{
if ('' === $note) {
$dbNote = $piggyBank->notes()->first();
$dbNote?->delete();
return true;
}
$dbNote = $piggyBank->notes()->first();
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable()->associate($piggyBank);
}
$dbNote->text = trim($note);
$dbNote->save();
return true;
}
public function update(PiggyBank $piggyBank, array $data): PiggyBank
{
$piggyBank = $this->updateProperties($piggyBank, $data);
@@ -339,25 +358,6 @@ trait ModifiesPiggyBanks
return $piggyBank;
}
private function updateNote(PiggyBank $piggyBank, string $note): bool
{
if ('' === $note) {
$dbNote = $piggyBank->notes()->first();
$dbNote?->delete();
return true;
}
$dbNote = $piggyBank->notes()->first();
if (null === $dbNote) {
$dbNote = new Note();
$dbNote->noteable()->associate($piggyBank);
}
$dbNote->text = trim($note);
$dbNote->save();
return true;
}
private function updateProperties(PiggyBank $piggyBank, array $data): PiggyBank
{
if (array_key_exists('name', $data) && '' !== $data['name']) {

View File

@@ -400,6 +400,21 @@ class RecurringRepository implements RecurringRepositoryInterface
return $this->filterMaxDate($repeatUntil, $occurrences);
}
private function filterMaxDate(?Carbon $max, array $occurrences): array
{
if (null === $max) {
return $occurrences;
}
$filtered = [];
foreach ($occurrences as $date) {
if ($date->lte($max)) {
$filtered[] = $date;
}
}
return $filtered;
}
/**
* Parse the repetition in a string that is user readable.
*
@@ -555,19 +570,4 @@ class RecurringRepository implements RecurringRepositoryInterface
return $service->update($recurrence, $data);
}
private function filterMaxDate(?Carbon $max, array $occurrences): array
{
if (null === $max) {
return $occurrences;
}
$filtered = [];
foreach ($occurrences as $date) {
if ($date->lte($max)) {
$filtered[] = $date;
}
}
return $filtered;
}
}

View File

@@ -280,6 +280,26 @@ class RuleRepository implements RuleRepositoryInterface
return $this->user->rules()->find($ruleId);
}
private function setRuleTrigger(string $moment, Rule $rule): void
{
/** @var null|RuleTrigger $trigger */
$trigger = $rule->ruleTriggers()->where('trigger_type', 'user_action')->first();
if (null !== $trigger) {
$trigger->trigger_value = $moment;
$trigger->save();
return;
}
$trigger = new RuleTrigger();
$trigger->order = 0;
$trigger->trigger_type = 'user_action';
$trigger->trigger_value = $moment;
$trigger->rule_id = $rule->id;
$trigger->active = true;
$trigger->stop_processing = false;
$trigger->save();
}
public function resetRuleOrder(RuleGroup $ruleGroup): bool
{
$groupRepository = app(RuleGroupRepositoryInterface::class);
@@ -336,6 +356,59 @@ class RuleRepository implements RuleRepositoryInterface
return (int)$ruleGroup->rules()->max('order');
}
private function storeTriggers(Rule $rule, array $data): void
{
$order = 1;
foreach ($data['triggers'] as $trigger) {
$value = $trigger['value'] ?? '';
$stopProcessing = $trigger['stop_processing'] ?? false;
$active = $trigger['active'] ?? true;
$type = $trigger['type'];
if (true === ($trigger['prohibited'] ?? false) && !str_starts_with($type, '-')) {
$type = sprintf('-%s', $type);
}
// empty the value in case the rule needs no context
// TODO create a helper to automatically return these.
$needTrue = [
'reconciled',
'has_attachments',
'has_any_category',
'has_any_budget',
'has_any_bill',
'has_any_tag',
'any_notes',
'any_external_url',
'has_no_attachments',
'has_no_category',
'has_no_budget',
'has_no_bill',
'has_no_tag',
'no_notes',
'no_external_url',
'source_is_cash',
'destination_is_cash',
'account_is_cash',
'exists',
'no_external_id',
'any_external_id',
];
if (in_array($type, $needTrue, true)) {
$value = '';
}
$triggerValues = [
'action' => $type,
'value' => $value,
'stop_processing' => $stopProcessing,
'order' => $order,
'active' => $active,
];
$this->storeTrigger($rule, $triggerValues);
++$order;
}
}
public function storeTrigger(Rule $rule, array $values): RuleTrigger
{
$ruleTrigger = new RuleTrigger();
@@ -350,6 +423,25 @@ class RuleRepository implements RuleRepositoryInterface
return $ruleTrigger;
}
private function storeActions(Rule $rule, array $data): void
{
$order = 1;
foreach ($data['actions'] as $action) {
$value = $action['value'] ?? '';
$stopProcessing = $action['stop_processing'] ?? false;
$active = $action['active'] ?? true;
$actionValues = [
'action' => $action['type'],
'value' => $value,
'stop_processing' => $stopProcessing,
'order' => $order,
'active' => $active,
];
$this->storeAction($rule, $actionValues);
++$order;
}
}
public function storeAction(Rule $rule, array $values): RuleAction
{
$ruleAction = new RuleAction();
@@ -426,96 +518,4 @@ class RuleRepository implements RuleRepositoryInterface
return $rule;
}
private function setRuleTrigger(string $moment, Rule $rule): void
{
/** @var null|RuleTrigger $trigger */
$trigger = $rule->ruleTriggers()->where('trigger_type', 'user_action')->first();
if (null !== $trigger) {
$trigger->trigger_value = $moment;
$trigger->save();
return;
}
$trigger = new RuleTrigger();
$trigger->order = 0;
$trigger->trigger_type = 'user_action';
$trigger->trigger_value = $moment;
$trigger->rule_id = $rule->id;
$trigger->active = true;
$trigger->stop_processing = false;
$trigger->save();
}
private function storeTriggers(Rule $rule, array $data): void
{
$order = 1;
foreach ($data['triggers'] as $trigger) {
$value = $trigger['value'] ?? '';
$stopProcessing = $trigger['stop_processing'] ?? false;
$active = $trigger['active'] ?? true;
$type = $trigger['type'];
if (true === ($trigger['prohibited'] ?? false) && !str_starts_with($type, '-')) {
$type = sprintf('-%s', $type);
}
// empty the value in case the rule needs no context
// TODO create a helper to automatically return these.
$needTrue = [
'reconciled',
'has_attachments',
'has_any_category',
'has_any_budget',
'has_any_bill',
'has_any_tag',
'any_notes',
'any_external_url',
'has_no_attachments',
'has_no_category',
'has_no_budget',
'has_no_bill',
'has_no_tag',
'no_notes',
'no_external_url',
'source_is_cash',
'destination_is_cash',
'account_is_cash',
'exists',
'no_external_id',
'any_external_id',
];
if (in_array($type, $needTrue, true)) {
$value = '';
}
$triggerValues = [
'action' => $type,
'value' => $value,
'stop_processing' => $stopProcessing,
'order' => $order,
'active' => $active,
];
$this->storeTrigger($rule, $triggerValues);
++$order;
}
}
private function storeActions(Rule $rule, array $data): void
{
$order = 1;
foreach ($data['actions'] as $action) {
$value = $action['value'] ?? '';
$stopProcessing = $action['stop_processing'] ?? false;
$active = $action['active'] ?? true;
$actionValues = [
'action' => $action['type'],
'value' => $value,
'stop_processing' => $stopProcessing,
'order' => $order,
'active' => $active,
];
$this->storeAction($rule, $actionValues);
++$order;
}
}
}

View File

@@ -151,6 +151,49 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return true;
}
private function resetRuleActionOrder(Rule $rule): void
{
$actions = $rule->ruleActions()
->orderBy('order', 'ASC')
->orderBy('active', 'DESC')
->orderBy('action_type', 'ASC')
->get()
;
$index = 1;
/** @var RuleAction $action */
foreach ($actions as $action) {
if ($action->order !== $index) {
$action->order = $index;
$action->save();
app('log')->debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index));
}
++$index;
}
}
private function resetRuleTriggerOrder(Rule $rule): void
{
$triggers = $rule->ruleTriggers()
->orderBy('order', 'ASC')
->orderBy('active', 'DESC')
->orderBy('trigger_type', 'ASC')
->get()
;
$index = 1;
/** @var RuleTrigger $trigger */
foreach ($triggers as $trigger) {
$order = $trigger->order;
if ($order !== $index) {
$trigger->order = $index;
$trigger->save();
app('log')->debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index));
}
++$index;
}
}
public function destroyAll(): void
{
Log::channel('audit')->info('Delete all rule groups through destroyAll');
@@ -412,47 +455,4 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
return $ruleGroup;
}
private function resetRuleActionOrder(Rule $rule): void
{
$actions = $rule->ruleActions()
->orderBy('order', 'ASC')
->orderBy('active', 'DESC')
->orderBy('action_type', 'ASC')
->get()
;
$index = 1;
/** @var RuleAction $action */
foreach ($actions as $action) {
if ($action->order !== $index) {
$action->order = $index;
$action->save();
app('log')->debug(sprintf('Rule action #%d was on spot %d but must be on spot %d', $action->id, $action->order, $index));
}
++$index;
}
}
private function resetRuleTriggerOrder(Rule $rule): void
{
$triggers = $rule->ruleTriggers()
->orderBy('order', 'ASC')
->orderBy('active', 'DESC')
->orderBy('trigger_type', 'ASC')
->get()
;
$index = 1;
/** @var RuleTrigger $trigger */
foreach ($triggers as $trigger) {
$order = $trigger->order;
if ($order !== $index) {
$trigger->order = $index;
$trigger->save();
app('log')->debug(sprintf('Rule trigger #%d was on spot %d but must be on spot %d', $trigger->id, $order, $index));
}
++$index;
}
}
}

View File

@@ -120,6 +120,14 @@ class OperationsRepository implements OperationsRepositoryInterface
}
}
private function getTags(): Collection
{
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
return $repository->get();
}
/**
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
* which have the specified tag(s) set to them. It's grouped per currency, with as few details in the array
@@ -215,12 +223,4 @@ class OperationsRepository implements OperationsRepositoryInterface
{
throw new FireflyException(sprintf('%s is not yet implemented.', __METHOD__));
}
private function getTags(): Collection
{
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
return $repository->get();
}
}

View File

@@ -259,7 +259,7 @@ class TagRepository implements TagRepositoryInterface
if (false === $found) {
continue;
}
$currencyId = (int) $journal['currency_id'];
$currencyId = (int)$journal['currency_id'];
$sums[$currencyId] ??= [
'currency_id' => $currencyId,
'currency_name' => $journal['currency_name'],
@@ -273,7 +273,7 @@ class TagRepository implements TagRepositoryInterface
];
// add amount to correct type:
$amount = app('steam')->positive((string) $journal['amount']);
$amount = app('steam')->positive((string)$journal['amount']);
$type = $journal['transaction_type_type'];
if (TransactionType::WITHDRAWAL === $type) {
$amount = bcmul($amount, '-1');
@@ -294,7 +294,7 @@ class TagRepository implements TagRepositoryInterface
TransactionType::OPENING_BALANCE => '0',
];
// add foreign amount to correct type:
$amount = app('steam')->positive((string) $journal['foreign_amount']);
$amount = app('steam')->positive((string)$journal['foreign_amount']);
if (TransactionType::WITHDRAWAL === $type) {
$amount = bcmul($amount, '-1');
}

View File

@@ -90,6 +90,46 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
return $result;
}
private function expandJournal(TransactionJournal $journal): array
{
$array = $journal->toArray();
$array['transactions'] = [];
$array['meta'] = $journal->transactionJournalMeta->toArray();
$array['tags'] = $journal->tags->toArray();
$array['categories'] = $journal->categories->toArray();
$array['budgets'] = $journal->budgets->toArray();
$array['notes'] = $journal->notes->toArray();
$array['locations'] = [];
$array['attachments'] = $journal->attachments->toArray();
$array['links'] = [];
$array['piggy_bank_events'] = $journal->piggyBankEvents->toArray();
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
$array['transactions'][] = $this->expandTransaction($transaction);
}
return $array;
}
private function expandTransaction(Transaction $transaction): array
{
$array = $transaction->toArray();
$array['account'] = $transaction->account->toArray();
$array['budgets'] = [];
$array['categories'] = [];
foreach ($transaction->categories as $category) {
$array['categories'][] = $category->toArray();
}
foreach ($transaction->budgets as $budget) {
$array['budgets'][] = $budget->toArray();
}
return $array;
}
/**
* Return all attachments for all journals in the group.
*/
@@ -201,6 +241,48 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
return $return;
}
private function getFormattedAmount(TransactionJournal $journal): string
{
/** @var Transaction $transaction */
$transaction = $journal->transactions->first();
$currency = $transaction->transactionCurrency;
$type = $journal->transactionType->type;
$amount = app('steam')->positive($transaction->amount);
$return = '';
if (TransactionType::WITHDRAWAL === $type) {
$return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
}
if (TransactionType::WITHDRAWAL !== $type) {
$return = app('amount')->formatAnything($currency, $amount);
}
return $return;
}
private function getFormattedForeignAmount(TransactionJournal $journal): string
{
/** @var Transaction $transaction */
$transaction = $journal->transactions->first();
if (null === $transaction->foreign_amount || '' === $transaction->foreign_amount) {
return '';
}
if (0 === bccomp('0', $transaction->foreign_amount)) {
return '';
}
$currency = $transaction->foreignCurrency;
$type = $journal->transactionType->type;
$amount = app('steam')->positive($transaction->foreign_amount);
$return = '';
if (TransactionType::WITHDRAWAL === $type) {
$return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
}
if (TransactionType::WITHDRAWAL !== $type) {
$return = app('amount')->formatAnything($currency, $amount);
}
return $return;
}
public function getLocation(int $journalId): ?Location
{
/** @var TransactionJournal $journal */
@@ -351,86 +433,4 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
return $service->update($transactionGroup, $data);
}
private function expandJournal(TransactionJournal $journal): array
{
$array = $journal->toArray();
$array['transactions'] = [];
$array['meta'] = $journal->transactionJournalMeta->toArray();
$array['tags'] = $journal->tags->toArray();
$array['categories'] = $journal->categories->toArray();
$array['budgets'] = $journal->budgets->toArray();
$array['notes'] = $journal->notes->toArray();
$array['locations'] = [];
$array['attachments'] = $journal->attachments->toArray();
$array['links'] = [];
$array['piggy_bank_events'] = $journal->piggyBankEvents->toArray();
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
$array['transactions'][] = $this->expandTransaction($transaction);
}
return $array;
}
private function expandTransaction(Transaction $transaction): array
{
$array = $transaction->toArray();
$array['account'] = $transaction->account->toArray();
$array['budgets'] = [];
$array['categories'] = [];
foreach ($transaction->categories as $category) {
$array['categories'][] = $category->toArray();
}
foreach ($transaction->budgets as $budget) {
$array['budgets'][] = $budget->toArray();
}
return $array;
}
private function getFormattedAmount(TransactionJournal $journal): string
{
/** @var Transaction $transaction */
$transaction = $journal->transactions->first();
$currency = $transaction->transactionCurrency;
$type = $journal->transactionType->type;
$amount = app('steam')->positive($transaction->amount);
$return = '';
if (TransactionType::WITHDRAWAL === $type) {
$return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
}
if (TransactionType::WITHDRAWAL !== $type) {
$return = app('amount')->formatAnything($currency, $amount);
}
return $return;
}
private function getFormattedForeignAmount(TransactionJournal $journal): string
{
/** @var Transaction $transaction */
$transaction = $journal->transactions->first();
if (null === $transaction->foreign_amount || '' === $transaction->foreign_amount) {
return '';
}
if (0 === bccomp('0', $transaction->foreign_amount)) {
return '';
}
$currency = $transaction->foreignCurrency;
$type = $journal->transactionType->type;
$amount = app('steam')->positive($transaction->foreign_amount);
$return = '';
if (TransactionType::WITHDRAWAL === $type) {
$return = app('amount')->formatAnything($currency, app('steam')->negative($amount));
}
if (TransactionType::WITHDRAWAL !== $type) {
$return = app('amount')->formatAnything($currency, $amount);
}
return $return;
}
}

View File

@@ -112,6 +112,34 @@ class UserGroupRepository implements UserGroupRepositoryInterface
return $collection;
}
/**
* Because there is the chance that a group with this name already exists,
* Firefly III runs a little loop of combinations to make sure the group name is unique.
*/
private function createNewUserGroup(User $user): UserGroup
{
$loop = 0;
$groupName = $user->email;
$exists = true;
$existingGroup = null;
while ($exists && $loop < 10) {
$existingGroup = $this->findByName($groupName);
if (null === $existingGroup) {
$exists = false;
/** @var null|UserGroup $existingGroup */
$existingGroup = $this->store(['user' => $user, 'title' => $groupName]);
}
if (null !== $existingGroup) {
// group already exists
$groupName = sprintf('%s-%s', $user->email, substr(sha1(rand(1000, 9999).microtime()), 0, 4));
}
++$loop;
}
return $existingGroup;
}
public function findByName(string $title): ?UserGroup
{
return UserGroup::whereTitle($title)->first();
@@ -213,8 +241,8 @@ class UserGroupRepository implements UserGroupRepositoryInterface
if (
0 === $ownerCount
&& (0 === count($data['roles'])
|| (count($data['roles']) > 0 // @phpstan-ignore-line
&& !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)))) {
|| (count($data['roles']) > 0 // @phpstan-ignore-line
&& !in_array(UserRoleEnum::OWNER->value, $data['roles'], true)))) {
app('log')->debug('User needs to keep owner role in this group, refuse to act');
throw new FireflyException('The last owner in this user group must keep the "owner" role.');
@@ -239,34 +267,6 @@ class UserGroupRepository implements UserGroupRepositoryInterface
return $userGroup;
}
/**
* Because there is the chance that a group with this name already exists,
* Firefly III runs a little loop of combinations to make sure the group name is unique.
*/
private function createNewUserGroup(User $user): UserGroup
{
$loop = 0;
$groupName = $user->email;
$exists = true;
$existingGroup = null;
while ($exists && $loop < 10) {
$existingGroup = $this->findByName($groupName);
if (null === $existingGroup) {
$exists = false;
/** @var null|UserGroup $existingGroup */
$existingGroup = $this->store(['user' => $user, 'title' => $groupName]);
}
if (null !== $existingGroup) {
// group already exists
$groupName = sprintf('%s-%s', $user->email, substr(sha1(rand(1000, 9999).microtime()), 0, 4));
}
++$loop;
}
return $existingGroup;
}
private function simplifyListByName(array $roles): array
{
if (in_array(UserRoleEnum::OWNER->value, $roles, true)) {

View File

@@ -81,18 +81,18 @@ class BillRepository implements BillRepositoryInterface
$currencyId = $bill->transaction_currency_id;
$return[$currencyId] ??= [
'currency_id' => (string)$currency->id,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'native_currency_id' => (string)$default->id,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_code' => $default->code,
'native_currency_decimal_places' => $default->decimal_places,
'sum' => '0',
'native_sum' => '0',
'currency_id' => (string)$currency->id,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'native_currency_id' => (string)$default->id,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_code' => $default->code,
'native_currency_decimal_places' => $default->decimal_places,
'sum' => '0',
'native_sum' => '0',
];
/** @var TransactionJournal $transactionJournal */
@@ -154,18 +154,18 @@ class BillRepository implements BillRepositoryInterface
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
$nativeAverage = $converter->convert($currency, $default, $start, $average);
$return[$currencyId] ??= [
'currency_id' => (string)$currency->id,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'native_currency_id' => (string)$default->id,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_code' => $default->code,
'native_currency_decimal_places' => $default->decimal_places,
'sum' => '0',
'native_sum' => '0',
'currency_id' => (string)$currency->id,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_code' => $currency->code,
'currency_decimal_places' => $currency->decimal_places,
'native_currency_id' => (string)$default->id,
'native_currency_name' => $default->name,
'native_currency_symbol' => $default->symbol,
'native_currency_code' => $default->code,
'native_currency_decimal_places' => $default->decimal_places,
'sum' => '0',
'native_sum' => '0',
];
$return[$currencyId]['sum'] = bcadd($return[$currencyId]['sum'], bcmul($average, (string)$total));
$return[$currencyId]['native_sum'] = bcadd($return[$currencyId]['native_sum'], bcmul($nativeAverage, (string)$total));

View File

@@ -150,6 +150,14 @@ class CurrencyRepository implements CurrencyRepositoryInterface
return null;
}
private function countJournals(TransactionCurrency $currency): int
{
$count = $currency->transactions()->whereNull('deleted_at')->count() + $currency->transactionJournals()->whereNull('deleted_at')->count();
// also count foreign:
return $count + Transaction::where('foreign_currency_id', $currency->id)->count();
}
/**
* Returns ALL currencies, regardless of whether they are enabled or not.
*/
@@ -372,12 +380,4 @@ class CurrencyRepository implements CurrencyRepositoryInterface
return $service->update($currency, $data);
}
private function countJournals(TransactionCurrency $currency): int
{
$count = $currency->transactions()->whereNull('deleted_at')->count() + $currency->transactionJournals()->whereNull('deleted_at')->count();
// also count foreign:
return $count + Transaction::where('foreign_currency_id', $currency->id)->count();
}
}