feat: Allow to create rules to mark transactions as transfers or payments (#920)

* feat: Allow to create rules to define transfer or payments

* fix: lint issues

* Update app/models/rule/action_executor/set_as_transfer_or_payment.rb

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com>

* fix: indentation issue

* fix: add explicit return

* fix: add guard on target_account

* fix: use local variable for transfer and revert explicit return as it doesn't work

* fix: Adjust transaction naming

---------

Signed-off-by: Alessio Cappa <104093777+alessiocappa@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
Alessio Cappa
2026-02-06 21:17:04 +01:00
committed by GitHub
parent b88734fb5e
commit 7d0fe057d3
2 changed files with 50 additions and 1 deletions

View File

@@ -0,0 +1,48 @@
class Rule::ActionExecutor::SetAsTransferOrPayment < Rule::ActionExecutor
def type
"select"
end
def options
family.accounts.alphabetically.pluck(:name, :id)
end
def execute(transaction_scope, value: nil, ignore_attribute_locks: false, rule_run: nil)
target_account = family.accounts.find_by_id(value)
return 0 unless target_account
scope = transaction_scope.with_entry
count_modified_resources(scope) do |txn|
entry = txn.entry
unless txn.transfer?
transfer = build_transfer(target_account, entry)
Transfer.transaction do
transfer.save!
transfer.outflow_transaction.update!(kind: Transfer.kind_for_account(transfer.outflow_transaction.entry.account))
transfer.inflow_transaction.update!(kind: "funds_movement")
end
transfer.sync_account_later
end
end
end
private
def build_transfer(target_account, entry)
missing_transaction = Transaction.new(
entry: target_account.entries.build(
amount: entry.amount * -1,
currency: entry.currency,
date: entry.date,
name: "#{target_account.liability? ? "Payment" : "Transfer"} #{entry.amount.negative? ? "to #{target_account.name}" : "from #{entry.account.name}"}",
)
)
transfer = Transfer.find_or_initialize_by(
inflow_transaction: entry.amount.positive? ? missing_transaction : entry.transaction,
outflow_transaction: entry.amount.positive? ? entry.transaction : missing_transaction
)
transfer.status = "confirmed"
transfer
end
end

View File

@@ -22,7 +22,8 @@ class Rule::Registry::TransactionResource < Rule::Registry
Rule::ActionExecutor::SetTransactionMerchant.new(rule),
Rule::ActionExecutor::SetTransactionName.new(rule),
Rule::ActionExecutor::SetInvestmentActivityLabel.new(rule),
Rule::ActionExecutor::ExcludeTransaction.new(rule)
Rule::ActionExecutor::ExcludeTransaction.new(rule),
Rule::ActionExecutor::SetAsTransferOrPayment.new(rule)
]
if ai_enabled?