Compare commits

..

12 Commits

Author SHA1 Message Date
github-actions[bot]
d3add7c92b Merge pull request #11647 from firefly-iii/release-1770268490
🤖 Automatically merge the PR into the develop branch.
2026-02-05 06:14:57 +01:00
JC5
a491e4921f 🤖 Auto commit for release 'develop' on 2026-02-05 2026-02-05 06:14:50 +01:00
James Cole
171bc03668 Fix running balance events. 2026-02-05 06:10:25 +01:00
James Cole
dd5476bfc7 Clean up events and filters. 2026-02-05 06:02:32 +01:00
James Cole
bc0769358d Clean up update handlers. 2026-02-05 05:51:44 +01:00
James Cole
ccf33f1db6 Also include delete event in new event triggers. 2026-02-05 05:47:37 +01:00
github-actions[bot]
35f611b3f2 Merge pull request #11645 from firefly-iii/release-1770234250
🤖 Automatically merge the PR into the develop branch.
2026-02-04 20:44:18 +01:00
JC5
e5d394533c 🤖 Auto commit for release 'develop' on 2026-02-04 2026-02-04 20:44:10 +01:00
James Cole
831d39a41e Catch missing nonce 2026-02-04 20:39:54 +01:00
James Cole
2920a9b9e3 Fix call. 2026-02-04 20:39:01 +01:00
James Cole
5c8204e963 Unify more event handlers. 2026-02-04 20:29:28 +01:00
James Cole
d25283f193 Clean up processing for group. 2026-02-04 20:17:47 +01:00
28 changed files with 247 additions and 361 deletions

View File

@@ -31,7 +31,6 @@ use FireflyIII\Enums\UserRoleEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\Debug\Timer;
use FireflyIII\Support\Facades\Amount;
use FireflyIII\Support\Facades\Steam;
use FireflyIII\Support\Http\Api\AccountFilter;
@@ -80,7 +79,7 @@ class AccountController extends Controller
*/
public function accounts(AutocompleteApiRequest $request): JsonResponse
{
Log::debug('Before All.');
// Log::debug('Before All.');
['types' => $types, 'query' => $query, 'date' => $date, 'limit' => $limit] = $request->attributes->all();
$date ??= today(config('app.timezone'));
@@ -89,8 +88,6 @@ class AccountController extends Controller
$date->endOfDay();
$return = [];
$timer = Timer::getInstance();
$timer->start(sprintf('AC accounts "%s"', $query));
$result = $this->repository->searchAccount((string) $query, $types, $limit);
$allBalances = Steam::accountsBalancesOptimized($result, $date, $this->primaryCurrency, $this->convertToPrimary);
@@ -136,7 +133,6 @@ class AccountController extends Controller
return $posA - $posB;
});
$timer->stop(sprintf('AC accounts "%s"', $query));
return response()->api($return);
}

View File

@@ -25,9 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers\Models\Transaction;
use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Events\UpdatedAccount;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
@@ -74,31 +71,9 @@ class DestroyController extends Controller
public function destroy(TransactionGroup $transactionGroup): JsonResponse
{
Log::debug(sprintf('Now in %s', __METHOD__));
// grab asset account(s) from group:
$accounts = [];
/** @var TransactionJournal $journal */
foreach ($transactionGroup->transactionJournals as $journal) {
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
$type = $transaction->account->accountType->type;
// if is valid liability, trigger event!
if (in_array($type, config('firefly.valid_liabilities'), true)) {
$accounts[] = $transaction->account;
}
}
}
$this->groupRepository->destroy($transactionGroup);
Preferences::mark();
/** @var Account $account */
foreach ($accounts as $account) {
Log::debug(sprintf('Now going to trigger updated account event for account #%d', $account->id));
event(new UpdatedAccount($account));
}
return response()->json([], 204);
}

View File

@@ -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;
@@ -75,7 +76,9 @@ class UpdateController extends Controller
Log::debug('Now in update routine for transaction group');
$data = $request->getAll();
$oldHash = $this->groupRepository->getCompareHash($transactionGroup);
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($transactionGroup);
$transactionGroup = $this->groupRepository->update($transactionGroup, $data);
$objects->appendFromTransactionGroup($transactionGroup);
$newHash = $this->groupRepository->getCompareHash($transactionGroup);
$manager = $this->getManager();
@@ -88,7 +91,7 @@ class UpdateController extends Controller
$flags->applyRules = $applyRules;
$flags->fireWebhooks = $fireWebhooks;
$flags->recalculateCredit = $runRecalculations;
event(new UpdatedSingleTransactionGroup($transactionGroup, $flags));
event(new UpdatedSingleTransactionGroup($flags, $objects));
/** @var User $admin */
$admin = auth()->user();

