From f2996dcebe91b8bf7d4d5d9f4d4c9a772e8bdae9 Mon Sep 17 00:00:00 2001 From: James Cole Date: Tue, 3 Feb 2026 20:24:16 +0100 Subject: [PATCH] Clean up event handlers and other code. --- .../Models/Recurrence/TriggerController.php | 4 +- .../Models/BudgetLimit/StoreRequest.php | 5 +- .../Correction/CorrectsCurrencies.php | 3 +- .../CorrectsTimezoneInformation.php | 2 - app/Factory/WebhookMessageFactory.php | 42 +++++ .../Webhook/StandardMessageGenerator.php | 82 +++++----- .../ProcessesNewTransactionGroup.php | 60 +++---- app/Models/AccountBalance.php | 52 ------- app/Policies/AccountBalancePolicy.php | 49 ------ .../Recurring/RecurringRepository.php | 138 ++++++++-------- .../RecurringRepositoryInterface.php | 1 + .../Models/AccountBalanceCalculator.php | 147 ++++++------------ mago.toml | 14 ++ 13 files changed, 245 insertions(+), 354 deletions(-) create mode 100644 app/Factory/WebhookMessageFactory.php delete mode 100644 app/Models/AccountBalance.php delete mode 100644 app/Policies/AccountBalancePolicy.php diff --git a/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php index 8880d7b307..b273a4e5b8 100644 --- a/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php +++ b/app/Api/V1/Controllers/Models/Recurrence/TriggerController.php @@ -79,9 +79,7 @@ class TriggerController extends Controller $groups = $job->getGroups(); $this->repository->markGroupsAsNow($groups); - $recurrence->latest_date = $backupDate; - $recurrence->latest_date_tz = $backupDate?->format('e'); - $recurrence->save(); + $recurrence = $this->repository->setLatestDate($recurrence, $backupDate); Preferences::mark(); // enrich groups and return them: diff --git a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php index b02b19dc9f..3b9437a18a 100644 --- a/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php +++ b/app/Api/V1/Requests/Models/BudgetLimit/StoreRequest.php @@ -26,6 +26,7 @@ namespace FireflyIII\Api\V1\Requests\Models\BudgetLimit; use Carbon\Carbon; use FireflyIII\Factory\TransactionCurrencyFactory; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Rules\IsBoolean; use FireflyIII\Rules\IsValidPositiveAmount; use FireflyIII\Support\Facades\Amount; @@ -98,8 +99,8 @@ class StoreRequest extends FormRequest if (null === $currency) { $currency = Amount::getPrimaryCurrency(); } - $currency->enabled = true; - $currency->save(); + $repository = app(CurrencyRepositoryInterface::class); + $repository->enable($currency); // validator already concluded start and end are valid dates: $start = Carbon::parse($data['start'], config('app.timezone')); diff --git a/app/Console/Commands/Correction/CorrectsCurrencies.php b/app/Console/Commands/Correction/CorrectsCurrencies.php index ba2fcf776e..3c15075b55 100644 --- a/app/Console/Commands/Correction/CorrectsCurrencies.php +++ b/app/Console/Commands/Correction/CorrectsCurrencies.php @@ -74,8 +74,7 @@ class CorrectsCurrencies extends Command ->where('accounts.user_group_id', $userGroup->id) ->where('account_meta.name', 'currency_id') ->groupBy('data') - ->get(['data']) - ; + ->get(['data']); foreach ($meta as $entry) { $found[] = (int) $entry->data; } diff --git a/app/Console/Commands/Correction/CorrectsTimezoneInformation.php b/app/Console/Commands/Correction/CorrectsTimezoneInformation.php index b746ea7ee9..a14e6ee98f 100644 --- a/app/Console/Commands/Correction/CorrectsTimezoneInformation.php +++ b/app/Console/Commands/Correction/CorrectsTimezoneInformation.php @@ -25,7 +25,6 @@ declare(strict_types=1); namespace FireflyIII\Console\Commands\Correction; use FireflyIII\Console\Commands\ShowsFriendlyMessages; -use FireflyIII\Models\AccountBalance; use FireflyIII\Models\AvailableBudget; use FireflyIII\Models\Bill; use FireflyIII\Models\BudgetLimit; @@ -47,7 +46,6 @@ class CorrectsTimezoneInformation extends Command use ShowsFriendlyMessages; public static array $models = [ - AccountBalance::class => ['date'], // done AvailableBudget::class => ['start_date', 'end_date'], // done Bill::class => ['date', 'end_date', 'extension_date'], // done BudgetLimit::class => ['start_date', 'end_date'], // done diff --git a/app/Factory/WebhookMessageFactory.php b/app/Factory/WebhookMessageFactory.php new file mode 100644 index 0000000000..cfc0313e92 --- /dev/null +++ b/app/Factory/WebhookMessageFactory.php @@ -0,0 +1,42 @@ +. + */ + +namespace FireflyIII\Factory; + +use FireflyIII\Models\Webhook; +use FireflyIII\Models\WebhookMessage; +use Illuminate\Support\Facades\Log; + +class WebhookMessageFactory +{ + public function create(Webhook $webhook, array $data): WebhookMessage { + $webhookMessage = new WebhookMessage(); + $webhookMessage->webhook()->associate($webhook); + $webhookMessage->sent = false; + $webhookMessage->errored = false; + $webhookMessage->uuid = $data['uuid']; + $webhookMessage->message = $data; + $webhookMessage->save(); + Log::debug(sprintf('Stored new webhook message #%d', $webhookMessage->id)); + return $webhookMessage; + } + +} diff --git a/app/Generator/Webhook/StandardMessageGenerator.php b/app/Generator/Webhook/StandardMessageGenerator.php index 3e5d3e4fb9..2e29705ec5 100644 --- a/app/Generator/Webhook/StandardMessageGenerator.php +++ b/app/Generator/Webhook/StandardMessageGenerator.php @@ -27,13 +27,13 @@ namespace FireflyIII\Generator\Webhook; use FireflyIII\Enums\WebhookResponse; use FireflyIII\Enums\WebhookTrigger; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Factory\WebhookMessageFactory; use FireflyIII\Models\Budget; use FireflyIII\Models\BudgetLimit; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\Webhook; -use FireflyIII\Models\WebhookMessage; use FireflyIII\Models\WebhookResponse as WebhookResponseModel; use FireflyIII\Models\WebhookTrigger as WebhookTriggerModel; use FireflyIII\Support\JsonApi\Enrichments\AccountEnrichment; @@ -55,11 +55,11 @@ use Symfony\Component\HttpFoundation\ParameterBag; */ class StandardMessageGenerator implements MessageGeneratorInterface { - private Collection $objects; + private Collection $objects; private WebhookTrigger $trigger; - private User $user; - private int $version = 0; - private Collection $webhooks; + private User $user; + private int $version = 0; + private Collection $webhooks; public function __construct() { @@ -77,10 +77,10 @@ class StandardMessageGenerator implements MessageGeneratorInterface // do some debugging Log::debug(sprintf( - 'StandardMessageGenerator will generate messages for %d object(s) and %d webhook(s).', - $this->objects->count(), - $this->webhooks->count() - )); + 'StandardMessageGenerator will generate messages for %d object(s) and %d webhook(s).', + $this->objects->count(), + $this->webhooks->count() + )); $this->run(); } @@ -92,8 +92,7 @@ class StandardMessageGenerator implements MessageGeneratorInterface ->leftJoin('webhook_triggers', 'webhook_webhook_trigger.webhook_trigger_id', 'webhook_triggers.id') ->where('active', true) ->whereIn('webhook_triggers.title', [$this->trigger->name, WebhookTrigger::ANY->name]) - ->get(['webhooks.*']) - ; + ->get(['webhooks.*']); } /** @@ -128,15 +127,15 @@ class StandardMessageGenerator implements MessageGeneratorInterface */ private function generateMessage(Webhook $webhook, Model $model): void { - $class = $model::class; + $class = $model::class; // Line is ignored because all of Firefly III's Models have an id property. Log::debug(sprintf('Now in generateMessage(#%d, %s#%d)', $webhook->id, $class, $model->id)); - $uuid = Uuid::uuid4(); + $uuid = Uuid::uuid4(); /** @var WebhookResponseModel $response */ - $response = $webhook->webhookResponses()->first(); + $response = $webhook->webhookResponses()->first(); $this->getTriggerTitles($webhook->webhookTriggers()->get()); - $basicMessage = [ + $basicMessage = [ 'uuid' => $uuid->toString(), 'user_id' => 0, 'user_group_id' => 0, @@ -181,17 +180,17 @@ class StandardMessageGenerator implements MessageGeneratorInterface switch ($responseTitle) { default: Log::error(sprintf( - 'The response code for webhook #%d is "%s" and the message generator cant handle it. Soft fail.', - $webhook->id, - $webhook->response - )); + 'The response code for webhook #%d is "%s" and the message generator cant handle it. Soft fail.', + $webhook->id, + $webhook->response + )); return; case WebhookResponse::BUDGET->name: $basicMessage['content'] = []; if ($model instanceof Budget) { - $enrichment = new BudgetEnrichment(); + $enrichment = new BudgetEnrichment(); $enrichment->setUser($model->user); /** @var Budget $model */ @@ -200,17 +199,17 @@ class StandardMessageGenerator implements MessageGeneratorInterface $basicMessage['content'] = $transformer->transform($model); } if ($model instanceof BudgetLimit) { - $user = $model->budget->user; - $enrichment = new BudgetLimitEnrichment(); + $user = $model->budget->user; + $enrichment = new BudgetLimitEnrichment(); $enrichment->setUser($user); - $parameters = new ParameterBag(); + $parameters = new ParameterBag(); $parameters->set('start', $model->start_date); $parameters->set('end', $model->end_date); /** @var BudgetLimit $model */ - $model = $enrichment->enrichSingle($model); - $transformer = new BudgetLimitTransformer(); + $model = $enrichment->enrichSingle($model); + $transformer = new BudgetLimitTransformer(); $transformer->setParameters($parameters); $basicMessage['content'] = $transformer->transform($model); } @@ -224,16 +223,16 @@ class StandardMessageGenerator implements MessageGeneratorInterface case WebhookResponse::TRANSACTIONS->name: /** @var TransactionGroup $model */ - $transformer = new TransactionGroupTransformer(); + $transformer = new TransactionGroupTransformer(); try { $basicMessage['content'] = $transformer->transformObject($model); } catch (FireflyException $e) { Log::error(sprintf( - 'The transformer could not include the requested transaction group for webhook #%d: %s', - $webhook->id, - $e->getMessage() - )); + 'The transformer could not include the requested transaction group for webhook #%d: %s', + $webhook->id, + $e->getMessage() + )); Log::error($e->getTraceAsString()); return; @@ -243,18 +242,19 @@ class StandardMessageGenerator implements MessageGeneratorInterface case WebhookResponse::ACCOUNTS->name: /** @var TransactionGroup $model */ - $accounts = $this->collectAccounts($model); - $enrichment = new AccountEnrichment(); + $accounts = $this->collectAccounts($model); + $enrichment = new AccountEnrichment(); $enrichment->setDate(null); $enrichment->setUser($model->user); - $accounts = $enrichment->enrich($accounts); + $accounts = $enrichment->enrich($accounts); foreach ($accounts as $account) { - $transformer = new AccountTransformer(); + $transformer = new AccountTransformer(); $transformer->setParameters(new ParameterBag()); $basicMessage['content'][] = $transformer->transform($account); } } - $this->storeMessage($webhook, $basicMessage); + $factory = new WebhookMessageFactory(); + $factory->create($webhook, $basicMessage); } public function getVersion(): int @@ -277,18 +277,6 @@ class StandardMessageGenerator implements MessageGeneratorInterface return $accounts->unique(); } - private function storeMessage(Webhook $webhook, array $message): void - { - $webhookMessage = new WebhookMessage(); - $webhookMessage->webhook()->associate($webhook); - $webhookMessage->sent = false; - $webhookMessage->errored = false; - $webhookMessage->uuid = $message['uuid']; - $webhookMessage->message = $message; - $webhookMessage->save(); - Log::debug(sprintf('Stored new webhook message #%d', $webhookMessage->id)); - } - public function setObjects(Collection $objects): void { $this->objects = $objects; diff --git a/app/Listeners/Model/TransactionGroup/ProcessesNewTransactionGroup.php b/app/Listeners/Model/TransactionGroup/ProcessesNewTransactionGroup.php index 34d367131d..4956227457 100644 --- a/app/Listeners/Model/TransactionGroup/ProcessesNewTransactionGroup.php +++ b/app/Listeners/Model/TransactionGroup/ProcessesNewTransactionGroup.php @@ -47,7 +47,7 @@ use Illuminate\Support\Facades\Log; class ProcessesNewTransactionGroup implements ShouldQueue { - public function handle(CreatedSingleTransactionGroup|UserRequestedBatchProcessing $event): void + public function handle(CreatedSingleTransactionGroup | UserRequestedBatchProcessing $event): void { $groupId = 0; $collection = new Collection(); @@ -60,7 +60,7 @@ class ProcessesNewTransactionGroup implements ShouldQueue Log::debug('User called UserRequestedBatchProcessing'); } - $setting = FireflyConfig::get('enable_batch_processing', false)->data; + $setting = FireflyConfig::get('enable_batch_processing', false)->data; if (true === $event->flags->batchSubmission && true === $setting) { Log::debug(sprintf('Will do nothing for group #%d because it is part of a batch.', $groupId)); @@ -104,35 +104,37 @@ class ProcessesNewTransactionGroup implements ShouldQueue $repository->markAsCompleted($set); } - private function recalculateRunningBalance(Collection $set): void + private function getFromInternalDate(array $ids): Carbon { - Log::debug('Now in recalculateRunningBalance'); - // find the earliest date in the set, based on date and _internal_previous_date - $earliest = $set->pluck('date')->sort()->first(); - $entries = TransactionJournalMeta::whereIn('transaction_journal_id', $set->pluck('id')->toArray())->where('name', '_internal_previous_date')->get([ - 'journal_meta.*', - ]); - $array = $entries->toArray(); + $entries = TransactionJournalMeta::whereIn('transaction_journal_id', $ids)->where('name', '_internal_previous_date')->get(['journal_meta.*',]); + $array = $entries->toArray(); + $return = today()->subDay(); if (count($array) > 0) { usort($array, function (array $a, array $b) { return Carbon::parse($a['data'])->gt(Carbon::parse($b['data'])); }); - /** @var Carbon $date */ - $date = Carbon::parse($array[0]['data']); - - /** @var Carbon $earliest */ - $earliest = $date->lt($earliest) ? $date : $earliest; + $date = Carbon::parse($array[0]['data']); + $return = $date->lt($return) ? $date : $return; } + return $return; + } + + private function recalculateRunningBalance(Collection $set): void + { + Log::debug('Now in recalculateRunningBalance'); + // find the earliest date in the set, based on date and _internal_previous_date + $earliest = $set->pluck('date')->sort()->first(); + $fromInternalDate = $this->getFromInternalDate($set->pluck('id')->toArray()); + $earliest = $fromInternalDate->lt($earliest) ? $fromInternalDate : $earliest; Log::debug(sprintf('Found earliest date: %s', $earliest->toW3cString())); // get accounts $accounts = Account::leftJoin('transactions', 'transactions.account_id', 'accounts.id') - ->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id') - ->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id') - ->whereIn('transaction_journals.id', $set->pluck('id')->toArray()) - ->get(['accounts.*']) - ; + ->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id') + ->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id') + ->whereIn('transaction_journals.id', $set->pluck('id')->toArray()) + ->get(['accounts.*']); Log::debug('Found accounts to process', $accounts->pluck('id')->toArray()); @@ -158,8 +160,8 @@ class ProcessesNewTransactionGroup implements ShouldQueue Log::debug(__METHOD__); /** @var TransactionJournal $first */ - $first = $set->first(); - $user = $first->user; + $first = $set->first(); + $user = $first->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); @@ -190,12 +192,12 @@ class ProcessesNewTransactionGroup implements ShouldQueue private function processRules(Collection $set): void { Log::debug(sprintf('Will now processRules for %d journal(s)', $set->count())); - $array = $set->pluck('id')->toArray(); + $array = $set->pluck('id')->toArray(); /** @var TransactionJournal $first */ - $first = $set->first(); - $journalIds = implode(',', $array); - $user = $first->user; + $first = $set->first(); + $journalIds = implode(',', $array); + $user = $first->user; Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: @@ -205,12 +207,12 @@ class ProcessesNewTransactionGroup implements ShouldQueue // add the groups to the rule engine. // it should run the rules in the group and cancel the group if necessary. Log::debug('Fire processRules with ALL store-journal rule groups.'); - $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); + $groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal'); // create and fire rule engine. - $newRuleEngine = app(RuleEngineInterface::class); + $newRuleEngine = app(RuleEngineInterface::class); $newRuleEngine->setUser($user); - $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); + $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); $newRuleEngine->setRuleGroups($groups); $newRuleEngine->fire(); } diff --git a/app/Models/AccountBalance.php b/app/Models/AccountBalance.php deleted file mode 100644 index 297698f42b..0000000000 --- a/app/Models/AccountBalance.php +++ /dev/null @@ -1,52 +0,0 @@ -belongsTo(Account::class); - } - - public function transactionCurrency(): BelongsTo - { - return $this->belongsTo(TransactionCurrency::class); - } - - protected function casts(): array - { - return ['date' => SeparateTimezoneCaster::class, 'balance' => 'string']; - } -} diff --git a/app/Policies/AccountBalancePolicy.php b/app/Policies/AccountBalancePolicy.php deleted file mode 100644 index 8ed26305c9..0000000000 --- a/app/Policies/AccountBalancePolicy.php +++ /dev/null @@ -1,49 +0,0 @@ -where('name', 'recurrence_id'); - $q1->where('data', json_encode((string) $recurrence->id)); + $q1->where('data', json_encode((string)$recurrence->id)); })->get(['journal_meta.transaction_journal_id']); // there are X journals made for this recurrence. Any of them meant for today? foreach ($set as $journalMeta) { $count = TransactionJournalMeta::where(static function (Builder $q2) use ($date): void { - $string = (string) $date; + $string = (string)$date; Log::debug(sprintf('Search for date: %s', json_encode($string))); $q2->where('name', 'recurrence_date'); $q2->where('data', json_encode($string)); @@ -104,8 +103,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte ->orderBy('active', 'DESC') ->orderBy('transaction_type_id', 'ASC') ->orderBy('title', 'ASC') - ->get() - ; + ->get(); } /** @@ -131,10 +129,9 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte { // grab ALL recurring transactions: return Recurrence::with(['TransactionCurrency', 'TransactionType', 'RecurrenceRepetitions', 'RecurrenceTransactions']) - ->orderBy('active', 'DESC') - ->orderBy('title', 'ASC') - ->get() - ; + ->orderBy('active', 'DESC') + ->orderBy('title', 'ASC') + ->get(); } public function getBillId(RecurrenceTransaction $recTransaction): ?int @@ -144,7 +141,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte /** @var RecurrenceTransactionMeta $meta */ foreach ($recTransaction->recurrenceTransactionMeta as $meta) { if ('bill_id' === $meta->name) { - $return = (int) $meta->value; + $return = (int)$meta->value; } } @@ -161,7 +158,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte /** @var RecurrenceTransactionMeta $meta */ foreach ($recTransaction->recurrenceTransactionMeta as $meta) { if ('budget_id' === $meta->name) { - $return = (int) $meta->value; + $return = (int)$meta->value; } } @@ -178,7 +175,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte /** @var RecurrenceTransactionMeta $meta */ foreach ($recTransaction->recurrenceTransactionMeta as $meta) { if ('category_id' === $meta->name) { - $return = (int) $meta->value; + $return = (int)$meta->value; } } @@ -195,7 +192,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte /** @var RecurrenceTransactionMeta $meta */ foreach ($recTransaction->recurrenceTransactionMeta as $meta) { if ('category_name' === $meta->name) { - $return = (string) $meta->value; + $return = (string)$meta->value; } } @@ -209,11 +206,10 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte { Log::debug(sprintf('Now in getJournalCount(#%d, "%s", "%s")', $recurrence->id, $start?->format('Y-m-d H:i:s'), $end?->format('Y-m-d H:i:s'))); $query = TransactionJournal::leftJoin('journal_meta', 'journal_meta.transaction_journal_id', '=', 'transaction_journals.id') - ->where('transaction_journals.user_id', $recurrence->user_id) - ->whereNull('transaction_journals.deleted_at') - ->where('journal_meta.name', 'recurrence_id') - ->where('journal_meta.data', '"'.$recurrence->id.'"') - ; + ->where('transaction_journals.user_id', $recurrence->user_id) + ->whereNull('transaction_journals.deleted_at') + ->where('journal_meta.name', 'recurrence_id') + ->where('journal_meta.data', '"' . $recurrence->id . '"'); if ($start instanceof Carbon) { $query->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00')); } @@ -232,13 +228,12 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte public function getJournalIds(Recurrence $recurrence): array { return TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') - ->where('transaction_journals.user_id', $this->user->id) - ->where('journal_meta.name', '=', 'recurrence_id') - ->where('journal_meta.data', '=', json_encode((string) $recurrence->id)) - ->get(['journal_meta.transaction_journal_id']) - ->pluck('transaction_journal_id') - ->toArray() - ; + ->where('transaction_journals.user_id', $this->user->id) + ->where('journal_meta.name', '=', 'recurrence_id') + ->where('journal_meta.data', '=', json_encode((string)$recurrence->id)) + ->get(['journal_meta.transaction_journal_id']) + ->pluck('transaction_journal_id') + ->toArray(); } /** @@ -249,7 +244,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte /** @var null|Note $note */ $note = $recurrence->notes()->first(); - return (string) $note?->text; + return (string)$note?->text; } public function getPiggyBank(RecurrenceTransaction $transaction): ?int @@ -259,7 +254,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte /** @var RecurrenceTransactionMeta $metaEntry */ foreach ($meta as $metaEntry) { if ('piggy_bank_id' === $metaEntry->name) { - return (int) $metaEntry->value; + return (int)$metaEntry->value; } } @@ -276,7 +271,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte /** @var RecurrenceMeta $meta */ foreach ($transaction->recurrenceTransactionMeta as $meta) { if ('tags' === $meta->name && '' !== $meta->value) { - $tags = json_decode((string) $meta->value, true, 512, JSON_THROW_ON_ERROR); + $tags = json_decode((string)$meta->value, true, 512, JSON_THROW_ON_ERROR); } } @@ -286,21 +281,20 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte public function getTransactionPaginator(Recurrence $recurrence, int $page, int $pageSize): LengthAwarePaginator { $journalMeta = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') - ->whereNull('transaction_journals.deleted_at') - ->where('transaction_journals.user_id', $this->user->id) - ->where('name', 'recurrence_id') - ->where('data', json_encode((string) $recurrence->id)) - ->get() - ->pluck('transaction_journal_id') - ->toArray() - ; + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->where('name', 'recurrence_id') + ->where('data', json_encode((string)$recurrence->id)) + ->get() + ->pluck('transaction_journal_id') + ->toArray(); $search = []; foreach ($journalMeta as $journalId) { - $search[] = (int) $journalId; + $search[] = (int)$journalId; } /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setUser($recurrence->user); $collector->withCategoryInformation()->withBudgetInformation()->setLimit($pageSize)->setPage($page)->withAccountInformation(); @@ -312,25 +306,24 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte public function getTransactions(Recurrence $recurrence): Collection { $journalMeta = TransactionJournalMeta::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'journal_meta.transaction_journal_id') - ->whereNull('transaction_journals.deleted_at') - ->where('transaction_journals.user_id', $this->user->id) - ->where('name', 'recurrence_id') - ->where('data', json_encode((string) $recurrence->id)) - ->get() - ->pluck('transaction_journal_id') - ->toArray() - ; + ->whereNull('transaction_journals.deleted_at') + ->where('transaction_journals.user_id', $this->user->id) + ->where('name', 'recurrence_id') + ->where('data', json_encode((string)$recurrence->id)) + ->get() + ->pluck('transaction_journal_id') + ->toArray(); $search = []; foreach ($journalMeta as $journalId) { - $search[] = (int) $journalId; + $search[] = (int)$journalId; } if (0 === count($search)) { return new Collection(); } /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector->setUser($recurrence->user); $collector->withCategoryInformation()->withBudgetInformation()->withAccountInformation(); @@ -443,51 +436,51 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte if (is_array($language)) { $language = 'en_US'; } - $language = (string) $language; + $language = (string)$language; if ('daily' === $repetition->repetition_type) { - return (string) trans('firefly.recurring_daily', [], $language); + return (string)trans('firefly.recurring_daily', [], $language); } if ('weekly' === $repetition->repetition_type) { $dayOfWeek = trans(sprintf('config.dow_%s', $repetition->repetition_moment), [], $language); if ($repetition->repetition_skip > 0) { - return (string) trans('firefly.recurring_weekly_skip', ['weekday' => $dayOfWeek, 'skip' => $repetition->repetition_skip + 1], $language); + return (string)trans('firefly.recurring_weekly_skip', ['weekday' => $dayOfWeek, 'skip' => $repetition->repetition_skip + 1], $language); } - return (string) trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek], $language); + return (string)trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek], $language); } if ('monthly' === $repetition->repetition_type) { if ($repetition->repetition_skip > 0) { - return (string) trans( + return (string)trans( 'firefly.recurring_monthly_skip', - ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip + 1], + ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip + 1], $language ); } - return (string) trans( + return (string)trans( 'firefly.recurring_monthly', - ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip - 1], + ['dayOfMonth' => $repetition->repetition_moment, 'skip' => $repetition->repetition_skip - 1], $language ); } if ('ndom' === $repetition->repetition_type) { - $parts = explode(',', $repetition->repetition_moment); + $parts = explode(',', $repetition->repetition_moment); // first part is number of week, second is weekday. $dayOfWeek = trans(sprintf('config.dow_%s', $parts[1]), [], $language); - return (string) trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $parts[0]], $language); + return (string)trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $parts[0]], $language); } if ('yearly' === $repetition->repetition_type) { - $today = today(config('app.timezone'))->endOfYear(); - $repDate = Carbon::createFromFormat('Y-m-d', $repetition->repetition_moment); + $today = today(config('app.timezone'))->endOfYear(); + $repDate = Carbon::createFromFormat('Y-m-d', $repetition->repetition_moment); if (!$repDate instanceof Carbon) { $repDate = clone $today; } - $diffInYears = (int) $today->diffInYears($repDate, true); + $diffInYears = (int)$today->diffInYears($repDate, true); $repDate->addYears($diffInYears); // technically not necessary. - $string = $repDate->isoFormat((string) trans('config.month_and_day_no_year_js')); + $string = $repDate->isoFormat((string)trans('config.month_and_day_no_year_js')); - return (string) trans('firefly.recurring_yearly', ['date' => $string], $language); + return (string)trans('firefly.recurring_yearly', ['date' => $string], $language); } return ''; @@ -519,16 +512,16 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte public function totalTransactions(Recurrence $recurrence, RecurrenceRepetition $repetition): int { // if repeat = null just return 0. - if (null === $recurrence->repeat_until && 0 === (int) $recurrence->repetitions) { + if (null === $recurrence->repeat_until && 0 === (int)$recurrence->repetitions) { return 0; } // expect X transactions then stop. Return that number - if (null === $recurrence->repeat_until && 0 !== (int) $recurrence->repetitions) { - return (int) $recurrence->repetitions; + if (null === $recurrence->repeat_until && 0 !== (int)$recurrence->repetitions) { + return (int)$recurrence->repetitions; } // need to calculate, this depends on the repetition: - if (null !== $recurrence->repeat_until && 0 === (int) $recurrence->repetitions) { + if (null !== $recurrence->repeat_until && 0 === (int)$recurrence->repetitions) { $occurrences = $this->getOccurrencesInRange($repetition, $recurrence->first_date ?? today(), $recurrence->repeat_until); return count($occurrences); @@ -545,7 +538,7 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte $occurrences = []; $mutator = clone $start; $mutator->startOfDay(); - $skipMod = $repetition->repetition_skip + 1; + $skipMod = $repetition->repetition_skip + 1; Log::debug(sprintf('Calculating occurrences for rep type "%s"', $repetition->repetition_type)); Log::debug(sprintf('Mutator is now: %s', $mutator->format('Y-m-d'))); @@ -594,4 +587,13 @@ class RecurringRepository implements RecurringRepositoryInterface, UserGroupInte } } } + + #[\Override] + public function setLatestDate(Recurrence $recurrence, ?Carbon $date): Recurrence + { + $recurrence->latest_date = $date; + $recurrence->latest_date_tz = $date?->format('e'); + $recurrence->save(); + return $recurrence; + } } diff --git a/app/Repositories/Recurring/RecurringRepositoryInterface.php b/app/Repositories/Recurring/RecurringRepositoryInterface.php index 9fb715e545..39fde3c6d6 100644 --- a/app/Repositories/Recurring/RecurringRepositoryInterface.php +++ b/app/Repositories/Recurring/RecurringRepositoryInterface.php @@ -48,6 +48,7 @@ use Illuminate\Support\Collection; */ interface RecurringRepositoryInterface { + public function setLatestDate(Recurrence $recurrence, Carbon $date): Recurrence; public function createdPreviously(Recurrence $recurrence, Carbon $date): bool; /** diff --git a/app/Support/Models/AccountBalanceCalculator.php b/app/Support/Models/AccountBalanceCalculator.php index 0e61f45077..b7ba22bb58 100644 --- a/app/Support/Models/AccountBalanceCalculator.php +++ b/app/Support/Models/AccountBalanceCalculator.php @@ -25,12 +25,8 @@ declare(strict_types=1); namespace FireflyIII\Support\Models; use Carbon\Carbon; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\Account; -use FireflyIII\Models\AccountBalance; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionJournal; -use FireflyIII\Support\Facades\Amount; use FireflyIII\Support\Facades\FireflyConfig; use FireflyIII\Support\Facades\Steam; use Illuminate\Support\Collection; @@ -58,7 +54,7 @@ class AccountBalanceCalculator if ($forced) { Transaction::whereNull('deleted_at')->update(['balance_dirty' => true]); // also delete account balances. - AccountBalance::whereNotNull('created_at')->delete(); + // AccountBalance::whereNotNull('created_at')->delete(); } $object = new self(); self::optimizedCalculation(new Collection()); @@ -70,17 +66,17 @@ class AccountBalanceCalculator return; } Log::debug(__METHOD__); - $object = new self(); + $object = new self(); - $set = []; + $set = []; foreach ($transactionJournal->transactions as $transaction) { $set[$transaction->account_id] = $transaction->account; } $accounts = new Collection()->push(...$set); // find meta value: - $date = $transactionJournal->date; - $meta = $transactionJournal->transactionJournalMeta()->where('name', '_internal_previous_date')->where('data', '!=', '')->first(); + $date = $transactionJournal->date; + $meta = $transactionJournal->transactionJournalMeta()->where('name', '_internal_previous_date')->where('data', '!=', '')->first(); Log::debug(sprintf('Date used is "%s"', $date->toW3cString())); if (null !== $meta) { $date = Carbon::parse($meta->data); @@ -97,29 +93,28 @@ class AccountBalanceCalculator return '0'; } - $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->whereNull('transactions.deleted_at') - ->where('transactions.transaction_currency_id', $currencyId) - ->whereNull('transaction_journals.deleted_at') + $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') + ->whereNull('transactions.deleted_at') + ->where('transactions.transaction_currency_id', $currencyId) + ->whereNull('transaction_journals.deleted_at') // this order is the same as GroupCollector - ->orderBy('transaction_journals.date', 'DESC') - ->orderBy('transaction_journals.order', 'ASC') - ->orderBy('transaction_journals.id', 'DESC') - ->orderBy('transaction_journals.description', 'DESC') - ->orderBy('transactions.amount', 'DESC') - ->where('transactions.account_id', $accountId) - ; + ->orderBy('transaction_journals.date', 'DESC') + ->orderBy('transaction_journals.order', 'ASC') + ->orderBy('transaction_journals.id', 'DESC') + ->orderBy('transaction_journals.description', 'DESC') + ->orderBy('transactions.amount', 'DESC') + ->where('transactions.account_id', $accountId); $query->where('transaction_journals.date', '<', $notBefore); - $first = $query->first([ - 'transactions.id', - 'transactions.balance_dirty', - 'transactions.transaction_currency_id', - 'transaction_journals.date', - 'transactions.account_id', - 'transactions.amount', - 'transactions.balance_after', - ]); + $first = $query->first([ + 'transactions.id', + 'transactions.balance_dirty', + 'transactions.transaction_currency_id', + 'transaction_journals.date', + 'transactions.account_id', + 'transactions.amount', + 'transactions.balance_after', + ]); if (null === $first) { Log::debug(sprintf('Found no transactions for currency #%d and account #%d, return 0.', $currencyId, $accountId)); @@ -127,13 +122,13 @@ class AccountBalanceCalculator return '0'; } - $balance = (string) ($first->balance_after ?? '0'); + $balance = (string)($first->balance_after ?? '0'); Log::debug(sprintf( - 'getLatestBalance: found balance: %s in transaction #%d on moment %s', - Steam::bcround($balance, 2), - $first->id ?? 0, - $notBefore->format('Y-m-d H:i:s') - )); + 'getLatestBalance: found balance: %s in transaction #%d on moment %s', + Steam::bcround($balance, 2), + $first->id ?? 0, + $notBefore->format('Y-m-d H:i:s') + )); return $balance; } @@ -152,15 +147,14 @@ class AccountBalanceCalculator $balances = []; $count = 0; $query = Transaction::leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id') - ->whereNull('transactions.deleted_at') - ->whereNull('transaction_journals.deleted_at') + ->whereNull('transactions.deleted_at') + ->whereNull('transaction_journals.deleted_at') // this order is the same as GroupCollector, but in the exact reverse. - ->orderBy('transaction_journals.date', 'asc') - ->orderBy('transaction_journals.order', 'desc') - ->orderBy('transaction_journals.id', 'asc') - ->orderBy('transaction_journals.description', 'asc') - ->orderBy('transactions.amount', 'asc') - ; + ->orderBy('transaction_journals.date', 'asc') + ->orderBy('transaction_journals.order', 'desc') + ->orderBy('transaction_journals.id', 'asc') + ->orderBy('transaction_journals.description', 'asc') + ->orderBy('transactions.amount', 'asc'); if ($accounts->count() > 0) { $query->whereIn('transactions.account_id', $accounts->pluck('id')->toArray()); } @@ -168,14 +162,14 @@ class AccountBalanceCalculator $query->where('transaction_journals.date', '>=', $notBefore); } - $set = $query->get([ - 'transactions.id', - 'transactions.balance_dirty', - 'transactions.transaction_currency_id', - 'transaction_journals.date', - 'transactions.account_id', - 'transactions.amount', - ]); + $set = $query->get([ + 'transactions.id', + 'transactions.balance_dirty', + 'transactions.transaction_currency_id', + 'transaction_journals.date', + 'transactions.account_id', + 'transactions.amount', + ]); Log::debug(sprintf('Found %d transaction(s)', $set->count())); // the balance value is an array. @@ -192,8 +186,8 @@ class AccountBalanceCalculator ]; // before and after are easy: - $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; - $after = bcadd($before, (string) $entry->amount); + $before = $balances[$entry->account_id][$entry->transaction_currency_id][0]; + $after = bcadd($before, (string)$entry->amount); // Log::debug(sprintf('Before:%s, after:%s', Steam::bcround($before, 2), Steam::bcround($after, 2))); @@ -215,51 +209,4 @@ class AccountBalanceCalculator // save all collected balances in their respective account objects. // $this->storeAccountBalances($balances); } - - private function storeAccountBalances(array $balances): void - { - /** - * @var int $accountId - * @var array $currencies - */ - foreach ($balances as $accountId => $currencies) { - /** @var null|Account $account */ - $account = Account::find($accountId); - if (null === $account) { - Log::error(sprintf('Could not find account #%d, will not save account balance.', $accountId)); - - continue; - } - - /** - * @var int $currencyId - * @var array $balance - */ - foreach ($currencies as $currencyId => $balance) { - try { - $currency = Amount::getTransactionCurrencyById($currencyId); - } catch (FireflyException) { - Log::error(sprintf('Could not find currency #%d, will not save account balance.', $currencyId)); - - continue; - } - - /** @var AccountBalance $object */ - $object = $account - ->accountBalances() - ->firstOrCreate([ - 'title' => 'running_balance', - 'balance' => '0', - 'transaction_currency_id' => $currencyId, - 'date' => $balance[1], - 'date_tz' => $balance[1]?->format('e'), - ]) - ; - $object->balance = $balance[0]; - $object->date = $balance[1]; - $object->date_tz = $balance[1]?->format('e'); - $object->saveQuietly(); - } - } - } } diff --git a/mago.toml b/mago.toml index 18ed0bf381..e641346d84 100644 --- a/mago.toml +++ b/mago.toml @@ -41,3 +41,17 @@ strict-list-index-checks = false no-boolean-literal-comparison = false check-missing-type-hints = false register-super-globals = true + +# deze mag iedereen +[[guard.perimeter.rules]] +namespace = "FireflyIII\\" +permit = ["Carbon\\Carbon"] + +# guard rules +[[guard.perimeter.rules]] +namespace = "FireflyIII\\Services" +permit = ["@self", "@native","FireflyIII\\Models"] + +[[guard.perimeter.rules]] +namespace = "FireflyIII\\Transformers" +permit = ["@self", "@native","FireflyIII\\Models"]