From ca3922d00ab872871031313e1830946be725304b Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 6 Dec 2025 13:50:51 +0100 Subject: [PATCH] Fix #11313 --- .../TriggeredStoredTransactionGroup.php | 36 +++++++++++ .../Events/StoredGroupEventHandler.php | 27 +++++---- .../RuleGroup/ExecutionController.php | 59 ++++++++++--------- app/Providers/EventServiceProvider.php | 4 ++ 4 files changed, 89 insertions(+), 37 deletions(-) create mode 100644 app/Events/Model/TransactionGroup/TriggeredStoredTransactionGroup.php diff --git a/app/Events/Model/TransactionGroup/TriggeredStoredTransactionGroup.php b/app/Events/Model/TransactionGroup/TriggeredStoredTransactionGroup.php new file mode 100644 index 0000000000..858119b6b0 --- /dev/null +++ b/app/Events/Model/TransactionGroup/TriggeredStoredTransactionGroup.php @@ -0,0 +1,36 @@ +. + */ + +namespace FireflyIII\Events\Model\TransactionGroup; + +use FireflyIII\Events\Event; +use FireflyIII\Models\TransactionGroup; +use Illuminate\Queue\SerializesModels; + +class TriggeredStoredTransactionGroup extends Event +{ + use SerializesModels; + + /** + * Create a new event instance. + */ + public function __construct(public TransactionGroup $transactionGroup) {} +} diff --git a/app/Handlers/Events/StoredGroupEventHandler.php b/app/Handlers/Events/StoredGroupEventHandler.php index fd33908b32..381cdd542e 100644 --- a/app/Handlers/Events/StoredGroupEventHandler.php +++ b/app/Handlers/Events/StoredGroupEventHandler.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Handlers\Events; use FireflyIII\Enums\WebhookTrigger; +use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup; use FireflyIII\Events\RequestedSendWebhookMessages; use FireflyIII\Events\StoredTransactionGroup; use FireflyIII\Generator\Webhook\MessageGeneratorInterface; @@ -51,6 +52,12 @@ class StoredGroupEventHandler $this->removePeriodStatistics($event); } + public function triggerRulesManually(TriggeredStoredTransactionGroup $event): void + { + $newEvent = new StoredTransactionGroup($event->transactionGroup, true, false); + $this->processRules($newEvent); + } + /** * This method grabs all the users rules and processes them. */ @@ -63,14 +70,14 @@ class StoredGroupEventHandler } Log::debug('Now in StoredGroupEventHandler::processRules()'); - $journals = $storedGroupEvent->transactionGroup->transactionJournals; - $array = []; + $journals = $storedGroupEvent->transactionGroup->transactionJournals; + $array = []; /** @var TransactionJournal $journal */ foreach ($journals as $journal) { $array[] = $journal->id; } - $journalIds = implode(',', $array); + $journalIds = implode(',', $array); Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); // collect rules: @@ -79,10 +86,10 @@ class StoredGroupEventHandler // add the groups to the rule engine. // it should run the rules in the group and cancel the group if necessary. - $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($storedGroupEvent->transactionGroup->user); $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); $newRuleEngine->setRuleGroups($groups); @@ -91,7 +98,7 @@ class StoredGroupEventHandler private function recalculateCredit(StoredTransactionGroup $event): void { - $group = $event->transactionGroup; + $group = $event->transactionGroup; /** @var CreditRecalculateService $object */ $object = app(CreditRecalculateService::class); @@ -107,10 +114,10 @@ class StoredGroupEventHandler /** @var TransactionJournal $journal */ foreach ($event->transactionGroup->transactionJournals as $journal) { /** @var null|Transaction $source */ - $source = $journal->transactions()->where('amount', '<', '0')->first(); + $source = $journal->transactions()->where('amount', '<', '0')->first(); /** @var null|Transaction $dest */ - $dest = $journal->transactions()->where('amount', '>', '0')->first(); + $dest = $journal->transactions()->where('amount', '>', '0')->first(); if (null !== $source) { $repository->deleteStatisticsForModel($source->account, $journal->date); @@ -145,14 +152,14 @@ class StoredGroupEventHandler private function triggerWebhooks(StoredTransactionGroup $storedGroupEvent): void { Log::debug(__METHOD__); - $group = $storedGroupEvent->transactionGroup; + $group = $storedGroupEvent->transactionGroup; if (false === $storedGroupEvent->fireWebhooks) { Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); return; } - $user = $group->user; + $user = $group->user; /** @var MessageGeneratorInterface $engine */ $engine = app(MessageGeneratorInterface::class); diff --git a/app/Http/Controllers/RuleGroup/ExecutionController.php b/app/Http/Controllers/RuleGroup/ExecutionController.php index 3cd97826cc..3a96f6138c 100644 --- a/app/Http/Controllers/RuleGroup/ExecutionController.php +++ b/app/Http/Controllers/RuleGroup/ExecutionController.php @@ -26,14 +26,16 @@ namespace FireflyIII\Http\Controllers\RuleGroup; use Carbon\Carbon; use Exception; +use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup; +use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\SelectTransactionsRequest; use FireflyIII\Models\RuleGroup; -use FireflyIII\TransactionRules\Engine\RuleEngineInterface; -use FireflyIII\User; +use FireflyIII\Models\TransactionGroup; +use FireflyIII\Repositories\Account\AccountRepositoryInterface; use Illuminate\Contracts\View\Factory; use Illuminate\Http\RedirectResponse; -use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Log; use Illuminate\View\View; /** @@ -41,18 +43,20 @@ use Illuminate\View\View; */ class ExecutionController extends Controller { + private AccountRepositoryInterface $repository; + /** * ExecutionController constructor. */ public function __construct() { parent::__construct(); - + $this->repository = app(AccountRepositoryInterface::class); $this->middleware( function ($request, $next) { - app('view')->share('title', (string) trans('firefly.rules')); + app('view')->share('title', (string)trans('firefly.rules')); app('view')->share('mainTitleIcon', 'fa-random'); - + $this->repository->setUser(auth()->user()); return $next($request); } @@ -67,34 +71,35 @@ class ExecutionController extends Controller public function execute(SelectTransactionsRequest $request, RuleGroup $ruleGroup): RedirectResponse { // Get parameters specified by the user - /** @var User $user */ - $user = auth()->user(); - $accounts = implode(',', $request->get('accounts')); - // create new rule engine: - $newRuleEngine = app(RuleEngineInterface::class); - $newRuleEngine->setUser($user); - + $accounts = $request->get('accounts'); + $set = $this->repository->getAccountsById($accounts); + /** @var GroupCollectorInterface $collector */ + $collector = app(GroupCollectorInterface::class); + $collector->setAccounts($set); // add date operators. if (null !== $request->get('start')) { $startDate = new Carbon($request->get('start')); - $newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]); + $collector->setStart($startDate); } if (null !== $request->get('end')) { $endDate = new Carbon($request->get('end')); - $newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]); + $collector->setEnd($endDate); + } + $final = $collector->getGroups(); + $ids = $final->pluck('id')->toArray(); + Log::debug(sprintf('Found %d groups collected from %d account(s)', $final->count(), $set->count())); + foreach (array_chunk($ids, 1337) as $setOfIds) { + Log::debug(sprintf('Now processing %d groups', count($setOfIds))); + $groups = TransactionGroup::whereIn('id', $setOfIds)->get(); + /** @var TransactionGroup $group */ + foreach ($groups as $group) { + Log::debug(sprintf('Processing group #%d.', $group->id)); + event(new TriggeredStoredTransactionGroup($group)); + } } - // add extra operators: - $newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]); - - // set rules: - // #10427, file rule group and not the set of rules. - $collection = new Collection()->push($ruleGroup); - $newRuleEngine->setRuleGroups($collection); - $newRuleEngine->fire(); - // Tell the user that the job is queued - session()->flash('success', (string) trans('firefly.applied_rule_group_selection', ['title' => $ruleGroup->title])); + session()->flash('success', (string)trans('firefly.applied_rule_group_selection', ['title' => $ruleGroup->title])); return redirect()->route('rules.index'); } @@ -104,9 +109,9 @@ class ExecutionController extends Controller * * @return Factory|View */ - public function selectTransactions(RuleGroup $ruleGroup): Factory|\Illuminate\Contracts\View\View + public function selectTransactions(RuleGroup $ruleGroup): Factory | \Illuminate\Contracts\View\View { - $subTitle = (string) trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]); + $subTitle = (string)trans('firefly.apply_rule_group_selection', ['title' => $ruleGroup->title]); return view('rules.rule-group.select-transactions', ['ruleGroup' => $ruleGroup, 'subTitle' => $subTitle]); } diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 1b221013f5..4ced9fe70b 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -33,6 +33,7 @@ use FireflyIII\Events\Model\PiggyBank\ChangedAmount; use FireflyIII\Events\Model\PiggyBank\ChangedName; use FireflyIII\Events\Model\Rule\RuleActionFailedOnArray; use FireflyIII\Events\Model\Rule\RuleActionFailedOnObject; +use FireflyIII\Events\Model\TransactionGroup\TriggeredStoredTransactionGroup; use FireflyIII\Events\NewVersionAvailable; use FireflyIII\Events\Preferences\UserGroupChangedPrimaryCurrency; use FireflyIII\Events\RegisteredUser; @@ -131,6 +132,9 @@ class EventServiceProvider extends ServiceProvider StoredTransactionGroup::class => [ 'FireflyIII\Handlers\Events\StoredGroupEventHandler@runAllHandlers', ], + TriggeredStoredTransactionGroup::class => [ + 'FireflyIII\Handlers\Events\StoredGroupEventHandler@triggerRulesManually', + ], // is a Transaction Journal related event. UpdatedTransactionGroup::class => [ 'FireflyIII\Handlers\Events\UpdatedGroupEventHandler@runAllHandlers',