View File

@@ -53,7 +53,7 @@ abstract class AggregateFormRequest extends ApiRequest
parent::initialize($query, $request, $attributes, $cookies, $files, $server, $content);
// instantiate all subrequests and share current requests' bags with them
Log::debug('Initializing AggregateFormRequest.');
// Log::debug('Initializing AggregateFormRequest.');
/** @var array|string $config */
foreach ($this->getRequests() as $config) {
@@ -62,7 +62,7 @@ abstract class AggregateFormRequest extends ApiRequest
if (!is_a($requestClass, Request::class, true)) {
throw new RuntimeException('getRequests() must return class-strings of subclasses of Request');
}
Log::debug(sprintf('Initializing subrequest %s', $requestClass));
// Log::debug(sprintf('Initializing subrequest %s', $requestClass));
$instance = $this->requests[] = new $requestClass();
$instance->request = $this->request;
@@ -77,7 +77,8 @@ abstract class AggregateFormRequest extends ApiRequest
$instance->handleConfig(is_array($config) ? $config : []);
}
}
Log::debug('Done initializing AggregateFormRequest.');
// Log::debug('Done initializing AggregateFormRequest.');
}
public function rules(): array
@@ -95,7 +96,7 @@ abstract class AggregateFormRequest extends ApiRequest
// register all subrequests' validators
foreach ($this->requests as $request) {
if (method_exists($request, 'withValidator')) {
Log::debug(sprintf('Process withValidator from class %s', $request::class));
// Log::debug(sprintf('Process withValidator from class %s', $request::class));
$request->withValidator($validator);
}
}

View File

@@ -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;
@@ -44,8 +45,8 @@ class CorrectsGroupAccounts extends Command
*/
public function handle(): int
{
$groups = [];
$res = TransactionJournal::groupBy('transaction_group_id')->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
$groups = [];
$res = TransactionJournal::groupBy('transaction_group_id')->get(['transaction_group_id', DB::raw('COUNT(transaction_group_id) as the_count')]);
/** @var TransactionJournal $journal */
foreach ($res as $journal) {
@@ -53,14 +54,16 @@ class CorrectsGroupAccounts extends Command
$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;
}

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Events\Model\TransactionGroup;
use FireflyIII\Events\Event;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class CreatedSingleTransactionGroup extends Event
{
@@ -38,7 +37,5 @@ class CreatedSingleTransactionGroup extends Event
public function __construct(
public TransactionGroupEventFlags $flags,
public TransactionGroupEventObjects $objects
) {
Log::debug(__METHOD__);
}
) {}
}

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Events\Model\TransactionGroup;
use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionGroup;
use Illuminate\Queue\SerializesModels;
class DestroyedSingleTransactionGroup extends Event
@@ -36,6 +35,7 @@ class DestroyedSingleTransactionGroup extends Event
* Create a new event instance.
*/
public function __construct(
public TransactionGroup $transactionGroup
public TransactionGroupEventFlags $flags,
public TransactionGroupEventObjects $objects
) {}
}

View File

@@ -8,6 +8,7 @@ use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
/**
* This class collects all objects before and after the creation, removal or updating
@@ -23,6 +24,7 @@ class TransactionGroupEventObjects
public Collection $budgets;
public Collection $categories;
public Collection $tags;
public Collection $transactionGroups;
public Collection $transactionJournals;
public function __construct()
@@ -31,26 +33,40 @@ class TransactionGroupEventObjects
$this->budgets = new Collection();
$this->categories = new Collection();
$this->tags = new Collection();
$this->transactionGroups = new Collection();
$this->transactionJournals = new Collection();
}
public static function collectFromTransactionGroup(TransactionGroup $transactionGroup): self
{
Log::debug(sprintf('collectFromTransactionGroup(#%d)', $transactionGroup->id));
$object = new self();
/** @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);
}
}
$this->transactionGroups = $this->transactionGroups->unique('id');
$this->transactionJournals = $this->transactionJournals->unique('id');
$this->budgets = $this->budgets->unique('id');
$this->categories = $this->categories->unique('id');
$this->tags = $this->tags->unique('id');
$this->accounts = $this->accounts->unique('id');
}
}

View File

@@ -25,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Events\Model\TransactionGroup;
use FireflyIII\Events\Event;
use FireflyIII\Models\TransactionGroup;
use Illuminate\Queue\SerializesModels;
class UpdatedSingleTransactionGroup extends Event
@@ -36,7 +35,7 @@ class UpdatedSingleTransactionGroup extends Event
* Create a new event instance.
*/
public function __construct(
public TransactionGroup $transactionGroup,
public TransactionGroupEventFlags $flags
public TransactionGroupEventFlags $flags,
public TransactionGroupEventObjects $objects
) {}
}

