diff --git a/app/Api/V1/Controllers/Models/Transaction/UpdateController.php b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php index 6e79d27c5a..695bace167 100644 --- a/app/Api/V1/Controllers/Models/Transaction/UpdateController.php +++ b/app/Api/V1/Controllers/Models/Transaction/UpdateController.php @@ -27,6 +27,7 @@ namespace FireflyIII\Api\V1\Controllers\Models\Transaction; use FireflyIII\Api\V1\Controllers\Controller; use FireflyIII\Api\V1\Requests\Models\Transaction\UpdateRequest; use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags; +use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects; use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup; use FireflyIII\Helpers\Collector\GroupCollectorInterface; use FireflyIII\Models\TransactionGroup; @@ -55,7 +56,7 @@ class UpdateController extends Controller parent::__construct(); $this->middleware(function ($request, $next) { /** @var User $admin */ - $admin = auth()->user(); + $admin = auth()->user(); $this->groupRepository = app(TransactionGroupRepositoryInterface::class); $this->groupRepository->setUser($admin); @@ -73,51 +74,51 @@ class UpdateController extends Controller public function update(UpdateRequest $request, TransactionGroup $transactionGroup): JsonResponse { Log::debug('Now in update routine for transaction group'); - $data = $request->getAll(); - $oldHash = $this->groupRepository->getCompareHash($transactionGroup); - $transactionGroup = $this->groupRepository->update($transactionGroup, $data); - $newHash = $this->groupRepository->getCompareHash($transactionGroup); - $manager = $this->getManager(); + $data = $request->getAll(); + $oldHash = $this->groupRepository->getCompareHash($transactionGroup); + $transactionGroup = $this->groupRepository->update($transactionGroup, $data); + $newHash = $this->groupRepository->getCompareHash($transactionGroup); + $manager = $this->getManager(); Preferences::mark(); - $applyRules = $data['apply_rules'] ?? true; - $fireWebhooks = $data['fire_webhooks'] ?? true; - $runRecalculations = $oldHash !== $newHash; + $applyRules = $data['apply_rules'] ?? true; + $fireWebhooks = $data['fire_webhooks'] ?? true; + $runRecalculations = $oldHash !== $newHash; $flags = new TransactionGroupEventFlags(); $flags->applyRules = $applyRules; $flags->fireWebhooks = $fireWebhooks; $flags->recalculateCredit = $runRecalculations; - event(new UpdatedSingleTransactionGroup($transactionGroup, $flags)); + $objects = TransactionGroupEventObjects::collectFromTransactionGroup($transactionGroup); + event(new UpdatedSingleTransactionGroup($flags, $objects)); /** @var User $admin */ - $admin = auth()->user(); + $admin = auth()->user(); // use new group collector: /** @var GroupCollectorInterface $collector */ - $collector = app(GroupCollectorInterface::class); + $collector = app(GroupCollectorInterface::class); $collector ->setUser($admin) // filter on transaction group. ->setTransactionGroup($transactionGroup) // all info needed for the API: - ->withAPIInformation() - ; + ->withAPIInformation(); - $selectedGroup = $collector->getGroups()->first(); + $selectedGroup = $collector->getGroups()->first(); if (null === $selectedGroup) { throw new NotFoundHttpException(); } // enrich - $enrichment = new TransactionGroupEnrichment(); + $enrichment = new TransactionGroupEnrichment(); $enrichment->setUser($admin); - $selectedGroup = $enrichment->enrichSingle($selectedGroup); + $selectedGroup = $enrichment->enrichSingle($selectedGroup); /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); + $transformer = app(TransactionGroupTransformer::class); $transformer->setParameters($this->parameters); - $resource = new Item($selectedGroup, $transformer, 'transactions'); + $resource = new Item($selectedGroup, $transformer, 'transactions'); return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE); } diff --git a/app/Console/Commands/Correction/CorrectsGroupAccounts.php b/app/Console/Commands/Correction/CorrectsGroupAccounts.php index 6db8df460a..88e029cefb 100644 --- a/app/Console/Commands/Correction/CorrectsGroupAccounts.php +++ b/app/Console/Commands/Correction/CorrectsGroupAccounts.php @@ -26,6 +26,7 @@ namespace FireflyIII\Console\Commands\Correction; use FireflyIII\Console\Commands\ShowsFriendlyMessages; use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags; +use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects; use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup; use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; @@ -49,18 +50,20 @@ class CorrectsGroupAccounts extends Command /** @var TransactionJournal $journal */ foreach ($res as $journal) { - if ((int) $journal->the_count > 1) { - $groups[] = (int) $journal->transaction_group_id; + if ((int)$journal->the_count > 1) { + $groups[] = (int)$journal->transaction_group_id; } } + $flags = new TransactionGroupEventFlags(); + $flags->applyRules = true; + $flags->fireWebhooks = true; + $flags->recalculateCredit = true; + $objects = new TransactionGroupEventObjects(); foreach ($groups as $groupId) { - $group = TransactionGroup::find($groupId); - $flags = new TransactionGroupEventFlags(); - $flags->applyRules = true; - $flags->fireWebhooks = true; - $flags->recalculateCredit = true; - event(new UpdatedSingleTransactionGroup($group, $flags)); + $group = TransactionGroup::find($groupId); + $objects->appendFromTransactionGroup($group); } + event(new UpdatedSingleTransactionGroup($flags, $objects)); return 0; } diff --git a/app/Events/Model/TransactionGroup/TransactionGroupEventObjects.php b/app/Events/Model/TransactionGroup/TransactionGroupEventObjects.php index 2d7279fbd2..73fb21aba5 100644 --- a/app/Events/Model/TransactionGroup/TransactionGroupEventObjects.php +++ b/app/Events/Model/TransactionGroup/TransactionGroupEventObjects.php @@ -41,21 +41,26 @@ class TransactionGroupEventObjects { Log::debug(sprintf('collectFromTransactionGroup(#%d)', $transactionGroup->id)); $object = new self(); - $object->transactionGroups->push($transactionGroup); - - /** @var TransactionJournal $journal */ - foreach ($transactionGroup->transactionJournals as $journal) { - $object->transactionJournals->push($journal); - $object->budgets = $object->tags->merge($journal->budgets); - $object->categories = $object->tags->merge($journal->categories); - $object->tags = $object->tags->merge($journal->tags); - - /** @var Transaction $transaction */ - foreach ($journal->transactions as $transaction) { - $object->accounts->push($transaction->account); - } - } + $object->appendFromTransactionGroup($transactionGroup); return $object; } + + public function appendFromTransactionGroup(TransactionGroup $transactionGroup): void + { + $this->transactionGroups->push($transactionGroup); + + /** @var TransactionJournal $journal */ + foreach ($transactionGroup->transactionJournals as $journal) { + $this->transactionJournals->push($journal); + $this->budgets = $this->budgets->merge($journal->budgets); + $this->categories = $this->categories->merge($journal->categories); + $this->tags = $this->tags->merge($journal->tags); + + /** @var Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + $this->accounts->push($transaction->account); + } + } + } } diff --git a/app/Events/Model/TransactionGroup/UpdatedSingleTransactionGroup.php b/app/Events/Model/TransactionGroup/UpdatedSingleTransactionGroup.php index 265a0418f9..436bf07a86 100644 --- a/app/Events/Model/TransactionGroup/UpdatedSingleTransactionGroup.php +++ b/app/Events/Model/TransactionGroup/UpdatedSingleTransactionGroup.php @@ -35,8 +35,5 @@ class UpdatedSingleTransactionGroup extends Event /** * Create a new event instance. */ - public function __construct( - public TransactionGroup $transactionGroup, - public TransactionGroupEventFlags $flags - ) {} + public function __construct(public TransactionGroupEventFlags $flags, public TransactionGroupEventObjects $objects) {} } diff --git a/app/Http/Controllers/Transaction/BulkController.php b/app/Http/Controllers/Transaction/BulkController.php index 4f2fa115d1..e2469cbb14 100644 --- a/app/Http/Controllers/Transaction/BulkController.php +++ b/app/Http/Controllers/Transaction/BulkController.php @@ -25,6 +25,7 @@ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Transaction; use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags; +use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects; use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\BulkEditJournalRequest; @@ -57,7 +58,7 @@ class BulkController extends Controller $this->middleware(function ($request, $next) { $this->repository = app(JournalRepositoryInterface::class); - app('view')->share('title', (string) trans('firefly.transactions')); + app('view')->share('title', (string)trans('firefly.transactions')); app('view')->share('mainTitleIcon', 'fa-exchange'); return $next($request); @@ -71,9 +72,9 @@ class BulkController extends Controller * * @return Factory|View */ - public function edit(array $journals): Factory|\Illuminate\Contracts\View\View + public function edit(array $journals): Factory | \Illuminate\Contracts\View\View { - $subTitle = (string) trans('firefly.mass_bulk_journals'); + $subTitle = (string)trans('firefly.mass_bulk_journals'); $this->rememberPreviousUrl('transactions.bulk-edit.url'); @@ -84,7 +85,7 @@ class BulkController extends Controller $budgetRepos = app(BudgetRepositoryInterface::class); $budgetList = app('expandedform')->makeSelectListWithEmpty($budgetRepos->getActiveBudgets()); - return view('transactions.bulk.edit', ['journals' => $journals, 'subTitle' => $subTitle, 'budgetList' => $budgetList]); + return view('transactions.bulk.edit', ['journals' => $journals, 'subTitle' => $subTitle, 'budgetList' => $budgetList]); } /** @@ -92,18 +93,18 @@ class BulkController extends Controller * * @return Application|Redirector|RedirectResponse */ - public function update(BulkEditJournalRequest $request): Redirector|RedirectResponse + public function update(BulkEditJournalRequest $request): Redirector | RedirectResponse { $journalIds = $request->get('journals'); $journalIds = is_array($journalIds) ? $journalIds : []; - $ignoreCategory = 1 === (int) $request->get('ignore_category'); - $ignoreBudget = 1 === (int) $request->get('ignore_budget'); + $ignoreCategory = 1 === (int)$request->get('ignore_category'); + $ignoreBudget = 1 === (int)$request->get('ignore_budget'); $tagsAction = $request->get('tags_action'); $collection = new Collection(); $count = 0; foreach ($journalIds as $journalId) { - $journalId = (int) $journalId; + $journalId = (int)$journalId; $journal = $this->repository->find($journalId); if (null !== $journal) { $resultA = $this->updateJournalBudget($journal, $ignoreBudget, $request->integer('budget_id')); @@ -116,13 +117,15 @@ class BulkController extends Controller } } - $flags = new TransactionGroupEventFlags(); + $flags = new TransactionGroupEventFlags(); + $objects = new TransactionGroupEventObjects(); // run rules on changed journals: /** @var TransactionJournal $journal */ foreach ($collection as $journal) { - event(new UpdatedSingleTransactionGroup($journal->transactionGroup, $flags)); + $objects->appendFromTransactionGroup($journal->transactionGroup); } + event(new UpdatedSingleTransactionGroup($flags, $objects)); Preferences::mark(); $request->session()->flash('success', trans_choice('firefly.mass_edited_transactions_success', $count)); diff --git a/app/Http/Controllers/Transaction/ConvertController.php b/app/Http/Controllers/Transaction/ConvertController.php index beb6de1b75..89c97857d8 100644 --- a/app/Http/Controllers/Transaction/ConvertController.php +++ b/app/Http/Controllers/Transaction/ConvertController.php @@ -27,6 +27,7 @@ use Exception; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags; +use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects; use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; @@ -72,7 +73,7 @@ class ConvertController extends Controller // some useful repositories: $this->middleware(function ($request, $next) { $this->accountRepository = app(AccountRepositoryInterface::class); - app('view')->share('title', (string) trans('firefly.transactions')); + app('view')->share('title', (string)trans('firefly.transactions')); app('view')->share('mainTitleIcon', 'fa-exchange'); return $next($request); @@ -86,23 +87,23 @@ class ConvertController extends Controller * * @throws Exception */ - public function index(TransactionType $destinationType, TransactionGroup $group): Factory|\Illuminate\Contracts\View\View|Redirector|RedirectResponse + public function index(TransactionType $destinationType, TransactionGroup $group): Factory | \Illuminate\Contracts\View\View | Redirector | RedirectResponse { if (!$this->isEditableGroup($group)) { return $this->redirectGroupToAccount($group); } /** @var TransactionGroupTransformer $transformer */ - $transformer = app(TransactionGroupTransformer::class); + $transformer = app(TransactionGroupTransformer::class); /** @var TransactionJournal $first */ - $first = $group->transactionJournals()->first(); - $sourceType = $first->transactionType; + $first = $group->transactionJournals()->first(); + $sourceType = $first->transactionType; - $groupTitle = $group->title ?? $first->description; - $groupArray = $transformer->transformObject($group); - $subTitle = (string) trans('firefly.convert_to_'.$destinationType->type, ['description' => $groupTitle]); - $subTitleIcon = 'fa-exchange'; + $groupTitle = $group->title ?? $first->description; + $groupArray = $transformer->transformObject($group); + $subTitle = (string)trans('firefly.convert_to_' . $destinationType->type, ['description' => $groupTitle]); + $subTitleIcon = 'fa-exchange'; // get a list of asset accounts and liabilities and stuff, in various combinations: $validDepositSources = $this->getValidDepositSources(); @@ -111,11 +112,11 @@ class ConvertController extends Controller $assets = $this->getAssetAccounts(); // old input variables: - $preFilled = ['source_name' => old('source_name')]; + $preFilled = ['source_name' => old('source_name')]; if ($sourceType->type === $destinationType->type) { // cannot convert to its own type. Log::debug('This is already a transaction of the expected type..'); - session()->flash('info', (string) trans('firefly.convert_is_already_type_'.$destinationType->type)); + session()->flash('info', (string)trans('firefly.convert_is_already_type_' . $destinationType->type)); return redirect(route('transactions.show', [$group->id])); } @@ -141,26 +142,26 @@ class ConvertController extends Controller // make repositories $liabilityTypes = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value]; $accountList = $this->accountRepository->getActiveAccountsByType([ - AccountTypeEnum::REVENUE->value, - AccountTypeEnum::CASH->value, - AccountTypeEnum::LOAN->value, - AccountTypeEnum::DEBT->value, - AccountTypeEnum::MORTGAGE->value, - ]); + AccountTypeEnum::REVENUE->value, + AccountTypeEnum::CASH->value, + AccountTypeEnum::LOAN->value, + AccountTypeEnum::DEBT->value, + AccountTypeEnum::MORTGAGE->value, + ]); $grouped = []; // group accounts: /** @var Account $account */ foreach ($accountList as $account) { - $role = (string) $this->accountRepository->getMetaValue($account, 'account_role'); - $name = $account->name; + $role = (string)$this->accountRepository->getMetaValue($account, 'account_role'); + $name = $account->name; if ('' === $role) { $role = 'no_account_type'; } // maybe it's a liability thing: if (in_array($account->accountType->type, $liabilityTypes, true)) { - $role = 'l_'.$account->accountType->type; + $role = 'l_' . $account->accountType->type; } if (AccountTypeEnum::CASH->value === $account->accountType->type) { $role = 'cash_account'; @@ -170,7 +171,7 @@ class ConvertController extends Controller $role = 'revenue_account'; } - $key = (string) trans('firefly.opt_group_'.$role); + $key = (string)trans('firefly.opt_group_' . $role); $grouped[$key][$account->id] = $name; } @@ -182,26 +183,26 @@ class ConvertController extends Controller // make repositories $liabilityTypes = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value]; $accountList = $this->accountRepository->getActiveAccountsByType([ - AccountTypeEnum::EXPENSE->value, - AccountTypeEnum::CASH->value, - AccountTypeEnum::LOAN->value, - AccountTypeEnum::DEBT->value, - AccountTypeEnum::MORTGAGE->value, - ]); + AccountTypeEnum::EXPENSE->value, + AccountTypeEnum::CASH->value, + AccountTypeEnum::LOAN->value, + AccountTypeEnum::DEBT->value, + AccountTypeEnum::MORTGAGE->value, + ]); $grouped = []; // group accounts: /** @var Account $account */ foreach ($accountList as $account) { - $role = (string) $this->accountRepository->getMetaValue($account, 'account_role'); - $name = $account->name; + $role = (string)$this->accountRepository->getMetaValue($account, 'account_role'); + $name = $account->name; if ('' === $role) { $role = 'no_account_type'; } // maybe it's a liability thing: if (in_array($account->accountType->type, $liabilityTypes, true)) { - $role = 'l_'.$account->accountType->type; + $role = 'l_' . $account->accountType->type; } if (AccountTypeEnum::CASH->value === $account->accountType->type) { $role = 'cash_account'; @@ -211,7 +212,7 @@ class ConvertController extends Controller $role = 'expense_account'; } - $key = (string) trans('firefly.opt_group_'.$role); + $key = (string)trans('firefly.opt_group_' . $role); $grouped[$key][$account->id] = $name; } @@ -225,23 +226,23 @@ class ConvertController extends Controller { // make repositories $accountList = $this->accountRepository->getActiveAccountsByType([ - AccountTypeEnum::LOAN->value, - AccountTypeEnum::DEBT->value, - AccountTypeEnum::MORTGAGE->value, - ]); + AccountTypeEnum::LOAN->value, + AccountTypeEnum::DEBT->value, + AccountTypeEnum::MORTGAGE->value, + ]); $grouped = []; // group accounts: /** @var Account $account */ foreach ($accountList as $account) { - $date = today()->endOfDay(); + $date = today()->endOfDay(); Log::debug(sprintf('getLiabilities: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); // 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized. // $balance = Steam::finalAccountBalance($account, $date)['balance']; $balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'] ?? '0'; $currency = $this->accountRepository->getAccountCurrency($account) ?? $this->primaryCurrency; $role = sprintf('l_%s', $account->accountType->type); - $key = (string) trans(sprintf('firefly.opt_group_%s', $role)); + $key = (string)trans(sprintf('firefly.opt_group_%s', $role)); $grouped[$key][$account->id] = sprintf('%s (%s)', $account->name, Amount::formatAnything($currency, $balance, false)); } @@ -260,19 +261,19 @@ class ConvertController extends Controller // group accounts: /** @var Account $account */ foreach ($accountList as $account) { - $date = today()->endOfDay(); + $date = today()->endOfDay(); Log::debug(sprintf('getAssetAccounts: Call finalAccountBalance with date/time "%s"', $date->toIso8601String())); // 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized. // $balance = Steam::finalAccountBalance($account, $date)['balance']; - $balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'] ?? '0'; + $balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'] ?? '0'; - $currency = $this->accountRepository->getAccountCurrency($account) ?? $this->primaryCurrency; - $role = (string) $this->accountRepository->getMetaValue($account, 'account_role'); + $currency = $this->accountRepository->getAccountCurrency($account) ?? $this->primaryCurrency; + $role = (string)$this->accountRepository->getMetaValue($account, 'account_role'); if ('' === $role) { $role = 'no_account_type'; } - $key = (string) trans(sprintf('firefly.opt_group_%s', $role)); + $key = (string)trans(sprintf('firefly.opt_group_%s', $role)); $grouped[$key][$account->id] = sprintf('%s (%s)', $account->name, Amount::formatAnything($currency, $balance, false)); } @@ -305,9 +306,10 @@ class ConvertController extends Controller // correct transfers: $group->refresh(); - session()->flash('success', (string) trans('firefly.converted_to_'.$destinationType->type)); - $flags = new TransactionGroupEventFlags(); - event(new UpdatedSingleTransactionGroup($group, $flags)); + session()->flash('success', (string)trans('firefly.converted_to_' . $destinationType->type)); + $flags = new TransactionGroupEventFlags(); + $objects = TransactionGroupEventObjects::collectFromTransactionGroup($group); + event(new UpdatedSingleTransactionGroup($flags, $objects)); return redirect(route('transactions.show', [$group->id])); } @@ -318,22 +320,22 @@ class ConvertController extends Controller private function convertJournal(TransactionJournal $journal, TransactionType $transactionType, array $data): TransactionJournal { /** @var AccountValidator $validator */ - $validator = app(AccountValidator::class); + $validator = app(AccountValidator::class); $validator->setUser(auth()->user()); $validator->setTransactionType($transactionType->type); - $sourceId = $data['source_id'][$journal->id] ?? null; - $sourceName = $data['source_name'][$journal->id] ?? null; - $destinationId = $data['destination_id'][$journal->id] ?? null; - $destinationName = $data['destination_name'][$journal->id] ?? null; + $sourceId = $data['source_id'][$journal->id] ?? null; + $sourceName = $data['source_name'][$journal->id] ?? null; + $destinationId = $data['destination_id'][$journal->id] ?? null; + $destinationName = $data['destination_name'][$journal->id] ?? null; // double check it's not an empty string. - $sourceId = '' === $sourceId || null === $sourceId ? null : (int) $sourceId; - $sourceName = '' === $sourceName ? null : (string) $sourceName; - $destinationId = '' === $destinationId || null === $destinationId ? null : (int) $destinationId; - $destinationName = '' === $destinationName ? null : (string) $destinationName; - $validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]); - $validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]); + $sourceId = '' === $sourceId || null === $sourceId ? null : (int)$sourceId; + $sourceName = '' === $sourceName ? null : (string)$sourceName; + $destinationId = '' === $destinationId || null === $destinationId ? null : (int)$destinationId; + $destinationName = '' === $destinationName ? null : (string)$destinationName; + $validSource = $validator->validateSource(['id' => $sourceId, 'name' => $sourceName]); + $validDestination = $validator->validateDestination(['id' => $destinationId, 'name' => $destinationName]); if (false === $validSource) { throw new FireflyException(sprintf(trans('firefly.convert_invalid_source'), $journal->id)); @@ -344,7 +346,7 @@ class ConvertController extends Controller // TODO typeOverrule: the account validator may have another opinion on the transaction type. - $update = [ + $update = [ 'source_id' => $sourceId, 'source_name' => $sourceName, 'destination_id' => $destinationId, @@ -358,9 +360,9 @@ class ConvertController extends Controller // also set the currency to the currency of the source account, in case you're converting a deposit into a transfer. if (TransactionTypeEnum::TRANSFER->value === $transactionType->type && TransactionTypeEnum::DEPOSIT->value === $journal->transactionType->type) { - $source = $this->accountRepository->find((int) $sourceId); + $source = $this->accountRepository->find((int)$sourceId); $sourceCurrency = $this->accountRepository->getAccountCurrency($source); - $dest = $this->accountRepository->find((int) $destinationId); + $dest = $this->accountRepository->find((int)$destinationId); $destCurrency = $this->accountRepository->getAccountCurrency($dest); if ( $sourceCurrency instanceof TransactionCurrency @@ -375,9 +377,9 @@ class ConvertController extends Controller // same thing for converting a withdrawal into a transfer, but with the currency of the destination account. if (TransactionTypeEnum::TRANSFER->value === $transactionType->type && TransactionTypeEnum::WITHDRAWAL->value === $journal->transactionType->type) { - $source = $this->accountRepository->find((int) $sourceId); + $source = $this->accountRepository->find((int)$sourceId); $sourceCurrency = $this->accountRepository->getAccountCurrency($source); - $dest = $this->accountRepository->find((int) $destinationId); + $dest = $this->accountRepository->find((int)$destinationId); $destCurrency = $this->accountRepository->getAccountCurrency($dest); if ( $sourceCurrency instanceof TransactionCurrency @@ -391,7 +393,7 @@ class ConvertController extends Controller } /** @var JournalUpdateService $service */ - $service = app(JournalUpdateService::class); + $service = app(JournalUpdateService::class); $service->setTransactionJournal($journal); $service->setData($update); $service->update(); diff --git a/app/Http/Controllers/Transaction/MassController.php b/app/Http/Controllers/Transaction/MassController.php index ee318e7b6e..08d7d1b989 100644 --- a/app/Http/Controllers/Transaction/MassController.php +++ b/app/Http/Controllers/Transaction/MassController.php @@ -27,6 +27,7 @@ use Carbon\Carbon; use FireflyIII\Enums\AccountTypeEnum; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags; +use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects; use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; @@ -61,7 +62,7 @@ class MassController extends Controller parent::__construct(); $this->middleware(function ($request, $next) { - app('view')->share('title', (string) trans('firefly.transactions')); + app('view')->share('title', (string)trans('firefly.transactions')); app('view')->share('mainTitleIcon', 'fa-exchange'); $this->repository = app(JournalRepositoryInterface::class); @@ -74,7 +75,7 @@ class MassController extends Controller */ public function delete(array $journals): IlluminateView { - $subTitle = (string) trans('firefly.mass_delete_journals'); + $subTitle = (string)trans('firefly.mass_delete_journals'); // put previous url in session $this->rememberPreviousUrl('transactions.mass-delete.url'); @@ -87,7 +88,7 @@ class MassController extends Controller * * @return Application|Redirector|RedirectResponse */ - public function destroy(MassDeleteJournalRequest $request): Redirector|RedirectResponse + public function destroy(MassDeleteJournalRequest $request): Redirector | RedirectResponse { Log::debug(sprintf('Now in %s', __METHOD__)); $ids = $request->get('confirm_mass_delete'); @@ -100,8 +101,8 @@ class MassController extends Controller Log::debug(sprintf('Searching for ID #%d', $journalId)); /** @var null|TransactionJournal $journal */ - $journal = $this->repository->find((int) $journalId); - if (null !== $journal && (int) $journalId === $journal->id) { + $journal = $this->repository->find((int)$journalId); + if (null !== $journal && (int)$journalId === $journal->id) { $this->repository->destroyJournal($journal); ++$count; Log::debug(sprintf('Deleted transaction journal #%d', $journalId)); @@ -123,22 +124,22 @@ class MassController extends Controller */ public function edit(array $journals): IlluminateView { - $subTitle = (string) trans('firefly.mass_edit_journals'); + $subTitle = (string)trans('firefly.mass_edit_journals'); /** @var AccountRepositoryInterface $accountRepository */ - $accountRepository = app(AccountRepositoryInterface::class); + $accountRepository = app(AccountRepositoryInterface::class); // valid withdrawal sources: - $array = array_keys(config(sprintf('firefly.source_dests.%s', TransactionTypeEnum::WITHDRAWAL->value))); - $withdrawalSources = $accountRepository->getAccountsByType($array); + $array = array_keys(config(sprintf('firefly.source_dests.%s', TransactionTypeEnum::WITHDRAWAL->value))); + $withdrawalSources = $accountRepository->getAccountsByType($array); // valid deposit destinations: $array = config(sprintf('firefly.source_dests.%s.%s', TransactionTypeEnum::DEPOSIT->value, AccountTypeEnum::REVENUE->value)); $depositDestinations = $accountRepository->getAccountsByType($array); /** @var BudgetRepositoryInterface $budgetRepository */ - $budgetRepository = app(BudgetRepositoryInterface::class); - $budgets = $budgetRepository->getBudgets(); + $budgetRepository = app(BudgetRepositoryInterface::class); + $budgets = $budgetRepository->getBudgets(); // reverse amounts foreach ($journals as $index => $journal) { @@ -162,18 +163,18 @@ class MassController extends Controller * * @throws FireflyException */ - public function update(MassEditJournalRequest $request): Redirector|RedirectResponse + public function update(MassEditJournalRequest $request): Redirector | RedirectResponse { $journalIds = $request->get('journals'); if (!is_array($journalIds)) { // TODO this is a weird error, should be caught. throw new FireflyException('This is not an array.'); } - $count = 0; + $count = 0; /** @var string $journalId */ foreach ($journalIds as $journalId) { - $integer = (int) $journalId; + $integer = (int)$journalId; try { $this->updateJournal($integer, $request); @@ -195,15 +196,15 @@ class MassController extends Controller */ private function updateJournal(int $journalId, MassEditJournalRequest $request): void { - $journal = $this->repository->find($journalId); + $journal = $this->repository->find($journalId); if (!$journal instanceof TransactionJournal) { throw new FireflyException(sprintf('Trying to edit non-existent or deleted journal #%d', $journalId)); } - $service = app(JournalUpdateService::class); + $service = app(JournalUpdateService::class); // for each field, call the update service. $service->setTransactionJournal($journal); - $data = [ + $data = [ 'date' => $this->getDateFromRequest($request, $journal->id, 'date'), 'description' => $this->getStringFromRequest($request, $journal->id, 'description'), 'source_id' => $this->getIntFromRequest($request, $journal->id, 'source_id'), @@ -224,7 +225,8 @@ class MassController extends Controller $runRecalculations = $service->isCompareHashChanged(); $flags = new TransactionGroupEventFlags(); $flags->recalculateCredit = $runRecalculations; - event(new UpdatedSingleTransactionGroup($journal->transactionGroup, $flags)); + $objects = TransactionGroupEventObjects::collectFromTransactionGroup($journal->transactionGroup); + event(new UpdatedSingleTransactionGroup($flags, $objects)); } private function getDateFromRequest(MassEditJournalRequest $request, int $journalId, string $key): ?Carbon @@ -259,7 +261,7 @@ class MassController extends Controller return null; } - return (string) $value[$journalId]; + return (string)$value[$journalId]; } private function getIntFromRequest(MassEditJournalRequest $request, int $journalId, string $string): ?int @@ -272,6 +274,6 @@ class MassController extends Controller return null; } - return (int) $value[$journalId]; + return (int)$value[$journalId]; } } diff --git a/app/Listeners/Model/TransactionGroup/ProcessesUpdatedTransactionGroup.php b/app/Listeners/Model/TransactionGroup/ProcessesUpdatedTransactionGroup.php index d2cf2721de..32cfb3712f 100644 --- a/app/Listeners/Model/TransactionGroup/ProcessesUpdatedTransactionGroup.php +++ b/app/Listeners/Model/TransactionGroup/ProcessesUpdatedTransactionGroup.php @@ -27,30 +27,43 @@ namespace FireflyIII\Listeners\Model\TransactionGroup; use FireflyIII\Enums\TransactionTypeEnum; use FireflyIII\Enums\WebhookTrigger; use FireflyIII\Events\Model\TransactionGroup\UpdatedSingleTransactionGroup; -use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending; -use FireflyIII\Generator\Webhook\MessageGeneratorInterface; use FireflyIII\Models\Account; use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionGroup; use FireflyIII\Models\TransactionJournal; -use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; -use FireflyIII\Services\Internal\Support\CreditRecalculateService; -use FireflyIII\Support\Facades\FireflyConfig; -use FireflyIII\Support\Models\AccountBalanceCalculator; -use FireflyIII\TransactionRules\Engine\RuleEngineInterface; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\Log; class ProcessesUpdatedTransactionGroup { + use SupportsGroupProcessingTrait; + public function handle(UpdatedSingleTransactionGroup $event): void { - Log::debug('Now in handle() for UpdatedSingleTransactionGroup'); + Log::debug(sprintf('User called %s', get_class($event))); $this->unifyAccounts($event); - $this->processRules($event); - $this->recalculateCredit($event); - $this->triggerWebhooks($event); - ProcessesNewTransactionGroup::removePeriodStatistics($event->transactionGroup->transactionJournals); - $this->updateRunningBalance($event); + + Log::debug(sprintf('Transaction journal count is %d', $event->objects->transactionJournals->count())); + if (!$event->flags->applyRules) { + Log::debug(sprintf('Will NOT process rules for %d journal(s)', $event->objects->transactionJournals->count())); + } + if (!$event->flags->recalculateCredit) { + Log::debug(sprintf('Will NOT recalculate credit for %d journal(s)', $event->objects->transactionJournals->count())); + } + if (!$event->flags->fireWebhooks) { + Log::debug(sprintf('Will NOT fire webhooks for %d journal(s)', $event->objects->transactionJournals->count())); + } + + if ($event->flags->applyRules) { + $this->processRules($event->objects->transactionJournals, 'update-journal'); + } + if ($event->flags->recalculateCredit) { + $this->recalculateCredit($event->objects->accounts); + } + if ($event->flags->fireWebhooks) { + $this->fireWebhooks($event->objects->transactionJournals, WebhookTrigger::UPDATE_TRANSACTION); + } + $this->removePeriodStatistics($event->objects); + $this->recalculateRunningBalance($event->objects); Log::debug('Done with handle() for UpdatedSingleTransactionGroup'); } @@ -58,10 +71,18 @@ class ProcessesUpdatedTransactionGroup /** * This method will make sure all source / destination accounts are the same. */ - public function unifyAccounts(UpdatedSingleTransactionGroup $updatedGroupEvent): void + protected function unifyAccounts(UpdatedSingleTransactionGroup $updatedGroupEvent): void { Log::debug('Now in unifyAccounts()'); - $group = $updatedGroupEvent->transactionGroup; + /** @var TransactionGroup $group */ + foreach ($updatedGroupEvent->objects->transactionGroups as $group) { + $this->unifyAccountsForGroup($group); + } + Log::debug('Done with unifyAccounts()'); + } + + private function unifyAccountsForGroup(TransactionGroup $group): void + { if (1 === $group->transactionJournals->count()) { Log::debug('Nothing to do in unifyAccounts()'); @@ -70,14 +91,13 @@ class ProcessesUpdatedTransactionGroup // first journal: /** @var null|TransactionJournal $first */ - $first = $group + $first = $group ->transactionJournals() ->orderBy('transaction_journals.date', 'DESC') ->orderBy('transaction_journals.order', 'ASC') ->orderBy('transaction_journals.id', 'DESC') ->orderBy('transaction_journals.description', 'DESC') - ->first() - ; + ->first(); if (null === $first) { Log::warning(sprintf('Group #%d has no transaction journals.', $group->id)); @@ -85,15 +105,15 @@ class ProcessesUpdatedTransactionGroup return; } - $all = $group->transactionJournals()->get()->pluck('id')->toArray(); + $all = $group->transactionJournals()->get()->pluck('id')->toArray(); /** @var Account $sourceAccount */ $sourceAccount = $first->transactions()->where('amount', '<', '0')->first()->account; /** @var Account $destAccount */ - $destAccount = $first->transactions()->where('amount', '>', '0')->first()->account; + $destAccount = $first->transactions()->where('amount', '>', '0')->first()->account; - $type = $first->transactionType->type; + $type = $first->transactionType->type; if (TransactionTypeEnum::TRANSFER->value === $type || TransactionTypeEnum::WITHDRAWAL->value === $type) { // set all source transactions to source account: Transaction::whereIn('transaction_journal_id', $all)->where('amount', '<', 0)->update(['account_id' => $sourceAccount->id]); @@ -102,92 +122,5 @@ class ProcessesUpdatedTransactionGroup // set all destination transactions to destination account: Transaction::whereIn('transaction_journal_id', $all)->where('amount', '>', 0)->update(['account_id' => $destAccount->id]); } - Log::debug('Done with unifyAccounts()'); - } - - /** - * This method will check all the rules when a journal is updated. - */ - private function processRules(UpdatedSingleTransactionGroup $updatedGroupEvent): void - { - Log::debug('Now in processRules()'); - if (false === $updatedGroupEvent->flags->applyRules) { - Log::info(sprintf('Will not run rules on group #%d', $updatedGroupEvent->transactionGroup->id)); - - return; - } - - $journals = $updatedGroupEvent->transactionGroup->transactionJournals; - $array = []; - - /** @var TransactionJournal $journal */ - foreach ($journals as $journal) { - $array[] = $journal->id; - } - $journalIds = implode(',', $array); - Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds)); - - // collect rules: - $ruleGroupRepository = app(RuleGroupRepositoryInterface::class); - $ruleGroupRepository->setUser($updatedGroupEvent->transactionGroup->user); - - $groups = $ruleGroupRepository->getRuleGroupsWithRules('update-journal'); - - // file rule engine. - $newRuleEngine = app(RuleEngineInterface::class); - $newRuleEngine->setUser($updatedGroupEvent->transactionGroup->user); - $newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]); - $newRuleEngine->setRuleGroups($groups); - $newRuleEngine->fire(); - Log::debug('Done with processRules()'); - } - - private function recalculateCredit(UpdatedSingleTransactionGroup $event): void - { - Log::debug('Now in recalculateCredit()'); - $group = $event->transactionGroup; - - /** @var CreditRecalculateService $object */ - $object = app(CreditRecalculateService::class); - $object->setGroup($group); - $object->recalculate(); - Log::debug('Done with recalculateCredit()'); - } - - private function triggerWebhooks(UpdatedSingleTransactionGroup $updatedGroupEvent): void - { - Log::debug('Now in triggerWebhooks()'); - $group = $updatedGroupEvent->transactionGroup; - if (false === $updatedGroupEvent->flags->fireWebhooks) { - Log::info(sprintf('Will not fire webhooks for transaction group #%d', $group->id)); - - return; - } - $user = $group->user; - - /** @var MessageGeneratorInterface $engine */ - $engine = app(MessageGeneratorInterface::class); - $engine->setUser($user); - $engine->setObjects(new Collection()->push($group)); - $engine->setTrigger(WebhookTrigger::UPDATE_TRANSACTION); - $engine->generateMessages(); - - Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__)); - event(new WebhookMessagesRequestSending()); - Log::debug('End of triggerWebhooks()'); - } - - private function updateRunningBalance(UpdatedSingleTransactionGroup $event): void - { - Log::debug('Now in updateRunningBalance()'); - if (false === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) { - return; - } - Log::debug(__METHOD__); - $group = $event->transactionGroup; - foreach ($group->transactionJournals as $journal) { - AccountBalanceCalculator::recalculateForJournal($journal); - } - Log::debug('Done with updateRunningBalance()'); } }