diff --git a/app/Models/RuleAction.php b/app/Models/RuleAction.php index eed2a1dc13..ae949c89d7 100644 --- a/app/Models/RuleAction.php +++ b/app/Models/RuleAction.php @@ -67,12 +67,12 @@ class RuleAction extends Model protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'active' => 'boolean', - 'order' => 'int', - 'stop_processing' => 'boolean', - ]; + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'active' => 'boolean', + 'order' => 'int', + 'stop_processing' => 'boolean', + ]; protected $fillable = ['rule_id', 'action_type', 'action_value', 'order', 'active', 'stop_processing']; diff --git a/app/TransactionRules/Actions/SetAmount.php b/app/TransactionRules/Actions/SetAmount.php new file mode 100644 index 0000000000..54501e6659 --- /dev/null +++ b/app/TransactionRules/Actions/SetAmount.php @@ -0,0 +1,127 @@ +action = $action; + } + + public function actOnArray(array $journal): bool + { + $this->refreshNotes($journal); + + // not on slpit transactions + $groupCount = TransactionJournal::where('transaction_group_id', $journal['transaction_group_id'])->count(); + if ($groupCount > 1) { + app('log')->error(sprintf('Group #%d has more than one transaction in it, cannot convert to transfer.', $journal['transaction_group_id'])); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.split_group'))); + + return false; + } + + $value = $this->action->getValue($journal); + + if (!is_numeric($value) || '' === $value || 0 === bccomp((string)$value, '0')) { + app('log')->debug(sprintf('RuleAction SetAmount, amount "%s" is not a number or is zero, will not continue.', $value)); + event(new RuleActionFailedOnArray($this->action, $journal, trans('rules.journal_invalid_amount', ['amount' => $value]))); + + return false; + } + + /** @var TransactionJournal $object */ + $object = TransactionJournal::where('user_id', $journal['user_id'])->find($journal['transaction_journal_id']); + + // doesn't actually do anything! + $positive = Steam::positive($value); + $negative = Steam::negative($value); + + $this->updatePositive($object, $positive); + $this->updateNegative($object, $negative); + $object->transactionGroup->touch(); + + // event for audit log entry + + event(new TriggeredAuditLog( + $this->action->rule, + $object, + 'update_amount', + [ + 'currency_symbol' => $object->transactionCurrency->symbol, + 'decimal_places' => $object->transactionCurrency->decimal_places, + 'amount' => $journal['amount'], + ], + [ + 'currency_symbol' => $object->transactionCurrency->symbol, + 'decimal_places' => $object->transactionCurrency->decimal_places, + 'amount' => $value, + ] + )); + + return true; + } + + private function updatePositive(TransactionJournal $object, string $amount): void + { + /** @var null|Transaction $transaction */ + $transaction = $object->transactions()->where('amount', '>', 0)->first(); + if (null === $transaction) { + return; + } + $this->updateAmount($transaction, $amount); + } + + private function updateNegative(TransactionJournal $object, string $amount): void + { + /** @var null|Transaction $transaction */ + $transaction = $object->transactions()->where('amount', '<', 0)->first(); + if (null === $transaction) { + return; + } + $this->updateAmount($transaction, $amount); + } + + private function updateAmount(Transaction $transaction, string $amount): void + { + $transaction->amount = $amount; + $transaction->save(); + $transaction->transactionJournal->touch(); + } +} diff --git a/app/TransactionRules/Expressions/ActionExpression.php b/app/TransactionRules/Expressions/ActionExpression.php index 1e089ad0e4..40708487fe 100644 --- a/app/TransactionRules/Expressions/ActionExpression.php +++ b/app/TransactionRules/Expressions/ActionExpression.php @@ -30,31 +30,31 @@ use Symfony\Component\ExpressionLanguage\SyntaxError; class ActionExpression { private static array $NAMES = [ - 'transaction_group_id', - 'user_id', - 'user_group_id', + // 'transaction_group_id', + // 'user_id', + // 'user_group_id', 'created_at', 'updated_at', 'transaction_group_title', 'group_created_at', 'group_updated_at', - 'transaction_journal_id', - 'transaction_type_id', + // 'transaction_journal_id', + // 'transaction_type_id', 'description', 'date', - 'order', + // 'order', 'transaction_type_type', - 'source_transaction_id', + // 'source_transaction_id', 'source_account_id', - 'reconciled', + // 'reconciled', 'amount', - 'currency_id', + // 'currency_id', 'currency_code', 'currency_name', 'currency_symbol', 'currency_decimal_places', 'foreign_amount', - 'foreign_currency_id', + // 'foreign_currency_id', 'foreign_currency_code', 'foreign_currency_name', 'foreign_currency_symbol', @@ -71,14 +71,14 @@ class ActionExpression 'budget_id', 'budget_name', 'tags', - 'attachments', + // 'attachments', 'interest_date', 'payment_date', 'invoice_date', 'book_date', 'due_date', 'process_date', - 'destination_transaction_id', + // 'destination_transaction_id', 'notes', ]; diff --git a/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php b/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php index 03b3d69970..a608fc6848 100644 --- a/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php +++ b/app/TransactionRules/Expressions/ActionExpressionLanguageProvider.php @@ -32,24 +32,34 @@ class ActionExpressionLanguageProvider implements ExpressionFunctionProviderInte public function getFunctions(): array { return [ - new ExpressionFunction('constant', function ($str): string { - return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str.'!'); - }, function ($arguments, $str): string { - if (!is_string($str)) { - return $str; - } + new ExpressionFunction( + 'constant', + function ($str): string { + return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str.'!'); + }, + // @SuppressWarnings(PHPMD.UnusedFormalParameter) + function ($arguments, $str): string { + if (!is_string($str)) { + return (string) $str; + } - return strtolower($str.'!'); - }), - new ExpressionFunction('enum', function ($str): string { - return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str.'?'); - }, function ($arguments, $str): string { - if (!is_string($str)) { - return $str; + return strtolower($str.'!'); } + ), + new ExpressionFunction( + 'enum', + function ($str): string { + return sprintf('(is_string(%1$s) ? strtolower(%1$s) : %1$s)', $str.'?'); + }, + // @SuppressWarnings(PHPMD.UnusedFormalParameter) + function ($arguments, $str): string { + if (!is_string($str)) { + return (string) $str; + } - return strtolower($str).'?'; - }), + return strtolower($str).'?'; + } + ), ExpressionFunction::fromPhp('substr'), ExpressionFunction::fromPhp('strlen'), diff --git a/app/Transformers/V2/AccountTransformer.php b/app/Transformers/V2/AccountTransformer.php index 727c1962ef..4e0fb1e6c0 100644 --- a/app/Transformers/V2/AccountTransformer.php +++ b/app/Transformers/V2/AccountTransformer.php @@ -91,7 +91,7 @@ class AccountTransformer extends AbstractTransformer // TODO needs separate method. $sort = $this->parameters->get('sort'); - if (count($sort) > 0) { + if (is_countable($sort) && count($sort) > 0) { foreach ($sort as $column => $direction) { // account_number + iban if ('iban' === $column) { diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 9517d92188..9331479b7b 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -829,7 +829,8 @@ class FireflyValidator extends Validator ->where('trigger', $trigger) ->where('response', $response) ->where('delivery', $delivery) - ->where('url', $url)->count(); + ->where('url', $url)->count() + ; } return false; diff --git a/config/firefly.php b/config/firefly.php index 4c081b593e..fa0ee36959 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -83,6 +83,7 @@ use FireflyIII\TransactionRules\Actions\PrependDescription; use FireflyIII\TransactionRules\Actions\PrependNotes; use FireflyIII\TransactionRules\Actions\RemoveAllTags; use FireflyIII\TransactionRules\Actions\RemoveTag; +use FireflyIII\TransactionRules\Actions\SetAmount; use FireflyIII\TransactionRules\Actions\SetBudget; use FireflyIII\TransactionRules\Actions\SetCategory; use FireflyIII\TransactionRules\Actions\SetDescription; @@ -109,11 +110,11 @@ return [ ], // some feature flags: 'feature_flags' => [ - 'export' => true, - 'telemetry' => false, - 'webhooks' => true, - 'handle_debts' => true, - 'expression_engine' => false, + 'export' => true, + 'telemetry' => false, + 'webhooks' => true, + 'handle_debts' => true, + 'expression_engine' => true, // see cer.php for exchange rates feature flag. ], 'version' => 'develop/2024-03-07', @@ -521,6 +522,9 @@ return [ 'move_notes_to_descr' => MoveNotesToDescription::class, 'set_source_to_cash' => SetSourceToCashAccount::class, 'set_destination_to_cash' => SetDestinationToCashAccount::class, + 'set_amount' => SetAmount::class, + // 'set_foreign_amount' => SetForeignAmount::class, + // 'set_foreign_currency' => SetForeignCurrency::class, ], 'context-rule-actions' => [ 'set_category', diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index 66accd2b86..caf38e16cb 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1250,6 +1250,8 @@ return [ 'rule_action_append_notes_to_descr' => 'Append notes to description', 'rule_action_move_descr_to_notes' => 'Replace notes with description', 'rule_action_move_notes_to_descr' => 'Replace description with notes', + 'rule_action_set_amount_choice' => 'Set amount to ..', + 'rule_action_set_amount' => 'Set amount to ":action_value"', 'rule_action_set_destination_to_cash_choice' => 'Set destination account to (cash)', 'rule_action_set_source_to_cash_choice' => 'Set source account to (cash)', 'rulegroup_for_bills_title' => 'Rule group for bills', @@ -2678,6 +2680,7 @@ return [ 'ale_action_add_to_piggy' => 'Piggy bank', 'ale_action_remove_from_piggy' => 'Piggy bank', 'ale_action_add_tag' => 'Added tag', + 'ale_action_update_amount' => 'Updated amount', // dashboard 'enable_auto_convert' => 'Enable currency conversion', diff --git a/resources/lang/en_US/rules.php b/resources/lang/en_US/rules.php index 8d4b44ad76..020162caf3 100644 --- a/resources/lang/en_US/rules.php +++ b/resources/lang/en_US/rules.php @@ -70,4 +70,5 @@ return [ 'cannot_find_budget' => 'Firefly III can\'t find budget ":name"', 'cannot_find_category' => 'Firefly III can\'t find category ":name"', 'cannot_set_budget' => 'Firefly III can\'t set budget ":name" to a transaction of type ":type"', + 'journal_invalid_amount' => 'Firefly III can\'t set amount ":amount" because it is not a valid number.', ]; diff --git a/resources/views/list/ale.twig b/resources/views/list/ale.twig index 3632ec3bd1..09433efa82 100644 --- a/resources/views/list/ale.twig +++ b/resources/views/list/ale.twig @@ -22,6 +22,12 @@ {{ logEntry.after }} {% endif %} + {% if 'update_amount' == logEntry.action %} + {{ formatAmountBySymbol(logEntry.before.amount, logEntry.before.currency_symbol, logEntry.before.decimal_places, true) }} + → + {{ formatAmountBySymbol(logEntry.after.amount, logEntry.after.currency_symbol, logEntry.after.decimal_places, true) }} + {% endif %} + {% if 'update_group_title' == logEntry.action %} {{ logEntry.before }}