View File

@@ -328,7 +328,7 @@ class TransactionJournalFactory
throw new FireflyException($e->getMessage(), 0, $e);
}
Log::debug(sprintf('Is part of a batch submission? %s', var_export($row['batch_submission'], true)));
// Log::debug(sprintf('Is part of a batch submission? %s', var_export($row['batch_submission'], true)));
$journal->save();
$this->storeBudget($journal, $row);
$this->storeCategory($journal, $row);

View File

@@ -84,9 +84,9 @@ class ConvertsAmountToPrimaryAmount
return;
}
$converter = new ExchangeRateConverter();
$newAmount = $converter->convert($params->originalCurrency, $primaryCurrency, now(), $amount);
$converter->setUserGroup($params->user->userGroup);
$converter->setIgnoreSettings(true);
$newAmount = $converter->convert($params->originalCurrency, $primaryCurrency, now(), $amount);
$params->model->{$primaryAmountField} = $newAmount;
$params->model->saveQuietly();
Log::debug(sprintf(

View File

@@ -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;
@@ -117,12 +118,14 @@ class BulkController extends Controller
}
$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));

View File

@@ -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;
@@ -306,8 +307,9 @@ class ConvertController extends Controller
$group->refresh();
session()->flash('success', (string) trans('firefly.converted_to_'.$destinationType->type));
$flags = new TransactionGroupEventFlags();
event(new UpdatedSingleTransactionGroup($group, $flags));
$flags = new TransactionGroupEventFlags();
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($group);
event(new UpdatedSingleTransactionGroup($flags, $objects));
return redirect(route('transactions.show', [$group->id]));
}

View File

@@ -24,12 +24,9 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Transaction;
use FireflyIII\Events\UpdatedAccount;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
use FireflyIII\Support\Facades\Preferences;
use FireflyIII\Support\Facades\Steam;
@@ -112,29 +109,7 @@ class DeleteController extends Controller
}
$objectType = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
session()->flash('success', (string) trans('firefly.deleted_'.strtolower($objectType), ['description' => $group->title ?? $journal->description]));
// grab asset account(s) from group:
$accounts = [];
/** @var TransactionJournal $currentJournal */
foreach ($group->transactionJournals as $currentJournal) {
/** @var Transaction $transaction */
foreach ($currentJournal->transactions as $transaction) {
$type = $transaction->account->accountType->type;
// if is valid liability, trigger event!
if (in_array($type, config('firefly.valid_liabilities'), true)) {
$accounts[] = $transaction->account;
}
}
}
$this->repository->destroy($group);
/** @var Account $account */
foreach ($accounts as $account) {
Log::debug(sprintf('Now going to trigger updated account event for account #%d', $account->id));
event(new UpdatedAccount($account));
}
Preferences::mark();
return redirect($this->getPreviousUrl('transactions.delete.url'));

View File

@@ -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;
@@ -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

View File

