mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-02-05 11:56:58 +00:00
Compare commits
14 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35f611b3f2 | ||
|
|
e5d394533c | ||
|
|
831d39a41e | ||
|
|
2920a9b9e3 | ||
|
|
5c8204e963 | ||
|
|
d25283f193 | ||
|
|
20986e6426 | ||
|
|
9cd0ebe37e | ||
|
|
9c2b83a971 | ||
|
|
e1d32da409 | ||
|
|
c51df8cd83 | ||
|
|
9f016aed16 | ||
|
|
27df5ea800 | ||
|
|
2d7cdd36f0 |
@@ -27,14 +27,11 @@ namespace FireflyIII\Api\V1\Controllers\Models\Transaction;
|
||||
use FireflyIII\Api\V1\Controllers\Controller;
|
||||
use FireflyIII\Api\V1\Requests\Models\Transaction\StoreRequest;
|
||||
use FireflyIII\Enums\UserRoleEnum;
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Exceptions\DuplicateTransactionException;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
||||
use FireflyIII\Rules\IsDuplicateTransaction;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\Support\Http\Api\TransactionFilter;
|
||||
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
@@ -88,9 +85,9 @@ class StoreController extends Controller
|
||||
public function store(StoreRequest $request): JsonResponse
|
||||
{
|
||||
Log::debug('Now in API StoreController::store()');
|
||||
$data = $request->getAll();
|
||||
$data['user'] = auth()->user();
|
||||
$data['user_group'] = $this->userGroup;
|
||||
$data = $request->getAll();
|
||||
$data['user'] = auth()->user();
|
||||
$data['user_group'] = $this->userGroup;
|
||||
|
||||
Log::channel('audit')->info('Store new transaction over API.', $data);
|
||||
|
||||
@@ -109,22 +106,15 @@ class StoreController extends Controller
|
||||
|
||||
throw new ValidationException($validator);
|
||||
}
|
||||
Preferences::mark();
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$flags->applyRules = $data['apply_rules'] ?? true;
|
||||
$flags->fireWebhooks = $data['fire_webhooks'] ?? true;
|
||||
$flags->batchSubmission = $data['batch_submission'] ?? false;
|
||||
Log::debug('CreatedSingleTransactionGroup');
|
||||
event(new CreatedSingleTransactionGroup($transactionGroup, $flags));
|
||||
|
||||
$manager = $this->getManager();
|
||||
$manager = $this->getManager();
|
||||
|
||||
/** @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)
|
||||
->setUserGroup($this->userGroup)
|
||||
@@ -134,20 +124,20 @@ class StoreController extends Controller
|
||||
->withAPIInformation()
|
||||
;
|
||||
|
||||
$selectedGroup = $collector->getGroups()->first();
|
||||
$selectedGroup = $collector->getGroups()->first();
|
||||
if (null === $selectedGroup) {
|
||||
throw HttpException::fromStatusCode(410, '200032: Cannot find transaction. Possibly, a rule deleted this transaction after its creation.');
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -88,7 +89,8 @@ class UpdateController extends Controller
|
||||
$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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -37,8 +36,8 @@ class CreatedSingleTransactionGroup extends Event
|
||||
* Create a new event instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public TransactionGroup $transactionGroup,
|
||||
public TransactionGroupEventFlags $flags
|
||||
public TransactionGroupEventFlags $flags,
|
||||
public TransactionGroupEventObjects $objects
|
||||
) {
|
||||
Log::debug(__METHOD__);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Events\Model\TransactionGroup;
|
||||
|
||||
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
|
||||
* of a transaction group. The idea is that this class contains all relevant objects.
|
||||
* Right now, that means journals, tags, accounts, budgets and categories.
|
||||
*
|
||||
* By collecting these objects (in case of an update: before AND after update) there
|
||||
* is a unified set of objects to manage: update balances, recalculate credits, etc.
|
||||
*/
|
||||
class TransactionGroupEventObjects
|
||||
{
|
||||
public Collection $accounts;
|
||||
public Collection $budgets;
|
||||
public Collection $categories;
|
||||
public Collection $tags;
|
||||
public Collection $transactionGroups;
|
||||
public Collection $transactionJournals;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->accounts = new Collection();
|
||||
$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();
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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]));
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Transaction;
|
||||
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
@@ -76,10 +74,6 @@ class CreateController extends Controller
|
||||
$service = app(GroupCloneService::class);
|
||||
$newGroup = $service->cloneGroup($group);
|
||||
|
||||
// event!
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
event(new CreatedSingleTransactionGroup($group, $flags));
|
||||
|
||||
Preferences::mark();
|
||||
|
||||
$title = $newGroup->title ?? $newGroup->transactionJournals->first()->description;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -25,8 +25,6 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupsRequestedReporting;
|
||||
use FireflyIII\Exceptions\DuplicateTransactionException;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
@@ -336,7 +334,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
Log::debug(sprintf('%s IS today (%s)', $date->format('Y-m-d'), $this->date->format('Y-m-d')));
|
||||
|
||||
// count created journals on THIS day.
|
||||
$journalCount = $this->repository->getJournalCount($recurrence, $date, $date);
|
||||
$journalCount = $this->repository->getJournalCount($recurrence, $date, $date);
|
||||
if ($journalCount > 0 && false === $this->force) {
|
||||
Log::info(sprintf('Already created %d journal(s) for date %s', $journalCount, $date->format('Y-m-d')));
|
||||
|
||||
@@ -354,11 +352,11 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
}
|
||||
|
||||
// create transaction array and send to factory.
|
||||
$groupTitle = null;
|
||||
$count = $recurrence->recurrenceTransactions->count();
|
||||
$groupTitle = null;
|
||||
$count = $recurrence->recurrenceTransactions->count();
|
||||
// #8844, if there is one recurrence transaction, use the first title as the title.
|
||||
// #9305, if there is one recurrence transaction, group title must be NULL.
|
||||
$groupTitle = null;
|
||||
$groupTitle = null;
|
||||
|
||||
// #8844, if there are more, use the recurrence transaction itself.
|
||||
if ($count > 1) {
|
||||
@@ -371,7 +369,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
return null;
|
||||
}
|
||||
|
||||
$array = [
|
||||
$array = [
|
||||
'user' => $recurrence->user,
|
||||
'user_group' => $recurrence->user->userGroup,
|
||||
'group_title' => $groupTitle,
|
||||
@@ -379,21 +377,13 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
];
|
||||
|
||||
/** @var TransactionGroup $group */
|
||||
$group = $this->groupRepository->store($array);
|
||||
$group = $this->groupRepository->store($array);
|
||||
++$this->created;
|
||||
Log::info(sprintf('Created new transaction group #%d', $group->id));
|
||||
|
||||
// trigger event:
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$flags->applyRules = $recurrence->apply_rules;
|
||||
event(new CreatedSingleTransactionGroup($group, $flags));
|
||||
// event(new StoredTransactionGroup($group, $recurrence->apply_rules, true));
|
||||
$this->groups->push($group);
|
||||
|
||||
// update recurring thing:
|
||||
$recurrence->latest_date = $date;
|
||||
$recurrence->latest_date_tz = $date->format('e');
|
||||
$recurrence->save();
|
||||
$this->repository->setLatestDate($recurrence, $date);
|
||||
|
||||
return $group;
|
||||
}
|
||||
|
||||
@@ -24,198 +24,54 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Model\TransactionGroup;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Enums\WebhookTrigger;
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\UserRequestedBatchProcessing;
|
||||
use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending;
|
||||
use FireflyIII\Generator\Webhook\MessageGeneratorInterface;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
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\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class ProcessesNewTransactionGroup implements ShouldQueue
|
||||
{
|
||||
use SupportsGroupProcessingTrait;
|
||||
|
||||
public function handle(CreatedSingleTransactionGroup|UserRequestedBatchProcessing $event): void
|
||||
{
|
||||
$groupId = 0;
|
||||
$collection = new Collection();
|
||||
if ($event instanceof CreatedSingleTransactionGroup) {
|
||||
Log::debug(sprintf('In ProcessesNewTransactionGroup::handle(#%d)', $event->transactionGroup->id));
|
||||
$groupId = $event->transactionGroup->id;
|
||||
$collection = $event->transactionGroup->transactionJournals;
|
||||
}
|
||||
if ($event instanceof UserRequestedBatchProcessing) {
|
||||
Log::debug('User called UserRequestedBatchProcessing');
|
||||
}
|
||||
Log::debug(sprintf('User called %s', get_class($event)));
|
||||
|
||||
$setting = FireflyConfig::get('enable_batch_processing', false)->data;
|
||||
if (true === $event->flags->batchSubmission && true === $setting) {
|
||||
Log::debug(sprintf('Will do nothing for group #%d because it is part of a batch.', $groupId));
|
||||
Log::debug('Will do nothing for event because it is part of a batch.');
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Will (joined with group #%d) collect all open transaction groups and process them.', $groupId));
|
||||
Log::debug('Will also collect all open transaction groups and process them as well.');
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$set = $collection->merge($repository->getAllUncompletedJournals());
|
||||
if (0 === $set->count()) {
|
||||
Log::debug('Set is empty, never mind.');
|
||||
$journals = $event->objects->transactionJournals->merge($repository->getAllUncompletedJournals());
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Set count is %d', $set->count()));
|
||||
Log::debug(sprintf('Transaction journal count is %d', $journals->count()));
|
||||
if (!$event->flags->applyRules) {
|
||||
Log::debug(sprintf('Will NOT process rules for %d journal(s)', $set->count()));
|
||||
Log::debug(sprintf('Will NOT process rules for %d journal(s)', $journals->count()));
|
||||
}
|
||||
if (!$event->flags->recalculateCredit) {
|
||||
Log::debug(sprintf('Will NOT recalculate credit for %d journal(s)', $set->count()));
|
||||
Log::debug(sprintf('Will NOT recalculate credit for %d journal(s)', $journals->count()));
|
||||
}
|
||||
if (!$event->flags->fireWebhooks) {
|
||||
Log::debug(sprintf('Will NOT fire webhooks for %d journal(s)', $set->count()));
|
||||
Log::debug(sprintf('Will NOT fire webhooks for %d journal(s)', $journals->count()));
|
||||
}
|
||||
|
||||
if ($event->flags->applyRules) {
|
||||
$this->processRules($set);
|
||||
$this->processRules($journals, 'store-journal');
|
||||
}
|
||||
if ($event->flags->recalculateCredit) {
|
||||
$this->recalculateCredit($set);
|
||||
$this->recalculateCredit($event->objects->accounts);
|
||||
}
|
||||
if ($event->flags->fireWebhooks) {
|
||||
$this->fireWebhooks($set);
|
||||
$this->fireWebhooks($journals, WebhookTrigger::STORE_TRANSACTION);
|
||||
}
|
||||
// always remove old relevant statistics.
|
||||
self::removePeriodStatistics($set);
|
||||
|
||||
// recalculate running balance if necessary.
|
||||
if (true === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
|
||||
$this->recalculateRunningBalance($set);
|
||||
}
|
||||
|
||||
$repository->markAsCompleted($set);
|
||||
}
|
||||
|
||||
private function getFromInternalDate(array $ids): Carbon
|
||||
{
|
||||
$entries = TransactionJournalMeta::whereIn('transaction_journal_id', $ids)->where('name', '_internal_previous_date')->get(['journal_meta.*']);
|
||||
$array = $entries->toArray();
|
||||
$return = today()->subDay();
|
||||
if (count($array) > 0) {
|
||||
usort($array, function (array $a, array $b) {
|
||||
return Carbon::parse($a['data'])->gt(Carbon::parse($b['data']));
|
||||
});
|
||||
|
||||
$date = Carbon::parse($array[0]['data']);
|
||||
$return = $date->lt($return) ? $date : $return;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function recalculateRunningBalance(Collection $set): void
|
||||
{
|
||||
Log::debug('Now in recalculateRunningBalance');
|
||||
// find the earliest date in the set, based on date and _internal_previous_date
|
||||
$earliest = $set->pluck('date')->sort()->first();
|
||||
$fromInternalDate = $this->getFromInternalDate($set->pluck('id')->toArray());
|
||||
$earliest = $fromInternalDate->lt($earliest) ? $fromInternalDate : $earliest;
|
||||
Log::debug(sprintf('Found earliest date: %s', $earliest->toW3cString()));
|
||||
|
||||
// get accounts
|
||||
$accounts = Account::leftJoin('transactions', 'transactions.account_id', 'accounts.id')
|
||||
->leftJoin('transaction_journals', 'transaction_journals.id', 'transactions.transaction_journal_id')
|
||||
->leftJoin('account_types', 'account_types.id', 'accounts.account_type_id')
|
||||
->whereIn('transaction_journals.id', $set->pluck('id')->toArray())
|
||||
->get(['accounts.*'])
|
||||
;
|
||||
|
||||
Log::debug('Found accounts to process', $accounts->pluck('id')->toArray());
|
||||
|
||||
AccountBalanceCalculator::optimizedCalculation($accounts, $earliest);
|
||||
}
|
||||
|
||||
public static function removePeriodStatistics(Collection $set): void
|
||||
{
|
||||
if (auth()->check()) {
|
||||
Log::debug('Always remove period statistics');
|
||||
|
||||
/** @var PeriodStatisticRepositoryInterface $repository */
|
||||
$repository = app(PeriodStatisticRepositoryInterface::class);
|
||||
$repository->deleteStatisticsForCollection($set);
|
||||
}
|
||||
}
|
||||
|
||||
private function fireWebhooks(Collection $set): void
|
||||
{
|
||||
// collect transaction groups by set ids.
|
||||
$groups = TransactionGroup::whereIn('id', array_unique($set->pluck('transaction_group_id')->toArray()))->get();
|
||||
|
||||
Log::debug(__METHOD__);
|
||||
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $set->first();
|
||||
$user = $first->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
|
||||
// tell the generator which trigger it should look for
|
||||
$engine->setTrigger(WebhookTrigger::STORE_TRANSACTION);
|
||||
// tell the generator which objects to process
|
||||
$engine->setObjects($groups);
|
||||
// tell the generator to generate the messages
|
||||
$engine->generateMessages();
|
||||
|
||||
// trigger event to send them:
|
||||
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
|
||||
private function recalculateCredit(Collection $set): void
|
||||
{
|
||||
Log::debug(sprintf('Will now recalculateCredit for %d journal(s)', $set->count()));
|
||||
|
||||
/** @var CreditRecalculateService $object */
|
||||
$object = app(CreditRecalculateService::class);
|
||||
$object->setJournals($set);
|
||||
$object->recalculate();
|
||||
}
|
||||
|
||||
private function processRules(Collection $set): void
|
||||
{
|
||||
Log::debug(sprintf('Will now processRules for %d journal(s)', $set->count()));
|
||||
$array = $set->pluck('id')->toArray();
|
||||
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $set->first();
|
||||
$journalIds = implode(',', $array);
|
||||
$user = $first->user;
|
||||
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
|
||||
|
||||
// collect rules:
|
||||
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$ruleGroupRepository->setUser($user);
|
||||
|
||||
// add the groups to the rule engine.
|
||||
// it should run the rules in the group and cancel the group if necessary.
|
||||
Log::debug('Fire processRules with ALL store-journal rule groups.');
|
||||
$groups = $ruleGroupRepository->getRuleGroupsWithRules('store-journal');
|
||||
|
||||
// create and fire rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($user);
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
|
||||
$newRuleEngine->setRuleGroups($groups);
|
||||
$newRuleEngine->fire();
|
||||
$this->removePeriodStatistics($event->objects);
|
||||
$this->recalculateRunningBalance($event->objects);
|
||||
$repository->markAsCompleted($journals);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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->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()');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Listeners\Model\TransactionGroup;
|
||||
|
||||
use Carbon\Carbon;
|
||||
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;
|
||||
use FireflyIII\Repositories\PeriodStatistic\PeriodStatisticRepositoryInterface;
|
||||
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;
|
||||
|
||||
trait SupportsGroupProcessingTrait
|
||||
{
|
||||
private function recalculateCredit(Collection $accounts): void
|
||||
{
|
||||
Log::debug(sprintf('Will now recalculateCredit for %d account(s)', $accounts->count()));
|
||||
|
||||
/** @var CreditRecalculateService $object */
|
||||
$object = app(CreditRecalculateService::class);
|
||||
$object->setAccounts($accounts);
|
||||
$object->recalculate();
|
||||
}
|
||||
|
||||
private function fireWebhooks(Collection $journals, WebhookTrigger $trigger): void
|
||||
{
|
||||
// collect transaction groups by set ids.
|
||||
$groups = TransactionGroup::whereIn('id', array_unique($journals->pluck('transaction_group_id')->toArray()))->get();
|
||||
|
||||
Log::debug(__METHOD__);
|
||||
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $journals->first();
|
||||
$user = $first->user;
|
||||
|
||||
/** @var MessageGeneratorInterface $engine */
|
||||
$engine = app(MessageGeneratorInterface::class);
|
||||
$engine->setUser($user);
|
||||
|
||||
// tell the generator which trigger it should look for
|
||||
$engine->setTrigger($trigger);
|
||||
// tell the generator which objects to process
|
||||
$engine->setObjects($groups);
|
||||
// tell the generator to generate the messages
|
||||
$engine->generateMessages();
|
||||
}
|
||||
|
||||
protected function removePeriodStatistics(TransactionGroupEventObjects $objects): void
|
||||
{
|
||||
if (auth()->check()) {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
Log::debug(sprintf('Will now processRules("%s") for %d journal(s)', $type, $set->count()));
|
||||
$array = $set->pluck('id')->toArray();
|
||||
|
||||
/** @var TransactionJournal $first */
|
||||
$first = $set->first();
|
||||
$journalIds = implode(',', $array);
|
||||
$user = $first->user;
|
||||
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
|
||||
|
||||
// collect rules:
|
||||
$ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
$ruleGroupRepository->setUser($user);
|
||||
|
||||
// add the groups to the rule engine.
|
||||
// it should run the rules in the group and cancel the group if necessary.
|
||||
Log::debug(sprintf('Fire processRules with ALL %s rule groups.', $type));
|
||||
$groups = $ruleGroupRepository->getRuleGroupsWithRules($type);
|
||||
|
||||
// create and fire rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($user);
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
|
||||
$newRuleEngine->setRuleGroups($groups);
|
||||
$newRuleEngine->fire();
|
||||
}
|
||||
|
||||
protected function recalculateRunningBalance(TransactionGroupEventObjects $objects): void
|
||||
{
|
||||
Log::debug('Now in recalculateRunningBalance');
|
||||
if (true === FireflyConfig::get('use_running_balance', config('firefly.feature_flags.running_balance_column'))->data) {
|
||||
Log::debug('Running balance is disabled.');
|
||||
|
||||
return;
|
||||
}
|
||||
// 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()));
|
||||
|
||||
Log::debug('Found accounts to process', $objects->accounts->pluck('id')->toArray());
|
||||
|
||||
AccountBalanceCalculator::optimizedCalculation($objects->accounts, $earliest);
|
||||
}
|
||||
|
||||
private function getFromInternalDate(array $ids): Carbon
|
||||
{
|
||||
$entries = TransactionJournalMeta::whereIn('transaction_journal_id', $ids)->where('name', '_internal_previous_date')->get(['journal_meta.*']);
|
||||
$array = $entries->toArray();
|
||||
$return = today()->subDay();
|
||||
if (count($array) > 0) {
|
||||
usort($array, function (array $a, array $b) {
|
||||
return Carbon::parse($a['data'])->gt(Carbon::parse($b['data']));
|
||||
});
|
||||
|
||||
$date = Carbon::parse($array[0]['data']);
|
||||
$return = $date->lt($return) ? $date : $return;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ namespace FireflyIII\Repositories\TransactionGroup;
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Enums\TransactionTypeEnum;
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
|
||||
use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending;
|
||||
use FireflyIII\Exceptions\DuplicateTransactionException;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Factory\TransactionGroupFactory;
|
||||
@@ -43,6 +47,7 @@ use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
|
||||
use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService;
|
||||
use FireflyIII\Services\Internal\Update\GroupUpdateService;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Preferences;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\NullArrayObject;
|
||||
use FireflyIII\Support\Repositories\UserGroup\UserGroupInterface;
|
||||
@@ -391,12 +396,12 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
|
||||
public function store(array $data): TransactionGroup
|
||||
{
|
||||
/** @var TransactionGroupFactory $factory */
|
||||
$factory = app(TransactionGroupFactory::class);
|
||||
$factory = app(TransactionGroupFactory::class);
|
||||
$factory->setUser($data['user']);
|
||||
$factory->setUserGroup($data['user_group']);
|
||||
|
||||
try {
|
||||
return $factory->create($data);
|
||||
$transactionGroup = $factory->create($data);
|
||||
} catch (DuplicateTransactionException $e) {
|
||||
Log::warning('Group repository caught group factory with a duplicate exception!');
|
||||
|
||||
@@ -408,6 +413,19 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface,
|
||||
|
||||
throw new FireflyException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
Preferences::mark();
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($transactionGroup);
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$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());
|
||||
|
||||
return $transactionGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,12 +45,14 @@ class CreditRecalculateService
|
||||
private ?Account $account = null;
|
||||
private ?TransactionGroup $group = null;
|
||||
private Collection $journals;
|
||||
private Collection $accounts;
|
||||
private AccountRepositoryInterface $repository;
|
||||
private array $work = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->journals = new Collection();
|
||||
$this->accounts = new Collection();
|
||||
}
|
||||
|
||||
public function recalculate(): void
|
||||
@@ -65,6 +67,9 @@ class CreditRecalculateService
|
||||
// work based on account.
|
||||
$this->processAccount();
|
||||
}
|
||||
if ($this->accounts->count() > 0) {
|
||||
$this->processAccounts();
|
||||
}
|
||||
if ($this->journals->count() > 0) {
|
||||
$this->processJournals();
|
||||
}
|
||||
@@ -206,7 +211,7 @@ class CreditRecalculateService
|
||||
->orderBy('transaction_journals.date', 'ASC')
|
||||
->get(['transactions.*'])
|
||||
;
|
||||
$transactions->count();
|
||||
// $transactions->count();
|
||||
// Log::debug(sprintf('Found %d transaction(s) to process.', $total));
|
||||
|
||||
/** @var Transaction $transaction */
|
||||
@@ -493,4 +498,21 @@ class CreditRecalculateService
|
||||
{
|
||||
$this->journals = $journals;
|
||||
}
|
||||
|
||||
private function processAccounts(): void
|
||||
{
|
||||
$valid = config('firefly.valid_liabilities');
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($this->accounts as $account) {
|
||||
if (in_array($account->accountType->type, $valid, true)) {
|
||||
$this->work[] = $account;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setAccounts(Collection $accounts): void
|
||||
{
|
||||
$this->accounts = $accounts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Services\Internal\Update;
|
||||
|
||||
use FireflyIII\Events\Model\TransactionGroup\CreatedSingleTransactionGroup;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventFlags;
|
||||
use FireflyIII\Events\Model\TransactionGroup\TransactionGroupEventObjects;
|
||||
use FireflyIII\Events\Model\Webhook\WebhookMessagesRequestSending;
|
||||
use FireflyIII\Factory\PiggyBankEventFactory;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
@@ -34,6 +38,7 @@ use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class GroupCloneService
|
||||
@@ -48,6 +53,14 @@ class GroupCloneService
|
||||
$this->cloneJournal($journal, $newGroup, $group->id);
|
||||
}
|
||||
|
||||
// event!
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($newGroup);
|
||||
event(new CreatedSingleTransactionGroup($flags, $objects));
|
||||
|
||||
Log::debug(sprintf('send event WebhookMessagesRequestSending from %s', __METHOD__));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
|
||||
return $newGroup;
|
||||
}
|
||||
|
||||
|
||||
52
composer.lock
generated
52
composer.lock
generated
@@ -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",
|
||||
@@ -10530,16 +10530,16 @@
|
||||
},
|
||||
{
|
||||
"name": "fruitcake/laravel-debugbar",
|
||||
"version": "v4.0.5",
|
||||
"version": "v4.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fruitcake/laravel-debugbar.git",
|
||||
"reference": "1da86437d28f36baf3bb9841d77e74cb639372a9"
|
||||
"reference": "0cbf2986de59f66870cee565491b81eb89f8d25e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/1da86437d28f36baf3bb9841d77e74cb639372a9",
|
||||
"reference": "1da86437d28f36baf3bb9841d77e74cb639372a9",
|
||||
"url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/0cbf2986de59f66870cee565491b81eb89f8d25e",
|
||||
"reference": "0cbf2986de59f66870cee565491b81eb89f8d25e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -10616,7 +10616,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/fruitcake/laravel-debugbar/issues",
|
||||
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.5"
|
||||
"source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.0.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -10628,7 +10628,7 @@
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-01-29T19:18:02+00:00"
|
||||
"time": "2026-02-04T11:48:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "hamcrest/hamcrest-php",
|
||||
|
||||
@@ -79,7 +79,7 @@ return [
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2026-02-04',
|
||||
'build_time' => 1770187668,
|
||||
'build_time' => 1770234113,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 28, // field is no longer used.
|
||||
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -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.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.2.tgz",
|
||||
"integrity": "sha512-7KyJnG9n1nXXVqyOV/TAcp6/4QFgqMoob5y1xTPnWRU5wnrsDYRUvWEmF6RV98EY72ET+nUGkLQsmmO6T1l94Q==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user