From a433ddcd7e81099cbbb41c6887386baf152ca680 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 31 Jan 2026 10:27:12 +0100 Subject: [PATCH] Clean up events. --- .../V1/Controllers/System/BatchController.php | 4 +- .../UserRequestedBatchProcessing.php | 34 ++++++++++ .../ProcessesNewTransactionGroup.php | 67 +++++++++++-------- .../PeriodStatisticRepository.php | 8 +++ composer.lock | 51 +++++++------- 5 files changed, 111 insertions(+), 53 deletions(-) create mode 100644 app/Events/Model/TransactionGroup/UserRequestedBatchProcessing.php diff --git a/app/Api/V1/Controllers/System/BatchController.php b/app/Api/V1/Controllers/System/BatchController.php index 6190591015..a28d2b8c16 100644 --- a/app/Api/V1/Controllers/System/BatchController.php +++ b/app/Api/V1/Controllers/System/BatchController.php @@ -27,6 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\System; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup; use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags; +use FireflyIII\Events\Model\TransactionGroup\UserRequestedBatchProcessing; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Http\JsonResponse; @@ -64,7 +65,8 @@ class BatchController extends Controller } $flags = new TransactionGroupEventFlags(); $flags->applyRules = 'true' === $request->get('apply_rules'); - event(new CreatedSingleTransactionGroup($group, $flags)); + event(new UserRequestedBatchProcessing($flags)); + // event(new CreatedSingleTransactionGroup($group, $flags)); return response()->json([], 204); } diff --git a/app/Events/Model/TransactionGroup/UserRequestedBatchProcessing.php b/app/Events/Model/TransactionGroup/UserRequestedBatchProcessing.php new file mode 100644 index 0000000000..68d6b67c4f --- /dev/null +++ b/app/Events/Model/TransactionGroup/UserRequestedBatchProcessing.php @@ -0,0 +1,34 @@ +. + */ + +namespace FireflyIII\Events\Model\TransactionGroup; + +use FireflyIII\Events\Event; +use Illuminate\Support\Facades\Log; + +class UserRequestedBatchProcessing extends Event +{ + public function __construct(public TransactionGroupEventFlags $flags) + { + Log::debug(__METHOD__); + } + +} diff --git a/app/Listeners/Model/TransactionGroup/ProcessesNewTransactionGroup.php b/app/Listeners/Model/TransactionGroup/ProcessesNewTransactionGroup.php index 3a89e20598..02ddb02800 100644 --- a/app/Listeners/Model/TransactionGroup/ProcessesNewTransactionGroup.php +++ b/app/Listeners/Model/TransactionGroup/ProcessesNewTransactionGroup.php @@ -27,10 +27,12 @@ namespace FireflyIII\Listeners\Model\TransactionGroup; use Carbon\Carbon; use FireflyIII\Enums\WebhookTrigger; use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup; +use FireflyIII\Events\Model\TransactionGroup\UserRequestedBatchProcessing; use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending; use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\TransactionGroup; +use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournalMeta; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface; @@ -45,21 +47,26 @@ use Illuminate\Support\Facades\Log; class ProcessesNewTransactionGroup implements ShouldQueue { - public function handle(CreatedSingleTransactionGroup $event): void + public function handle(CreatedSingleTransactionGroup | UserRequestedBatchProcessing $event): void { - Log::debug(sprintf('In ProcessesNewTransactionGroup::handle(#%d)', $event->transactionGroup->id)); - $setting = FireflyConfig::get('enable_batch_processing', false)->data; + $groupId = 0; + $collection = new Collection(); + if ($event instanceof CreatedSingleTransactionGroup) { + Log::debug(sprintf('In ProcessesNewTransactionGroup::handle(#%d)', $event->transactionGroup->id)); + $groupId = $event->transactionGroup->id; + $collection = $event->transactionGroup->transactionJournals; + } + if ($event instanceof UserRequestedBatchProcessing) { + Log::debug('User called UserRequestedBatchProcessing'); + } + + $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 (setting:%s).', - $event->transactionGroup->id, - var_export($setting, true) - )); + Log::debug(sprintf('Will do nothing for group #%d because it is part of a batch.', $groupId)); return; } - Log::debug(sprintf('Will join group #%d with all other open transaction groups and process them.', $event->transactionGroup->id)); - $collection = $event->transactionGroup->transactionJournals; + Log::debug(sprintf('Will (joined with group #%d) collect all open transaction groups and process them.', $groupId)); $repository = app(JournalRepositoryInterface::class); $set = $collection->merge($repository->getUncompletedJournals()); if (0 === $set->count()) { @@ -67,6 +74,7 @@ class ProcessesNewTransactionGroup implements ShouldQueue return; } + Log::debug(sprintf('Set count is %d', $set->count())); if (!$event->flags->applyRules) { Log::debug(sprintf('Will NOT process rules for %d journal(s)', $set->count())); } @@ -85,12 +93,10 @@ class ProcessesNewTransactionGroup implements ShouldQueue if ($event->flags->fireWebhooks) { $this->fireWebhooks($set); } - // always remove old statistics. + // always remove old relevant statistics. $this->removePeriodStatistics($set); // recalculate running balance if necessary. - - Log::debug('Observe "created" of a transaction.'); if (true === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) { $this->recalculateRunningBalance($set); } @@ -103,9 +109,7 @@ class ProcessesNewTransactionGroup implements ShouldQueue 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.*', - ]); + $entries = TransactionJournalMeta::whereIn('transaction_journal_id', $set->pluck('id')->toArray())->where('name', '_internal_previous_date')->get(['journal_meta.*']); $array = $entries->toArray(); if (count($array) > 0) { usort($array, function (array $a, array $b) { @@ -114,16 +118,19 @@ class ProcessesNewTransactionGroup implements ShouldQueue /** @var Carbon $date */ $date = Carbon::parse($array[0]['data']); + /** @var Carbon $earliest */ $earliest = $date->lt($earliest) ? $date : $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()); AccountBalanceCalculator::optimizedCalculation($accounts, $earliest); } @@ -143,7 +150,9 @@ class ProcessesNewTransactionGroup implements ShouldQueue $groups = TransactionGroup::whereIn('id', array_unique($set->pluck('transaction_group_id')->toArray()))->get(); Log::debug(__METHOD__); - $user = $set->first()->user; + /** @var TransactionJournal $first */ + $first = $set->first(); + $user = $first->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); @@ -174,9 +183,11 @@ 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(); - $journalIds = implode(',', $array); - $user = $set->first()->user; + $array = $set->pluck('id')->toArray(); + /** @var TransactionJournal $first */ + $first = $set->first(); + $journalIds = implode(',', $array); + $user = $first->user; Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: @@ -186,12 +197,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/Repositories/PeriodStatistic/PeriodStatisticRepository.php b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php index d71146d559..b469b41c46 100644 --- a/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php +++ b/app/Repositories/PeriodStatistic/PeriodStatisticRepository.php @@ -187,9 +187,11 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U Log::debug(sprintf('Delete statistics for %d transaction journals.', count($set))); // collect all transactions: $transactions = Transaction::whereIn('transaction_journal_id', $set->pluck('id')->toArray())->get(['transactions.*']); + Log::debug('Collected transaction IDs', $transactions->pluck('id')->toArray()); // collect all accounts and delete stats: $accounts = Account::whereIn('id', $transactions->pluck('account_id')->toArray())->get(['accounts.*']); + Log::debug('Collected account IDs', $accounts->pluck('id')->toArray()); $dates = $set->pluck('date'); $this->deleteStatisticsForType(Account::class, $accounts, $dates); @@ -202,6 +204,7 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U ->pluck('category_id') ->toArray() )->get(['categories.*']); + Log::debug('Collected category IDs', $categories->pluck('id')->toArray()); $this->deleteStatisticsForType(Category::class, $categories, $dates); // budgets, same thing @@ -213,6 +216,7 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U ->pluck('budget_id') ->toArray() )->get(['budgets.*']); + Log::debug('Collected budget IDs', $categories->pluck('id')->toArray()); $this->deleteStatisticsForType(Budget::class, $budgets, $dates); // tags @@ -224,16 +228,20 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U ->pluck('tag_id') ->toArray() )->get(['tags.*']); + Log::debug('Collected tag IDs', $categories->pluck('id')->toArray()); $this->deleteStatisticsForType(Tag::class, $tags, $dates); // remove for no tag, no cat, etc. if (0 === $categories->count()) { + Log::debug('No categories, delete "no_category" stats.'); $this->deleteStatisticsForPrefix('no_category', $dates); } if (0 === $budgets->count()) { + Log::debug('No budgets, delete "no_category" stats.'); $this->deleteStatisticsForPrefix('no_budget', $dates); } if (0 === $tags->count()) { + Log::debug('No tags, delete "no_category" stats.'); $this->deleteStatisticsForPrefix('no_tag', $dates); } } diff --git a/composer.lock b/composer.lock index c7008c0e96..b027c3e363 100644 --- a/composer.lock +++ b/composer.lock @@ -130,16 +130,16 @@ }, { "name": "brick/math", - "version": "0.14.1", + "version": "0.14.2", "source": { "type": "git", "url": "https://github.com/brick/math.git", - "reference": "f05858549e5f9d7bb45875a75583240a38a281d0" + "reference": "55c950aa71a2cabc1d8f2bec1f8a7020bd244aa2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/f05858549e5f9d7bb45875a75583240a38a281d0", - "reference": "f05858549e5f9d7bb45875a75583240a38a281d0", + "url": "https://api.github.com/repos/brick/math/zipball/55c950aa71a2cabc1d8f2bec1f8a7020bd244aa2", + "reference": "55c950aa71a2cabc1d8f2bec1f8a7020bd244aa2", "shasum": "" }, "require": { @@ -178,7 +178,7 @@ ], "support": { "issues": "https://github.com/brick/math/issues", - "source": "https://github.com/brick/math/tree/0.14.1" + "source": "https://github.com/brick/math/tree/0.14.2" }, "funding": [ { @@ -186,7 +186,7 @@ "type": "github" } ], - "time": "2025-11-24T14:40:29+00:00" + "time": "2026-01-30T14:03:11+00:00" }, { "name": "carbonphp/carbon-doctrine-types", @@ -3724,16 +3724,16 @@ }, { "name": "nesbot/carbon", - "version": "3.11.0", + "version": "3.11.1", "source": { "type": "git", "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "bdb375400dcd162624531666db4799b36b64e4a1" + "reference": "f438fcc98f92babee98381d399c65336f3a3827f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/bdb375400dcd162624531666db4799b36b64e4a1", - "reference": "bdb375400dcd162624531666db4799b36b64e4a1", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/f438fcc98f92babee98381d399c65336f3a3827f", + "reference": "f438fcc98f92babee98381d399c65336f3a3827f", "shasum": "" }, "require": { @@ -3757,7 +3757,7 @@ "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan": "^2.1.22", "phpunit/phpunit": "^10.5.53", - "squizlabs/php_codesniffer": "^3.13.4" + "squizlabs/php_codesniffer": "^3.13.4 || ^4.0.0" }, "bin": [ "bin/carbon" @@ -3800,14 +3800,14 @@ } ], "description": "An API extension for DateTime that supports 281 different languages.", - "homepage": "https://carbon.nesbot.com", + "homepage": "https://carbonphp.github.io/carbon/", "keywords": [ "date", "datetime", "time" ], "support": { - "docs": "https://carbon.nesbot.com/docs", + "docs": "https://carbonphp.github.io/carbon/guide/getting-started/introduction.html", "issues": "https://github.com/CarbonPHP/carbon/issues", "source": "https://github.com/CarbonPHP/carbon" }, @@ -3825,7 +3825,7 @@ "type": "tidelift" } ], - "time": "2025-12-02T21:04:28+00:00" + "time": "2026-01-29T09:26:29+00:00" }, { "name": "nette/schema", @@ -10530,16 +10530,16 @@ }, { "name": "fruitcake/laravel-debugbar", - "version": "v4.0.3", + "version": "v4.0.5", "source": { "type": "git", "url": "https://github.com/fruitcake/laravel-debugbar.git", - "reference": "9eec3770a304c52d1dd71577b44bbb9ab904f5d8" + "reference": "1da86437d28f36baf3bb9841d77e74cb639372a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/9eec3770a304c52d1dd71577b44bbb9ab904f5d8", - "reference": "9eec3770a304c52d1dd71577b44bbb9ab904f5d8", + "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/1da86437d28f36baf3bb9841d77e74cb639372a9", + "reference": "1da86437d28f36baf3bb9841d77e74cb639372a9", "shasum": "" }, "require": { @@ -10616,7 +10616,7 @@ ], "support": { "issues": "https://github.com/fruitcake/laravel-debugbar/issues", - "source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.3" + "source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.5" }, "funding": [ { @@ -10628,7 +10628,7 @@ "type": "github" } ], - "time": "2026-01-26T18:25:58+00:00" + "time": "2026-01-29T19:18:02+00:00" }, { "name": "hamcrest/hamcrest-php", @@ -11414,11 +11414,11 @@ }, { "name": "phpstan/phpstan", - "version": "2.1.37", + "version": "2.1.38", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/28cd424c5ea984128c95cfa7ea658808e8954e49", - "reference": "28cd424c5ea984128c95cfa7ea658808e8954e49", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dfaf1f530e1663aa167bc3e52197adb221582629", + "reference": "dfaf1f530e1663aa167bc3e52197adb221582629", "shasum": "" }, "require": { @@ -11463,7 +11463,7 @@ "type": "github" } ], - "time": "2026-01-24T08:21:55+00:00" + "time": "2026-01-30T17:12:46+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -13199,5 +13199,8 @@ "ext-xmlwriter": "*" }, "platform-dev": {}, + "platform-overrides": { + "php": "8.4" + }, "plugin-api-version": "2.9.0" }