@@ -26,47 +26,31 @@ namespace FireflyIII\Listeners\Model\TransactionGroup;
use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Events\Model\TransactionGroup\DestroyedSingleTransactionGroup;
use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending;
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Support\Facades\FireflyConfig;
use FireflyIII\Support\Models\AccountBalanceCalculator;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
class ProcessesDestroyedTransactionGroup implements ShouldQueue
{
use SupportsGroupProcessingTrait;
public function handle(DestroyedSingleTransactionGroup $event): void
{
$this->triggerWebhooks($event);
$this->updateRunningBalance($event);
}
Log::debug(sprintf('User called %s', get_class($event)));
private function triggerWebhooks(DestroyedSingleTransactionGroup $destroyedGroupEvent): void
{
Log::debug('DestroyedTransactionGroup:triggerWebhooks');
$group = $destroyedGroupEvent->transactionGroup;
$user = $group->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
$engine->setUser($user);
$engine->setObjects(new Collection()->push($group));
$engine->setTrigger(WebhookTrigger::DESTROY_TRANSACTION);
$engine->generateMessages();
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
event(new WebhookMessagesRequestSending());
}
private function updateRunningBalance(DestroyedSingleTransactionGroup $event): void
{
if (false === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
return;
if (!$event->flags->recalculateCredit) {
Log::debug(sprintf('Will NOT recalculate credit for %d journal(s)', $event->objects->transactionJournals->count()));
}
Log::debug(__METHOD__);
$group = $event->transactionGroup;
foreach ($group->transactionJournals as $journal) {
AccountBalanceCalculator::recalculateForJournal($journal);
if (!$event->flags->fireWebhooks) {
Log::debug(sprintf('Will NOT fire webhooks for %d journal(s)', $event->objects->transactionJournals->count()));
}
if ($event->flags->recalculateCredit) {
$this->recalculateCredit($event->objects->accounts);
}
if ($event->flags->fireWebhooks) {
$this->createWebhookMessages($event->objects->transactionGroups, WebhookTrigger::DESTROY_TRANSACTION);
}
$this->removePeriodStatistics($event->objects);
$this->recalculateRunningBalance($event->objects);
}
}

View File

@@ -38,7 +38,7 @@ class ProcessesNewTransactionGroup implements ShouldQueue
public function handle(CreatedSingleTransactionGroup|UserRequestedBatchProcessing $event): void
{
Log::debug(sprintf('User called %s', get_class($event)));
Log::debug(sprintf('Running event handler for %s', get_class($event)));
$setting = FireflyConfig::get('enable_batch_processing', false)->data;
if (true === $event->flags->batchSubmission && true === $setting) {
@@ -46,7 +46,6 @@ class ProcessesNewTransactionGroup implements ShouldQueue
return;
}
Log::debug('Will also collect all open transaction groups and process them.');
$repository = app(JournalRepositoryInterface::class);
$journals = $event->objects->transactionJournals->merge($repository->getAllUncompletedJournals());
@@ -68,7 +67,7 @@ class ProcessesNewTransactionGroup implements ShouldQueue
$this->recalculateCredit($event->objects->accounts);
}
if ($event->flags->fireWebhooks) {
$this->fireWebhooks($journals, WebhookTrigger::STORE_TRANSACTION);
$this->createWebhookMessages($event->objects->transactionGroups, WebhookTrigger::STORE_TRANSACTION);
}
$this->removePeriodStatistics($event->objects);
$this->recalculateRunningBalance($event->objects);

View File

@@ -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('Now handling event %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->createWebhookMessages($event->objects->transactionGroups, WebhookTrigger::UPDATE_TRANSACTION);
}
$this->removePeriodStatistics($event->objects);
$this->recalculateRunningBalance($event->objects);
Log::debug('Done with handle() for UpdatedSingleTransactionGroup');
}
@@ -58,10 +71,19 @@ 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()');
@@ -102,92 +124,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()');
}
}

View File

