Compare commits

..

6 Commits

Author SHA1 Message Date
github-actions[bot]
5577be7b08 Merge pull request #10428 from firefly-iii/release-1749383237
🤖 Automatically merge the PR into the develop branch.
2025-06-08 13:47:27 +02:00
JC5
e3bab9d7d8 🤖 Auto commit for release 'develop' on 2025-06-08 2025-06-08 13:47:17 +02:00
James Cole
e0512bee3d Fix #10427 2025-06-08 13:17:01 +02:00
James Cole
20827a5fd1 Fix #10426 2025-06-08 05:55:22 +02:00
James Cole
8819dac1e1 Merge branches 'develop' and 'develop' of github.com:firefly-iii/firefly-iii into develop 2025-06-07 06:31:19 +02:00
James Cole
da0a07262c Fix # 2025-06-07 06:31:14 +02:00
10 changed files with 228 additions and 213 deletions

View File

@@ -46,9 +46,9 @@ class PiggyBankFactory
{
use CreatesObjectGroups;
public User $user;
private AccountRepositoryInterface $accountRepository;
private CurrencyRepositoryInterface $currencyRepository;
public User $user;
private AccountRepositoryInterface $accountRepository;
private CurrencyRepositoryInterface $currencyRepository;
private PiggyBankRepositoryInterface $piggyBankRepository;
public function __construct()
@@ -78,7 +78,7 @@ class PiggyBankFactory
unset($piggyBankData['object_group_title'], $piggyBankData['transaction_currency_code'], $piggyBankData['transaction_currency_id'], $piggyBankData['accounts'], $piggyBankData['object_group_id'], $piggyBankData['notes']);
// validate amount:
if (array_key_exists('target_amount', $piggyBankData) && '' === (string) $piggyBankData['target_amount']) {
if (array_key_exists('target_amount', $piggyBankData) && '' === (string)$piggyBankData['target_amount']) {
$piggyBankData['target_amount'] = '0';
}
@@ -108,7 +108,7 @@ class PiggyBankFactory
}
}
// try also with ID
$objectGroupId = (int) ($data['object_group_id'] ?? 0);
$objectGroupId = (int)($data['object_group_id'] ?? 0);
if (0 !== $objectGroupId) {
$objectGroup = $this->findObjectGroupById($objectGroupId);
if ($objectGroup instanceof ObjectGroup) {
@@ -129,10 +129,10 @@ class PiggyBankFactory
$defaultCurrency = app('amount')->getNativeCurrency();
$currency = null;
if (array_key_exists('transaction_currency_code', $data)) {
$currency = $this->currencyRepository->findByCode((string) ($data['transaction_currency_code'] ?? ''));
$currency = $this->currencyRepository->findByCode((string)($data['transaction_currency_code'] ?? ''));
}
if (array_key_exists('transaction_currency_id', $data)) {
$currency = $this->currencyRepository->find((int) ($data['transaction_currency_id'] ?? 0));
$currency = $this->currencyRepository->find((int)($data['transaction_currency_id'] ?? 0));
}
$currency ??= $defaultCurrency;
@@ -141,8 +141,8 @@ class PiggyBankFactory
public function find(?int $piggyBankId, ?string $piggyBankName): ?PiggyBank
{
$piggyBankId = (int) $piggyBankId;
$piggyBankName = (string) $piggyBankName;
$piggyBankId = (int)$piggyBankId;
$piggyBankName = (string)$piggyBankName;
if ('' === $piggyBankName && 0 === $piggyBankId) {
return null;
}
@@ -221,7 +221,7 @@ class PiggyBankFactory
private function getMaxOrder(): int
{
return (int) $this->piggyBankRepository->getPiggyBanks()->max('order');
return (int)$this->piggyBankRepository->getPiggyBanks()->max('order');
}
@@ -230,12 +230,13 @@ class PiggyBankFactory
Log::debug(sprintf('Linking piggy bank #%d to %d accounts.', $piggyBank->id, count($accounts)), $accounts);
// collect current current_amount so the sync does not remove them.
// TODO this is a tedious check. Feels like a hack.
$toBeLinked = [];
$toBeLinked = [];
$oldSavedAmount = $this->piggyBankRepository->getCurrentAmount($piggyBank);
foreach ($piggyBank->accounts as $account) {
Log::debug(sprintf('Checking account #%d', $account->id));
foreach ($accounts as $info) {
Log::debug(sprintf(' Checking other account #%d', $info['account_id']));
if ((int) $account->id === (int) $info['account_id']) {
if ((int)$account->id === (int)$info['account_id']) {
$toBeLinked[$account->id] = ['current_amount' => $account->pivot->current_amount ?? '0'];
Log::debug(sprintf('Prefilled for account #%d with amount %s', $account->id, $account->pivot->current_amount ?? '0'));
}
@@ -244,9 +245,9 @@ class PiggyBankFactory
/** @var array $info */
foreach ($accounts as $info) {
$account = $this->accountRepository->find((int) ($info['account_id'] ?? 0));
$account = $this->accountRepository->find((int)($info['account_id'] ?? 0));
if (!$account instanceof Account) {
Log::debug(sprintf('Account #%d not found, skipping.', (int) ($info['account_id'] ?? 0)));
Log::debug(sprintf('Account #%d not found, skipping.', (int)($info['account_id'] ?? 0)));
continue;
}
@@ -290,7 +291,16 @@ class PiggyBankFactory
}
Log::debug(sprintf('Link information: %s', json_encode($toBeLinked)));
if (0 !== count($toBeLinked)) {
Log::debug('Syncing accounts to piggy bank.');
$piggyBank->accounts()->sync($toBeLinked);
$piggyBank->refresh();
$newSavedAmount = $this->piggyBankRepository->getCurrentAmount($piggyBank);
Log::debug(sprintf('Old saved amount: %s, new saved amount is %s', $oldSavedAmount, $newSavedAmount));
if (0 !== bccomp($oldSavedAmount, $newSavedAmount)) {
Log::debug('Amount changed, will create event for it.');
// create event for difference.
event(new ChangedAmount($piggyBank, bcsub($newSavedAmount, $oldSavedAmount), null, null));
}
}
if (0 === count($toBeLinked)) {
Log::warning('No accounts to link to piggy bank, will not change whatever is there now.');

View File

@@ -34,6 +34,7 @@ use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Collection;
use Illuminate\View\View;
/**
@@ -75,7 +76,6 @@ class ExecutionController extends Controller
$accounts = implode(',', $request->get('accounts'));
$startDate = new Carbon($request->get('start'));
$endDate = new Carbon($request->get('end'));
$rules = $this->ruleGroupRepository->getActiveRules($ruleGroup);
// create new rule engine:
$newRuleEngine = app(RuleEngineInterface::class);
$newRuleEngine->setUser($user);
@@ -86,7 +86,9 @@ class ExecutionController extends Controller
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
// set rules:
$newRuleEngine->setRules($rules);
// #10427, file rule group and not the set of rules.
$collection = new Collection([$ruleGroup]);
$newRuleEngine->setRuleGroups($collection);
$newRuleEngine->fire();
// Tell the user that the job is queued

View File

@@ -276,7 +276,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface, UserGroupInte
$amount = '' === $amount ? '0' : $amount;
$sum = bcadd($sum, $amount);
}
// Log::debug(sprintf('Current amount in piggy bank #%d ("%s") is %s', $piggyBank->id, $piggyBank->name, $sum));
Log::debug(sprintf('Current amount in piggy bank #%d ("%s") is %s', $piggyBank->id, $piggyBank->name, $sum));
return $sum;
}

View File

@@ -278,6 +278,8 @@ class Steam
$carbonKey = $carbon->format('Y-m-d');
// make sure sum is a string:
$sumOfDay = (string) ($entry->sum_of_day ?? '0');
// #10426 make sure sum is not in scientific notation.
$sumOfDay = $this->floatalize($sumOfDay);
// find currency of this entry, does not have to exist.
$currencies[$entry->transaction_currency_id] ??= TransactionCurrency::find($entry->transaction_currency_id);

View File

@@ -64,13 +64,13 @@ class SearchRuleEngine implements RuleEngineInterface
public function addOperator(array $operator): void
{
app('log')->debug('Add extra operator: ', $operator);
Log::debug('Add extra operator: ', $operator);
$this->operators[] = $operator;
}
public function find(): Collection
{
app('log')->debug('SearchRuleEngine::find()');
Log::debug('SearchRuleEngine::find()');
$collection = new Collection();
foreach ($this->rules as $rule) {
$found = new Collection();
@@ -83,7 +83,7 @@ class SearchRuleEngine implements RuleEngineInterface
$collection = $collection->merge($found);
}
$result = $collection->unique();
app('log')->debug(sprintf('SearchRuleEngine::find() returns %d unique transactions.', $result->count()));
Log::debug(sprintf('SearchRuleEngine::find() returns %d unique transactions.', $result->count()));
return $result;
}
@@ -93,7 +93,7 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function findStrictRule(Rule $rule): Collection
{
app('log')->debug(sprintf('Now in findStrictRule(#%d)', $rule->id ?? 0));
Log::debug(sprintf('Now in findStrictRule(#%d)', $rule->id ?? 0));
$searchArray = [];
$triggers = [];
if ($this->refreshTriggers) {
@@ -116,18 +116,18 @@ class SearchRuleEngine implements RuleEngineInterface
// if the trigger needs no context, value is different:
$needsContext = (bool) (config(sprintf('search.operators.%s.needs_context', $contextSearch)) ?? true);
if (false === $needsContext) {
app('log')->debug(sprintf('SearchRuleEngine:: add a rule trigger (no context): %s:true', $ruleTrigger->trigger_type));
Log::debug(sprintf('SearchRuleEngine:: add a rule trigger (no context): %s:true', $ruleTrigger->trigger_type));
$searchArray[$ruleTrigger->trigger_type][] = 'true';
}
if (true === $needsContext) {
app('log')->debug(sprintf('SearchRuleEngine:: add a rule trigger (context): %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value));
Log::debug(sprintf('SearchRuleEngine:: add a rule trigger (context): %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value));
$searchArray[$ruleTrigger->trigger_type][] = sprintf('"%s"', $ruleTrigger->trigger_value);
}
}
// add local operators:
foreach ($this->operators as $operator) {
app('log')->debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value']));
Log::debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value']));
$searchArray[$operator['type']][] = sprintf('"%s"', $operator['value']);
}
$date = today(config('app.timezone'));
@@ -141,11 +141,11 @@ class SearchRuleEngine implements RuleEngineInterface
$searchEngine->setPage(1);
$searchEngine->setLimit(31337);
$searchEngine->setDate($date);
app('log')->debug('Search array', $searchArray);
Log::debug('Search array', $searchArray);
foreach ($searchArray as $type => $searches) {
foreach ($searches as $value) {
$query = sprintf('%s:%s', $type, $value);
app('log')->debug(sprintf('SearchRuleEngine:: add query "%s"', $query));
Log::debug(sprintf('SearchRuleEngine:: add query "%s"', $query));
$searchEngine->parseQuery($query);
}
}
@@ -162,33 +162,33 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function hasSpecificJournalTrigger(array $array): bool
{
app('log')->debug('Now in hasSpecificJournalTrigger.');
Log::debug('Now in hasSpecificJournalTrigger.');
$journalTrigger = false;
$dateTrigger = false;
foreach ($array as $triggerName => $values) {
if ('journal_id' === $triggerName && is_array($values) && 1 === count($values)) {
app('log')->debug('Found a journal_id trigger with 1 journal, true.');
Log::debug('Found a journal_id trigger with 1 journal, true.');
$journalTrigger = true;
}
if (in_array($triggerName, ['date_is', 'date', 'on', 'date_before', 'before', 'date_after', 'after'], true)) {
app('log')->debug('Found a date related trigger, set to true.');
Log::debug('Found a date related trigger, set to true.');
$dateTrigger = true;
}
}
$result = $journalTrigger && $dateTrigger;
app('log')->debug(sprintf('Result of hasSpecificJournalTrigger is %s.', var_export($result, true)));
Log::debug(sprintf('Result of hasSpecificJournalTrigger is %s.', var_export($result, true)));
return $result;
}
private function setDateFromJournalTrigger(array $array): Carbon
{
app('log')->debug('Now in setDateFromJournalTrigger()');
Log::debug('Now in setDateFromJournalTrigger()');
$journalId = 0;
foreach ($array as $triggerName => $values) {
if ('journal_id' === $triggerName && is_array($values) && 1 === count($values)) {
$journalId = (int) trim($values[0] ?? '"0"', '"'); // follows format "123".
app('log')->debug(sprintf('Found journal ID #%d', $journalId));
Log::debug(sprintf('Found journal ID #%d', $journalId));
}
}
if (0 !== $journalId) {
@@ -197,12 +197,12 @@ class SearchRuleEngine implements RuleEngineInterface
$journal = $repository->find($journalId);
if (null !== $journal) {
$date = $journal->date;
app('log')->debug(sprintf('Found journal #%d with date %s.', $journal->id, $journal->date->format('Y-m-d')));
Log::debug(sprintf('Found journal #%d with date %s.', $journal->id, $journal->date->format('Y-m-d')));
return $date;
}
}
app('log')->debug('Found no journal, return default date.');
Log::debug('Found no journal, return default date.');
return today(config('app.timezone'));
}
@@ -215,48 +215,48 @@ class SearchRuleEngine implements RuleEngineInterface
private function findNonStrictRule(Rule $rule): Collection
{
app('log')->debug(sprintf('findNonStrictRule(#%d)', $rule->id));
Log::debug(sprintf('findNonStrictRule(#%d)', $rule->id));
// start a search query for individual each trigger:
$total = new Collection();
$count = 0;
$triggers = [];
if ($this->refreshTriggers) {
app('log')->debug('Will refresh triggers.');
Log::debug('Will refresh triggers.');
$triggers = $rule->ruleTriggers()->orderBy('order', 'ASC')->get();
}
if (!$this->refreshTriggers) {
app('log')->debug('Will not refresh triggers.');
Log::debug('Will not refresh triggers.');
$triggers = $rule->ruleTriggers;
}
app('log')->debug(sprintf('Will run %d trigger(s).', count($triggers)));
Log::debug(sprintf('Will run %d trigger(s).', count($triggers)));
/** @var RuleTrigger $ruleTrigger */
foreach ($triggers as $ruleTrigger) {
app('log')->debug(sprintf('Now at rule trigger #%d: %s:"%s" (%s).', $ruleTrigger->id, $ruleTrigger->trigger_type, $ruleTrigger->trigger_value, var_export($ruleTrigger->stop_processing, true)));
Log::debug(sprintf('Now at rule trigger #%d: %s:"%s" (%s).', $ruleTrigger->id, $ruleTrigger->trigger_type, $ruleTrigger->trigger_value, var_export($ruleTrigger->stop_processing, true)));
if (false === $ruleTrigger->active) {
app('log')->debug('Trigger is not active, continue.');
Log::debug('Trigger is not active, continue.');
continue;
}
if ('user_action' === $ruleTrigger->trigger_type) {
app('log')->debug('Skip trigger type. continue.');
Log::debug('Skip trigger type. continue.');
continue;
}
$searchArray = [];
$needsContext = config(sprintf('search.operators.%s.needs_context', $ruleTrigger->trigger_type)) ?? true;
if (false === $needsContext) {
app('log')->debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:true', $ruleTrigger->trigger_type));
Log::debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:true', $ruleTrigger->trigger_type));
$searchArray[$ruleTrigger->trigger_type] = 'true';
}
if (true === $needsContext) {
app('log')->debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value));
Log::debug(sprintf('SearchRuleEngine:: non strict, will search for: %s:"%s"', $ruleTrigger->trigger_type, $ruleTrigger->trigger_value));
$searchArray[$ruleTrigger->trigger_type] = sprintf('"%s"', $ruleTrigger->trigger_value);
}
// then, add local operators as well:
foreach ($this->operators as $operator) {
app('log')->debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value']));
Log::debug(sprintf('SearchRuleEngine:: add local added operator: %s:"%s"', $operator['type'], $operator['value']));
$searchArray[$operator['type']] = sprintf('"%s"', $operator['value']);
}
@@ -272,19 +272,19 @@ class SearchRuleEngine implements RuleEngineInterface
$result = $searchEngine->searchTransactions();
$collection = $result->getCollection();
app('log')->debug(sprintf('Found in this run, %d transactions', $collection->count()));
Log::debug(sprintf('Found in this run, %d transactions', $collection->count()));
$total = $total->merge($collection);
app('log')->debug(sprintf('Total collection is now %d transactions', $total->count()));
Log::debug(sprintf('Total collection is now %d transactions', $total->count()));
++$count;
// if trigger says stop processing, do so.
if (true === $ruleTrigger->stop_processing && $result->count() > 0) {
app('log')->debug('The trigger says to stop processing, so stop processing other triggers.');
Log::debug('The trigger says to stop processing, so stop processing other triggers.');
break;
}
}
app('log')->debug(sprintf('Total collection is now %d transactions', $total->count()));
app('log')->debug(sprintf('Done running %d trigger(s)', $count));
Log::debug(sprintf('Total collection is now %d transactions', $total->count()));
Log::debug(sprintf('Done running %d trigger(s)', $count));
// make collection unique
$unique = $total->unique(
@@ -295,11 +295,11 @@ class SearchRuleEngine implements RuleEngineInterface
}
return sprintf('%d%s', $group['id'], $str);
// app('log')->debug(sprintf('Return key: %s ', $key));
// Log::debug(sprintf('Return key: %s ', $key));
}
);
app('log')->debug(sprintf('SearchRuleEngine:: Found %d transactions using search engine.', $unique->count()));
Log::debug(sprintf('SearchRuleEngine:: Found %d transactions using search engine.', $unique->count()));
return $unique;
}
@@ -310,28 +310,28 @@ class SearchRuleEngine implements RuleEngineInterface
public function fire(): void
{
$this->resultCount = [];
app('log')->debug('SearchRuleEngine::fire()!');
Log::debug('SearchRuleEngine::fire()!');
// if rules and no rule groups, file each rule separately.
if (0 !== $this->rules->count()) {
app('log')->debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count()));
Log::debug(sprintf('SearchRuleEngine:: found %d rule(s) to fire.', $this->rules->count()));
/** @var Rule $rule */
foreach ($this->rules as $rule) {
$result = $this->fireRule($rule);
if (true === $result && true === $rule->stop_processing) {
app('log')->debug(sprintf('Rule #%d has triggered and executed, but calls to stop processing. Since not in the context of a group, do not stop.', $rule->id));
Log::debug(sprintf('Rule #%d has triggered and executed, but calls to stop processing. Since not in the context of a group, do not stop.', $rule->id));
}
if (false === $result && true === $rule->stop_processing) {
app('log')->debug(sprintf('Rule #%d has triggered and changed nothing, but calls to stop processing. Do not stop.', $rule->id));
Log::debug(sprintf('Rule #%d has triggered and changed nothing, but calls to stop processing. Do not stop.', $rule->id));
}
}
app('log')->debug('SearchRuleEngine:: done processing all rules!');
Log::debug('SearchRuleEngine:: done processing all rules!');
return;
}
if (0 !== $this->groups->count()) {
app('log')->debug(sprintf('SearchRuleEngine:: found %d rule group(s) to fire.', $this->groups->count()));
Log::debug(sprintf('SearchRuleEngine:: found %d rule group(s) to fire.', $this->groups->count()));
// fire each group:
/** @var RuleGroup $group */
@@ -339,7 +339,7 @@ class SearchRuleEngine implements RuleEngineInterface
$this->fireGroup($group);
}
}
app('log')->debug('SearchRuleEngine:: done processing all rules!');
Log::debug('SearchRuleEngine:: done processing all rules!');
}
/**
@@ -349,18 +349,18 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function fireRule(Rule $rule): bool
{
app('log')->debug(sprintf('Now going to fire rule #%d', $rule->id));
Log::debug(sprintf('Now going to fire rule #%d', $rule->id));
if (false === $rule->active) {
app('log')->debug(sprintf('Rule #%d is not active!', $rule->id));
Log::debug(sprintf('Rule #%d is not active!', $rule->id));
return false;
}
if (true === $rule->strict) {
app('log')->debug(sprintf('Rule #%d is a strict rule.', $rule->id));
Log::debug(sprintf('Rule #%d is a strict rule.', $rule->id));
return $this->fireStrictRule($rule);
}
app('log')->debug(sprintf('Rule #%d is not strict rule.', $rule->id));
Log::debug(sprintf('Rule #%d is not strict rule.', $rule->id));
return $this->fireNonStrictRule($rule);
}
@@ -372,19 +372,19 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function fireStrictRule(Rule $rule): bool
{
app('log')->debug(sprintf('SearchRuleEngine::fireStrictRule(%d)!', $rule->id));
Log::debug(sprintf('SearchRuleEngine::fireStrictRule(%d)!', $rule->id));
$collection = $this->findStrictRule($rule);
$this->processResults($rule, $collection);
app('log')->debug(sprintf('SearchRuleEngine:: done processing strict rule #%d', $rule->id));
Log::debug(sprintf('SearchRuleEngine:: done processing strict rule #%d', $rule->id));
$result = $collection->count() > 0;
if (true === $result) {
app('log')->debug(sprintf('SearchRuleEngine:: rule #%d was triggered (on %d transaction(s)).', $rule->id, $collection->count()));
Log::debug(sprintf('SearchRuleEngine:: rule #%d was triggered (on %d transaction(s)).', $rule->id, $collection->count()));
return true;
}
app('log')->debug(sprintf('SearchRuleEngine:: rule #%d was not triggered (on %d transaction(s)).', $rule->id, $collection->count()));
Log::debug(sprintf('SearchRuleEngine:: rule #%d was not triggered (on %d transaction(s)).', $rule->id, $collection->count()));
return false;
}
@@ -394,7 +394,7 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function processResults(Rule $rule, Collection $collection): void
{
app('log')->debug(sprintf('SearchRuleEngine:: Going to process %d results.', $collection->count()));
Log::debug(sprintf('SearchRuleEngine:: Going to process %d results.', $collection->count()));
/** @var array $group */
foreach ($collection as $group) {
@@ -407,7 +407,7 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function processTransactionGroup(Rule $rule, array $group): void
{
app('log')->debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction group #%d', $group['id']));
Log::debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction group #%d', $group['id']));
/** @var array $transaction */
foreach ($group['transactions'] as $transaction) {
@@ -420,7 +420,7 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function processTransactionJournal(Rule $rule, array $transaction): void
{
app('log')->debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id']));
Log::debug(sprintf('SearchRuleEngine:: Will now execute actions on transaction journal #%d', $transaction['transaction_journal_id']));
$actions = $rule->ruleActions()->orderBy('order', 'ASC')->get();
/** @var RuleAction $ruleAction */
@@ -440,14 +440,14 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function processRuleAction(RuleAction $ruleAction, array $transaction): bool
{
app('log')->debug(sprintf('Executing rule action "%s" with value "%s"', $ruleAction->action_type, $ruleAction->action_value));
Log::debug(sprintf('Executing rule action "%s" with value "%s"', $ruleAction->action_type, $ruleAction->action_value));
$transaction = $this->addNotes($transaction);
$actionClass = ActionFactory::getAction($ruleAction);
$result = $actionClass->actOnArray($transaction);
$journalId = $transaction['transaction_journal_id'] ?? 0;
if (true === $result) {
$this->resultCount[$journalId] = array_key_exists($journalId, $this->resultCount) ? $this->resultCount[$journalId]++ : 1;
app('log')->debug(
Log::debug(
sprintf(
'Action "%s" on journal #%d was executed, so count a result. Updated transaction journal count is now %d.',
$ruleAction->action_type,
@@ -457,17 +457,17 @@ class SearchRuleEngine implements RuleEngineInterface
);
}
if (false === $result) {
app('log')->debug(sprintf('Action "%s" reports NO changes were made.', $ruleAction->action_type));
Log::debug(sprintf('Action "%s" reports NO changes were made.', $ruleAction->action_type));
}
// pick up from the action if it actually acted or not:
if (true === $ruleAction->stop_processing && true === $result) {
app('log')->debug(sprintf('Rule action "%s" reports changes AND asks to break, so break!', $ruleAction->action_type));
Log::debug(sprintf('Rule action "%s" reports changes AND asks to break, so break!', $ruleAction->action_type));
return true;
}
if (true === $ruleAction->stop_processing && false === $result) {
app('log')->debug(sprintf('Rule action "%s" reports NO changes AND asks to break, but we wont break!', $ruleAction->action_type));
Log::debug(sprintf('Rule action "%s" reports NO changes AND asks to break, but we wont break!', $ruleAction->action_type));
}
return false;
@@ -492,11 +492,11 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function fireNonStrictRule(Rule $rule): bool
{
app('log')->debug(sprintf('SearchRuleEngine::fireNonStrictRule(%d)!', $rule->id));
Log::debug(sprintf('SearchRuleEngine::fireNonStrictRule(%d)!', $rule->id));
$collection = $this->findNonStrictRule($rule);
$this->processResults($rule, $collection);
app('log')->debug(sprintf('SearchRuleEngine:: done processing non-strict rule #%d', $rule->id));
Log::debug(sprintf('SearchRuleEngine:: done processing non-strict rule #%d', $rule->id));
return $collection->count() > 0;
}
@@ -506,14 +506,14 @@ class SearchRuleEngine implements RuleEngineInterface
*/
private function fireGroup(RuleGroup $group): void
{
app('log')->debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count()));
Log::debug(sprintf('Going to fire group #%d with %d rule(s)', $group->id, $group->rules->count()));
/** @var Rule $rule */
foreach ($group->rules as $rule) {
app('log')->debug(sprintf('Going to fire rule #%d from group #%d', $rule->id, $group->id));
Log::debug(sprintf('Going to fire rule #%d from group #%d', $rule->id, $group->id));
$result = $this->fireRule($rule);
if (true === $result && true === $rule->stop_processing) {
app('log')->debug(sprintf('The rule was triggered and rule->stop_processing = true, so group #%d will stop processing further rules.', $group->id));
Log::debug(sprintf('The rule was triggered and rule->stop_processing = true, so group #%d will stop processing further rules.', $group->id));
return;
}
@@ -535,10 +535,10 @@ class SearchRuleEngine implements RuleEngineInterface
public function setRuleGroups(Collection $ruleGroups): void
{
app('log')->debug(__METHOD__);
Log::debug(__METHOD__);
foreach ($ruleGroups as $group) {
if ($group instanceof RuleGroup) {
app('log')->debug(sprintf('Adding a rule group to the SearchRuleEngine: #%d ("%s")', $group->id, $group->title));
Log::debug(sprintf('Adding a rule group to the SearchRuleEngine: #%d ("%s")', $group->id, $group->title));
$this->groups->push($group);
}
}
@@ -546,10 +546,10 @@ class SearchRuleEngine implements RuleEngineInterface
public function setRules(Collection $rules): void
{
app('log')->debug(__METHOD__);
Log::debug(__METHOD__);
foreach ($rules as $rule) {
if ($rule instanceof Rule) {
app('log')->debug(sprintf('Adding a rule to the SearchRuleEngine: #%d ("%s")', $rule->id, $rule->title));
Log::debug(sprintf('Adding a rule to the SearchRuleEngine: #%d ("%s")', $rule->id, $rule->title));
$this->rules->push($rule);
}
}

View File

@@ -73,8 +73,8 @@ class PiggyBankEventTransformer extends AbstractTransformer
return [
'id' => (string) $event->id,
'created_at' => $event->created_at->toAtomString(),
'updated_at' => $event->updated_at->toAtomString(),
'created_at' => $event->created_at?->toAtomString(),
'updated_at' => $event->updated_at?->toAtomString(),
'amount' => app('steam')->bcround($event->amount, $currency->decimal_places),
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,

18
composer.lock generated
View File

@@ -11551,16 +11551,16 @@
},
{
"name": "phpunit/phpunit",
"version": "12.1.6",
"version": "12.2.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "2fdf0056c673c8f0f1eed00030be5f8243c1e6e0"
"reference": "5f09fda04e7caea93cff50b4e90319184f3e6ee3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2fdf0056c673c8f0f1eed00030be5f8243c1e6e0",
"reference": "2fdf0056c673c8f0f1eed00030be5f8243c1e6e0",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f09fda04e7caea93cff50b4e90319184f3e6ee3",
"reference": "5f09fda04e7caea93cff50b4e90319184f3e6ee3",
"shasum": ""
},
"require": {
@@ -11574,7 +11574,7 @@
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.3",
"phpunit/php-code-coverage": "^12.2.1",
"phpunit/php-code-coverage": "^12.3.0",
"phpunit/php-file-iterator": "^6.0.0",
"phpunit/php-invoker": "^6.0.0",
"phpunit/php-text-template": "^5.0.0",
@@ -11582,7 +11582,7 @@
"sebastian/cli-parser": "^4.0.0",
"sebastian/comparator": "^7.0.1",
"sebastian/diff": "^7.0.0",
"sebastian/environment": "^8.0.1",
"sebastian/environment": "^8.0.2",
"sebastian/exporter": "^7.0.0",
"sebastian/global-state": "^8.0.0",
"sebastian/object-enumerator": "^7.0.0",
@@ -11596,7 +11596,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "12.1-dev"
"dev-main": "12.2-dev"
}
},
"autoload": {
@@ -11628,7 +11628,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.1.6"
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.2.1"
},
"funding": [
{
@@ -11652,7 +11652,7 @@
"type": "tidelift"
}
],
"time": "2025-05-21T12:36:31+00:00"
"time": "2025-06-07T05:17:47+00:00"
},
{
"name": "rector/rector",

View File

@@ -78,7 +78,7 @@ return [
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
// see cer.php for exchange rates feature flag.
],
'version' => 'develop/2025-06-04',
'version' => 'develop/2025-06-08',
'api_version' => '2.1.0', // field is no longer used.
'db_version' => 25,

235
package-lock.json generated
View File

@@ -392,14 +392,14 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.27.4",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.4.tgz",
"integrity": "sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ==",
"version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz",
"integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.27.2",
"@babel/types": "^7.27.3"
"@babel/types": "^7.27.6"
},
"engines": {
"node": ">=6.9.0"
@@ -1592,9 +1592,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.27.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.4.tgz",
"integrity": "sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA==",
"version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
"integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1635,9 +1635,9 @@
}
},
"node_modules/@babel/types": {
"version": "7.27.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.3.tgz",
"integrity": "sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==",
"version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz",
"integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2552,9 +2552,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz",
"integrity": "sha512-NELNvyEWZ6R9QMkiytB4/L4zSEaBC03KIXEghptLGLZWJ6VPrL63ooZQCOnlx36aQPGhzuOMwDerC1Eb2VmrLw==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.42.0.tgz",
"integrity": "sha512-gldmAyS9hpj+H6LpRNlcjQWbuKUtb94lodB9uCz71Jm+7BxK1VIOo7y62tZZwxhA7j1ylv/yQz080L5WkS+LoQ==",
"cpu": [
"arm"
],
@@ -2566,9 +2566,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.41.1.tgz",
"integrity": "sha512-DXdQe1BJ6TK47ukAoZLehRHhfKnKg9BjnQYUu9gzhI8Mwa1d2fzxA1aw2JixHVl403bwp1+/o/NhhHtxWJBgEA==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.42.0.tgz",
"integrity": "sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==",
"cpu": [
"arm64"
],
@@ -2580,9 +2580,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.41.1.tgz",
"integrity": "sha512-5afxvwszzdulsU2w8JKWwY8/sJOLPzf0e1bFuvcW5h9zsEg+RQAojdW0ux2zyYAz7R8HvvzKCjLNJhVq965U7w==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.42.0.tgz",
"integrity": "sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==",
"cpu": [
"arm64"
],
@@ -2594,9 +2594,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.41.1.tgz",
"integrity": "sha512-egpJACny8QOdHNNMZKf8xY0Is6gIMz+tuqXlusxquWu3F833DcMwmGM7WlvCO9sB3OsPjdC4U0wHw5FabzCGZg==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.42.0.tgz",
"integrity": "sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==",
"cpu": [
"x64"
],
@@ -2608,9 +2608,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.41.1.tgz",
"integrity": "sha512-DBVMZH5vbjgRk3r0OzgjS38z+atlupJ7xfKIDJdZZL6sM6wjfDNo64aowcLPKIx7LMQi8vybB56uh1Ftck/Atg==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.42.0.tgz",
"integrity": "sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==",
"cpu": [
"arm64"
],
@@ -2622,9 +2622,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.41.1.tgz",
"integrity": "sha512-3FkydeohozEskBxNWEIbPfOE0aqQgB6ttTkJ159uWOFn42VLyfAiyD9UK5mhu+ItWzft60DycIN1Xdgiy8o/SA==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.42.0.tgz",
"integrity": "sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==",
"cpu": [
"x64"
],
@@ -2636,9 +2636,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.41.1.tgz",
"integrity": "sha512-wC53ZNDgt0pqx5xCAgNunkTzFE8GTgdZ9EwYGVcg+jEjJdZGtq9xPjDnFgfFozQI/Xm1mh+D9YlYtl+ueswNEg==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.42.0.tgz",
"integrity": "sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==",
"cpu": [
"arm"
],
@@ -2650,9 +2650,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.41.1.tgz",
"integrity": "sha512-jwKCca1gbZkZLhLRtsrka5N8sFAaxrGz/7wRJ8Wwvq3jug7toO21vWlViihG85ei7uJTpzbXZRcORotE+xyrLA==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.42.0.tgz",
"integrity": "sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==",
"cpu": [
"arm"
],
@@ -2664,9 +2664,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.41.1.tgz",
"integrity": "sha512-g0UBcNknsmmNQ8V2d/zD2P7WWfJKU0F1nu0k5pW4rvdb+BIqMm8ToluW/eeRmxCared5dD76lS04uL4UaNgpNA==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.42.0.tgz",
"integrity": "sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==",
"cpu": [
"arm64"
],
@@ -2678,9 +2678,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.41.1.tgz",
"integrity": "sha512-XZpeGB5TKEZWzIrj7sXr+BEaSgo/ma/kCgrZgL0oo5qdB1JlTzIYQKel/RmhT6vMAvOdM2teYlAaOGJpJ9lahg==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.42.0.tgz",
"integrity": "sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==",
"cpu": [
"arm64"
],
@@ -2692,9 +2692,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.41.1.tgz",
"integrity": "sha512-bkCfDJ4qzWfFRCNt5RVV4DOw6KEgFTUZi2r2RuYhGWC8WhCA8lCAJhDeAmrM/fdiAH54m0mA0Vk2FGRPyzI+tw==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.42.0.tgz",
"integrity": "sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==",
"cpu": [
"loong64"
],
@@ -2706,9 +2706,9 @@
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.41.1.tgz",
"integrity": "sha512-3mr3Xm+gvMX+/8EKogIZSIEF0WUu0HL9di+YWlJpO8CQBnoLAEL/roTCxuLncEdgcfJcvA4UMOf+2dnjl4Ut1A==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.42.0.tgz",
"integrity": "sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==",
"cpu": [
"ppc64"
],
@@ -2720,9 +2720,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.41.1.tgz",
"integrity": "sha512-3rwCIh6MQ1LGrvKJitQjZFuQnT2wxfU+ivhNBzmxXTXPllewOF7JR1s2vMX/tWtUYFgphygxjqMl76q4aMotGw==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.42.0.tgz",
"integrity": "sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==",
"cpu": [
"riscv64"
],
@@ -2734,9 +2734,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.41.1.tgz",
"integrity": "sha512-LdIUOb3gvfmpkgFZuccNa2uYiqtgZAz3PTzjuM5bH3nvuy9ty6RGc/Q0+HDFrHrizJGVpjnTZ1yS5TNNjFlklw==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.42.0.tgz",
"integrity": "sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==",
"cpu": [
"riscv64"
],
@@ -2748,9 +2748,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.41.1.tgz",
"integrity": "sha512-oIE6M8WC9ma6xYqjvPhzZYk6NbobIURvP/lEbh7FWplcMO6gn7MM2yHKA1eC/GvYwzNKK/1LYgqzdkZ8YFxR8g==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.42.0.tgz",
"integrity": "sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==",
"cpu": [
"s390x"
],
@@ -2762,9 +2762,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.41.1.tgz",
"integrity": "sha512-cWBOvayNvA+SyeQMp79BHPK8ws6sHSsYnK5zDcsC3Hsxr1dgTABKjMnMslPq1DvZIp6uO7kIWhiGwaTdR4Og9A==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.42.0.tgz",
"integrity": "sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==",
"cpu": [
"x64"
],
@@ -2776,9 +2776,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.41.1.tgz",
"integrity": "sha512-y5CbN44M+pUCdGDlZFzGGBSKCA4A/J2ZH4edTYSSxFg7ce1Xt3GtydbVKWLlzL+INfFIZAEg1ZV6hh9+QQf9YQ==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.42.0.tgz",
"integrity": "sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==",
"cpu": [
"x64"
],
@@ -2790,9 +2790,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.41.1.tgz",
"integrity": "sha512-lZkCxIrjlJlMt1dLO/FbpZbzt6J/A8p4DnqzSa4PWqPEUUUnzXLeki/iyPLfV0BmHItlYgHUqJe+3KiyydmiNQ==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.42.0.tgz",
"integrity": "sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==",
"cpu": [
"arm64"
],
@@ -2804,9 +2804,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.41.1.tgz",
"integrity": "sha512-+psFT9+pIh2iuGsxFYYa/LhS5MFKmuivRsx9iPJWNSGbh2XVEjk90fmpUEjCnILPEPJnikAU6SFDiEUyOv90Pg==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.42.0.tgz",
"integrity": "sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==",
"cpu": [
"ia32"
],
@@ -2818,9 +2818,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.41.1.tgz",
"integrity": "sha512-Wq2zpapRYLfi4aKxf2Xff0tN+7slj2d4R87WEzqw7ZLsVvO5zwYCIuEGSZYiK41+GlwUo1HiR+GdkLEJnCKTCw==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.42.0.tgz",
"integrity": "sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==",
"cpu": [
"x64"
],
@@ -2887,9 +2887,9 @@
}
},
"node_modules/@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
"version": "1.19.6",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
"integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2969,9 +2969,9 @@
"license": "MIT"
},
"node_modules/@types/express": {
"version": "4.17.22",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.22.tgz",
"integrity": "sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==",
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz",
"integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3019,9 +3019,9 @@
}
},
"node_modules/@types/http-errors": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
"integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
"dev": true,
"license": "MIT"
},
@@ -3108,9 +3108,9 @@
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.15.29",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.29.tgz",
"integrity": "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ==",
"version": "22.15.30",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.30.tgz",
"integrity": "sha512-6Q7lr06bEHdlfplU6YRbgG1SFBdlsfNC4/lX+SkhiTs0cpJkOElmWls8PxDFv4yY/xKb8Y6SO0OmSX4wgqTZbA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3156,9 +3156,9 @@
"license": "MIT"
},
"node_modules/@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"version": "0.17.5",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
"integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3177,9 +3177,9 @@
}
},
"node_modules/@types/serve-static": {
"version": "1.15.7",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
"version": "1.15.8",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz",
"integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6322,15 +6322,16 @@
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz",
"integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==",
"dev": true,
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -9973,9 +9974,9 @@
}
},
"node_modules/rollup": {
"version": "4.41.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.41.1.tgz",
"integrity": "sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==",
"version": "4.42.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.42.0.tgz",
"integrity": "sha512-LW+Vse3BJPyGJGAJt1j8pWDKPd73QM8cRXYK1IxOBgL2AGLu7Xd2YOW0M2sLUBCkF5MshXXtMApyEAEzMVMsnw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9989,26 +9990,26 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.41.1",
"@rollup/rollup-android-arm64": "4.41.1",
"@rollup/rollup-darwin-arm64": "4.41.1",
"@rollup/rollup-darwin-x64": "4.41.1",
"@rollup/rollup-freebsd-arm64": "4.41.1",
"@rollup/rollup-freebsd-x64": "4.41.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.41.1",
"@rollup/rollup-linux-arm-musleabihf": "4.41.1",
"@rollup/rollup-linux-arm64-gnu": "4.41.1",
"@rollup/rollup-linux-arm64-musl": "4.41.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.41.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.41.1",
"@rollup/rollup-linux-riscv64-gnu": "4.41.1",
"@rollup/rollup-linux-riscv64-musl": "4.41.1",
"@rollup/rollup-linux-s390x-gnu": "4.41.1",
"@rollup/rollup-linux-x64-gnu": "4.41.1",
"@rollup/rollup-linux-x64-musl": "4.41.1",
"@rollup/rollup-win32-arm64-msvc": "4.41.1",
"@rollup/rollup-win32-ia32-msvc": "4.41.1",
"@rollup/rollup-win32-x64-msvc": "4.41.1",
"@rollup/rollup-android-arm-eabi": "4.42.0",
"@rollup/rollup-android-arm64": "4.42.0",
"@rollup/rollup-darwin-arm64": "4.42.0",
"@rollup/rollup-darwin-x64": "4.42.0",
"@rollup/rollup-freebsd-arm64": "4.42.0",
"@rollup/rollup-freebsd-x64": "4.42.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.42.0",
"@rollup/rollup-linux-arm-musleabihf": "4.42.0",
"@rollup/rollup-linux-arm64-gnu": "4.42.0",
"@rollup/rollup-linux-arm64-musl": "4.42.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.42.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.42.0",
"@rollup/rollup-linux-riscv64-gnu": "4.42.0",
"@rollup/rollup-linux-riscv64-musl": "4.42.0",
"@rollup/rollup-linux-s390x-gnu": "4.42.0",
"@rollup/rollup-linux-x64-gnu": "4.42.0",
"@rollup/rollup-linux-x64-musl": "4.42.0",
"@rollup/rollup-win32-arm64-msvc": "4.42.0",
"@rollup/rollup-win32-ia32-msvc": "4.42.0",
"@rollup/rollup-win32-x64-msvc": "4.42.0",
"fsevents": "~2.3.2"
}
},
@@ -10858,9 +10859,9 @@
}
},
"node_modules/terser": {
"version": "5.40.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.40.0.tgz",
"integrity": "sha512-cfeKl/jjwSR5ar7d0FGmave9hFGJT8obyo0z+CrQOylLDbk7X81nPU6vq9VORa5jU30SkDnT2FXjLbR8HLP+xA==",
"version": "5.41.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.41.0.tgz",
"integrity": "sha512-H406eLPXpZbAX14+B8psIuvIr8+3c+2hkuYzpMkoE0ij+NdsVATbA78vb8neA/eqrj7rywa2pIkdmWRsXW6wmw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {

View File

@@ -2,7 +2,7 @@
<label for="{{ options.id }}" class="col-sm-4 control-label">{{ label }}</label>
<div class="col-sm-8">
{{ Html.multiselect(name~"[]", list, selected).id(options.id).class('form-control').attribute('autocomplete','off').attribute('spellcheck','false').attribute('placeholder', options.placeholder) }}
{{ Html.multiselect(name~"[]", list, selected).id(options.id).attribute('size',12).class('form-control').attribute('autocomplete','off').attribute('spellcheck','false').attribute('placeholder', options.placeholder) }}
{% include 'form.help' %}
{% include 'form.feedback' %}