Clean up processing for group.

This commit is contained in:
James Cole
2026-02-04 20:17:47 +01:00
parent 20986e6426
commit d25283f193
5 changed files with 81 additions and 80 deletions
@@ -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,12 +33,15 @@ 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();
$object->transactionGroups->push($transactionGroup);
/** @var TransactionJournal $journal */
foreach ($transactionGroup->transactionJournals as $journal) {
@@ -36,6 +36,8 @@ class ProcessesNewTransactionGroup implements ShouldQueue
{
use SupportsGroupProcessingTrait;
public function handle(CreatedSingleTransactionGroup|UserRequestedBatchProcessing $event): void
{
Log::debug(sprintf('User called %s', get_class($event)));
@@ -46,7 +48,7 @@ class ProcessesNewTransactionGroup implements ShouldQueue
return;
}
Log::debug('Will also collect all open transaction groups and process them.');
Log::debug('Will also collect all open transaction groups and process them as well.');
$repository = app(JournalRepositoryInterface::class);
$journals = $event->objects->transactionJournals->merge($repository->getAllUncompletedJournals());
@@ -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;
@@ -41,8 +44,8 @@ trait SupportsGroupProcessingTrait
Log::debug(__METHOD__);
/** @var TransactionJournal $first */
$first = $journals->first();
$user = $first->user;
$first = $journals->first();
$user = $first->user;
/** @var MessageGeneratorInterface $engine */
$engine = app(MessageGeneratorInterface::class);
@@ -56,28 +59,54 @@ trait SupportsGroupProcessingTrait
$engine->generateMessages();
}
protected function removePeriodStatistics(TransactionGroupEventObjects $set): void
protected function removePeriodStatistics(TransactionGroupEventObjects $objects): void
{
if (auth()->check()) {
Log::debug('Always remove period statistics');
// 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);
$repository->deleteStatisticsForCollection($set->transactionJournals);
$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);
// FIXME extend for categories, accounts, etc.
// 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();
$array = $set->pluck('id')->toArray();
/** @var TransactionJournal $first */
$first = $set->first();
$journalIds = implode(',', $array);
$user = $first->user;
$first = $set->first();
$journalIds = implode(',', $array);
$user = $first->user;
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
// collect rules:
@@ -87,33 +116,32 @@ trait SupportsGroupProcessingTrait
// 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);
$groups = $ruleGroupRepository->getRuleGroupsWithRules($type);
// create and fire rule engine.
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine->setUser($user);
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
$newRuleEngine->setRuleGroups($groups);
$newRuleEngine->fire();
}
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;
}
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
@@ -184,65 +184,30 @@ 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);
// }
}
}
@@ -37,6 +37,7 @@ interface PeriodStatisticRepositoryInterface
public function findPeriodStatistic(Model $model, Carbon $start, Carbon $end, string $type): Collection;
public function saveStatistic(Model $model, int $currencyId, Carbon $start, Carbon $end, string $type, int $count, string $amount): PeriodStatistic;
public function savePrefixedStatistic(