@@ -9,6 +9,9 @@ use FireflyIII\Enums\WebhookTrigger;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionJournalMeta;
@@ -31,17 +34,15 @@ trait SupportsGroupProcessingTrait
$object = app(CreditRecalculateService::class);
$object->setAccounts($accounts);
$object->recalculate();
Log::debug(sprintf('Done with recalculateCredit for %d account(s)', $accounts->count()));
}
private function fireWebhooks(Collection $journals, WebhookTrigger $trigger): void
private function createWebhookMessages(Collection $groups, WebhookTrigger $trigger): void
{
// collect transaction groups by set ids.
$groups = TransactionGroup::whereIn('id', array_unique($journals->pluck('transaction_group_id')->toArray()))->get();
Log::debug(sprintf('Will now create webhook messages for %d group(s)', $groups->count()));
Log::debug(__METHOD__);
/** @var TransactionJournal $first */
$first = $journals->first();
/** @var TransactionGroup $first */
$first = $groups->first();
$user = $first->user;
/** @var MessageGeneratorInterface $engine */
@@ -54,19 +55,52 @@ trait SupportsGroupProcessingTrait
$engine->setObjects($groups);
// tell the generator to generate the messages
$engine->generateMessages();
Log::debug(sprintf('Done with create webhook messages for %d group(s)', $groups->count()));
}
protected function removePeriodStatistics(TransactionGroupEventObjects $set): void
protected function removePeriodStatistics(TransactionGroupEventObjects $objects): void
{
if (auth()->check()) {
Log::debug('Always remove period statistics');
/** @var PeriodStatisticRepositoryInterface $repository */
$repository = app(PeriodStatisticRepositoryInterface::class);
$repository->deleteStatisticsForCollection($set->transactionJournals);
// FIXME extend for categories, accounts, etc.
if (!auth()->check()) {
Log::debug('Will NOT remove period statistics for all objects, because no user detected.');
}
Log::debug('Will now remove period statistics for all objects.');
// since you get a bunch of journals AND a bunch of
// objects, this needs to be a collection
/** @var PeriodStatisticRepositoryInterface $repository */
$repository = app(PeriodStatisticRepositoryInterface::class);
$dates = $this->collectDatesFromJournals($objects->transactionJournals);
$repository->deleteStatisticsForType(Account::class, $objects->accounts, $dates);
$repository->deleteStatisticsForType(Budget::class, $objects->budgets, $dates);
$repository->deleteStatisticsForType(Category::class, $objects->categories, $dates);
$repository->deleteStatisticsForType(Tag::class, $objects->tags, $dates);
// remove if no stuff present:
// remove for no tag, no cat, etc.
if (0 === $objects->budgets->count()) {
Log::debug('No budgets, delete "no_category" stats.');
$repository->deleteStatisticsForPrefix('no_budget', $dates);
}
if (0 === $objects->categories->count()) {
Log::debug('No categories, delete "no_category" stats.');
$repository->deleteStatisticsForPrefix('no_category', $dates);
}
if (0 === $objects->tags->count()) {
Log::debug('No tags, delete "no_category" stats.');
$repository->deleteStatisticsForPrefix('no_tag', $dates);
}
Log::debug('Done with remove period statistics for all objects.');
}
private function collectDatesFromJournals(Collection $journals): Collection
{
$collection = $journals->pluck('date');
if (0 === count($collection)) {
$collection->push(now(config('app.timezone')));
}
return $collection;
}
protected function processRules(Collection $set, string $type): void
@@ -95,25 +129,26 @@ trait SupportsGroupProcessingTrait
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
$newRuleEngine->setRuleGroups($groups);
$newRuleEngine->fire();
Log::debug(sprintf('Done with processRules("%s") for %d journal(s)', $type, $set->count()));
}
protected function recalculateRunningBalance(TransactionGroupEventObjects $objects): void
{
if (true === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
Log::debug('Now in recalculateRunningBalance');
if (false === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
Log::debug('Running balance is disabled.');
return;
}
Log::debug('Now in recalculateRunningBalance');
// find the earliest date in the set, based on date and _internal_previous_date
$earliest = $objects->transactionJournals->pluck('date')->sort()->first();
$fromInternalDate = $this->getFromInternalDate($objects->transactionJournals->pluck('id')->toArray());
$earliest = $fromInternalDate->lt($earliest) ? $fromInternalDate : $earliest;
Log::debug(sprintf('Found earliest date: %s', $earliest->toW3cString()));
$accounts = Account::whereIn('id', $objects->accounts->pluck('id')->toArray())->get(['accounts.*']);
Log::debug('Found accounts to process', $objects->accounts->pluck('id')->toArray());
Log::debug('Found accounts to process', $accounts->pluck('id')->toArray());
AccountBalanceCalculator::optimizedCalculation($accounts, $earliest);
AccountBalanceCalculator::optimizedCalculation($objects->accounts, $earliest);
}
private function getFromInternalDate(array $ids): Carbon

View File

@@ -26,8 +26,6 @@ namespace FireflyIII\Repositories\PeriodStatistic;
use Carbon\Carbon;
use FireflyIII\Models\Account;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\PeriodStatistic;
use FireflyIII\Models\Tag;
use FireflyIII\Models\Transaction;
@@ -36,7 +34,6 @@ use FireflyIII\Support\Repositories\UserGroup\UserGroupTrait;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Override;
@@ -184,65 +181,29 @@ class PeriodStatisticRepository implements PeriodStatisticRepositoryInterface, U
#[Override]
public function deleteStatisticsForCollection(Collection $set): void
{
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);
// collect all categories, and remove stats.
$categories = Category::whereIn(
'id',
DB::table('category_transaction_journal')
->whereIn('transaction_journal_id', $set->pluck('id')->toArray())
->get(['category_transaction_journal.category_id'])
->pluck('category_id')
->toArray()
)->get(['categories.*']);
Log::debug('Collected category IDs', $categories->pluck('id')->toArray());
$this->deleteStatisticsForType(Category::class, $categories, $dates);
// budgets, same thing
$budgets = Budget::whereIn(
'id',
DB::table('budget_transaction_journal')
->whereIn('transaction_journal_id', $set->pluck('id')->toArray())
->get(['budget_transaction_journal.budget_id'])
->pluck('budget_id')
->toArray()
)->get(['budgets.*']);
Log::debug('Collected budget IDs', $categories->pluck('id')->toArray());
$this->deleteStatisticsForType(Budget::class, $budgets, $dates);
// tags
$tags = Tag::whereIn(
'id',
DB::table('tag_transaction_journal')
->whereIn('transaction_journal_id', $set->pluck('id')->toArray())
->get(['tag_transaction_journal.tag_id'])
->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);
}
// 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);
//
// // 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);
// }
}
}

View File

@@ -420,7 +420,6 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
$flags->applyRules = $data['apply_rules'] ?? true;
$flags->fireWebhooks = $data['fire_webhooks'] ?? true;
$flags->batchSubmission = $data['batch_submission'] ?? false;
Log::debug('CreatedSingleTransactionGroup');
event(new CreatedSingleTransactionGroup($flags, $objects));
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
event(new WebhookMessagesRequestSending());

View File

@@ -25,6 +25,8 @@ declare(strict_types=1);
namespace FireflyIII\Services\Internal\Destroy;
use FireflyIII\Events\Model\TransactionGroup\DestroyedSingleTransactionGroup;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
use FireflyIII\Models\TransactionGroup;
use Illuminate\Support\Facades\Log;
@@ -36,6 +38,7 @@ class TransactionGroupDestroyService
public function destroy(TransactionGroup $transactionGroup): void
{
Log::debug(sprintf('Now in %s', __METHOD__));
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($transactionGroup);
/** @var JournalDestroyService $service */
$service = app(JournalDestroyService::class);
@@ -44,6 +47,7 @@ class TransactionGroupDestroyService
}
$transactionGroup->delete();
// trigger just after destruction
event(new DestroyedSingleTransactionGroup($transactionGroup));
$flags = new TransactionGroupEventFlags();
event(new DestroyedSingleTransactionGroup($flags, $objects));
}
}

View File

@@ -26,7 +26,6 @@ namespace FireflyIII\Services\Internal\Update;
use FireflyIII\Enums\AccountTypeEnum;
use FireflyIII\Events\Model\Account\UpdatedExistingAccount;
use FireflyIII\Events\UpdatedAccount;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
@@ -105,7 +104,7 @@ class AccountUpdateService
// update preferences if inactive:
$this->updatePreferences($account);
event(new UpdatedAccount($account));
event(new UpdatedExistingAccount($account));
return $account;
}
@@ -151,8 +150,6 @@ class AccountUpdateService
$account->save();
event(new UpdatedExistingAccount($account));
return $account;
}

40
composer.lock generated
View File

@@ -1878,16 +1878,16 @@
},
{
"name": "laravel/framework",
"version": "v12.49.0",
"version": "v12.50.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "4bde4530545111d8bdd1de6f545fa8824039fcb5"
"reference": "174ffed91d794a35a541a5eb7c3785a02a34aaba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/4bde4530545111d8bdd1de6f545fa8824039fcb5",
"reference": "4bde4530545111d8bdd1de6f545fa8824039fcb5",
"url": "https://api.github.com/repos/laravel/framework/zipball/174ffed91d794a35a541a5eb7c3785a02a34aaba",
"reference": "174ffed91d794a35a541a5eb7c3785a02a34aaba",
"shasum": ""
},
"require": {
@@ -2096,7 +2096,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2026-01-28T03:40:49+00:00"
"time": "2026-02-04T18:34:13+00:00"
},
{
"name": "laravel/passport",
@@ -2176,30 +2176,30 @@
},
{
"name": "laravel/prompts",
"version": "v0.3.11",
"version": "v0.3.12",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
"reference": "dd2a2ed95acacbcccd32fd98dee4c946ae7a7217"
"reference": "4861ded9003b7f8a158176a0b7666f74ee761be8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/prompts/zipball/dd2a2ed95acacbcccd32fd98dee4c946ae7a7217",
"reference": "dd2a2ed95acacbcccd32fd98dee4c946ae7a7217",
"url": "https://api.github.com/repos/laravel/prompts/zipball/4861ded9003b7f8a158176a0b7666f74ee761be8",
"reference": "4861ded9003b7f8a158176a0b7666f74ee761be8",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.2",
"ext-mbstring": "*",
"php": "^8.1",
"symfony/console": "^6.2|^7.0"
"symfony/console": "^6.2|^7.0|^8.0"
},
"conflict": {
"illuminate/console": ">=10.17.0 <10.25.0",
"laravel/framework": ">=10.17.0 <10.25.0"
},
"require-dev": {
"illuminate/collections": "^10.0|^11.0|^12.0",
"illuminate/collections": "^10.0|^11.0|^12.0|^13.0",
"mockery/mockery": "^1.5",
"pestphp/pest": "^2.3|^3.4|^4.0",
"phpstan/phpstan": "^1.12.28",
@@ -2229,9 +2229,9 @@
"description": "Add beautiful and user-friendly forms to your command-line applications.",
"support": {
"issues": "https://github.com/laravel/prompts/issues",
"source": "https://github.com/laravel/prompts/tree/v0.3.11"
"source": "https://github.com/laravel/prompts/tree/v0.3.12"
},
"time": "2026-01-27T02:55:06+00:00"
"time": "2026-02-03T06:57:26+00:00"
},
{
"name": "laravel/sanctum",
@@ -2298,27 +2298,27 @@
},
{
"name": "laravel/serializable-closure",
"version": "v2.0.8",
"version": "v2.0.9",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b"
"reference": "8f631589ab07b7b52fead814965f5a800459cb3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7581a4407012f5f53365e11bafc520fd7f36bc9b",
"reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/8f631589ab07b7b52fead814965f5a800459cb3e",
"reference": "8f631589ab07b7b52fead814965f5a800459cb3e",
"shasum": ""
},
"require": {
"php": "^8.1"
},
"require-dev": {
"illuminate/support": "^10.0|^11.0|^12.0",
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
"nesbot/carbon": "^2.67|^3.0",
"pestphp/pest": "^2.36|^3.0|^4.0",
"phpstan/phpstan": "^2.0",
"symfony/var-dumper": "^6.2.0|^7.0.0"
"symfony/var-dumper": "^6.2.0|^7.0.0|^8.0.0"
},
"type": "library",
"extra": {
@@ -2355,7 +2355,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
"time": "2026-01-08T16:22:46+00:00"
"time": "2026-02-03T06:55:34+00:00"
},
{
"name": "laravel/slack-notification-channel",

View File

@@ -78,8 +78,8 @@ return [
'running_balance_column' => (bool)envNonEmpty('USE_RUNNING_BALANCE', true), // this is only the default value, is not used.
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2026-02-04',
'build_time' => 1770218409,
'version' => 'develop/2026-02-05',
'build_time' => 1770268364,
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 28, // field is no longer used.

12
package-lock.json generated
View File

@@ -83,9 +83,9 @@
}
},
"node_modules/@babel/generator": {
"version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.0.tgz",
"integrity": "sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==",
"version": "7.29.1",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
"integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -7118,9 +7118,9 @@
}
},
"node_modules/i18next": {
"version": "25.8.1",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.1.tgz",
"integrity": "sha512-nFFxhwcRNggIrkv2hx/xMYVMG7Z8iMUA4ZuH4tgcbZiI0bK1jn3kSDIXNWuQDt1xVAu7mb7Qn82TpH7ZAk/okA==",
"version": "25.8.3",
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.3.tgz",
"integrity": "sha512-IC/pp2vkczdu1sBheq1eC92bLavN6fM5jH61c7Xa23PGio5ePEd+EP+re1IkO7KEM9eyeJHUxvIRxsaYTlsSyQ==",
"funding": [
{
"type": "individual",

View File

@@ -107,18 +107,18 @@
"multi_account_warning_withdrawal": "\ub4a4\ub530\ub974\ub294 \ubd84\ud560\uc758 \uc18c\uc2a4 \uacc4\uc815\uc740 \uccab \ubc88\uc9f8 \ucd9c\uae08 \ubd84\ud560\uc5d0 \uc815\uc758\ub41c \ub0b4\uc6a9\uc5d0 \ub530\ub77c \uc7ac\uc815\uc758\ub41c\ub2e4\ub294 \uc810\uc5d0 \uc720\uc758\ud558\uc2dc\uae30 \ubc14\ub78d\ub2c8\ub2e4.",
"multi_account_warning_deposit": "\ub4a4\ub530\ub974\ub294 \ubd84\ud560\uc758 \ubaa9\uc801\uc9c0 \uacc4\uc815\uc740 \uccab \ubc88\uc9f8 \uc785\uae08 \ubd84\ud560\uc5d0 \uc815\uc758\ub41c \ub0b4\uc6a9\uc5d0 \ub530\ub77c \uc7ac\uc815\uc758\ub41c\ub2e4\ub294 \uc810\uc5d0 \uc720\uc758\ud558\uc2dc\uae30 \ubc14\ub78d\ub2c8\ub2e4.",
"multi_account_warning_transfer": "\ub4a4\ub530\ub974\ub294 \ubd84\ud560\uc758 \ucd9c\ubc1c\uc9c0\uc640 \ubaa9\uc801\uc9c0 \uacc4\uc815\uc740 \uccab \ubc88\uc9f8 \uc774\uccb4 \ubd84\ud560\uc5d0 \uc815\uc758\ub41c \ub0b4\uc6a9\uc5d0 \ub530\ub77c \uc7ac\uc815\uc758\ub41c\ub2e4\ub294 \uc810\uc5d0 \uc720\uc758\ud558\uc2dc\uae30 \ubc14\ub78d\ub2c8\ub2e4.",
"webhook_trigger_ANY": "After any event",
"webhook_trigger_ANY": "\ubaa8\ub4e0 \uc885\ub958\uc758 \ub3d9\uc791 \uc774\ud6c4",
"webhook_trigger_STORE_TRANSACTION": "\uac70\ub798 \uc0dd\uc131 \uc774\ud6c4",
"webhook_trigger_UPDATE_TRANSACTION": "\uac70\ub798 \uc5c5\ub370\uc774\ud2b8 \uc774\ud6c4",
"webhook_trigger_DESTROY_TRANSACTION": "\uac70\ub798 \uc0ad\uc81c \uc774\ud6c4",
"webhook_trigger_STORE_BUDGET": "After budget creation",
"webhook_trigger_UPDATE_BUDGET": "After budget update",
"webhook_trigger_DESTROY_BUDGET": "After budget delete",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "After budgeted amount change",
"webhook_trigger_STORE_BUDGET": "\uc608\uc0b0 \uc0dd\uc131 \uc774\ud6c4",
"webhook_trigger_UPDATE_BUDGET": "\uc608\uc0b0 \uc5c5\ub370\uc774\ud2b8 \uc774\ud6c4",
"webhook_trigger_DESTROY_BUDGET": "\uc608\uc0b0 \uc0ad\uc81c \uc774\ud6c4",
"webhook_trigger_STORE_UPDATE_BUDGET_LIMIT": "\uc608\uc0b0 \uc561\uc218 \ubcc0\ub3d9 \uc774\ud6c4",
"webhook_response_TRANSACTIONS": "\uac70\ub798 \uc138\ubd80 \uc815\ubcf4",
"webhook_response_RELEVANT": "Relevant details",
"webhook_response_RELEVANT": "\uad00\ub828 \uc138\ubd80 \uc815\ubcf4",
"webhook_response_ACCOUNTS": "\uacc4\uc815 \uc815\ubcf4",
"webhook_response_NONE": "No details",
"webhook_response_NONE": "\uc0c1\uc138\uc815\ubcf4 \uc5c6\uc74c",
"webhook_delivery_JSON": "JSON",
"actions": "\ud589\ub3d9",
"meta_data": "\uba54\ud0c0\ub370\uc774\ud130",

View File

@@ -15,7 +15,7 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="light dark">
<script type="text/javascript" nonce="{{ $JS_NONCE }}">
<script type="text/javascript" nonce="{{ $JS_NONCE ?? 'none' }}">
/*!
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2023 The Bootstrap Authors