mirror of
https://github.com/we-promise/sure.git
synced 2026-05-29 23:39:03 +00:00
feat(enable-banking): safe pending transaction merge with sync re-import prevention (#1709)
* feat(enable-banking): safe pending transaction merge with sync re-import prevention * preserve all merged pending IDs across syncs * fix(enable-banking): harden merge locking, safe logging, and non-blocking index * fix(enable-banking): use safe external ID in invalid currency log * refactor(models): centralize pending transaction SQL logic Move the SQL fragment used to identify pending transactions from the `Entry` model to a constant in the `Transaction` model. This improves maintainability and ensures that the logic for determining if a transaction is pending is defined in a single location. * fix(enable-banking): drop dead manual_merge index, use lateral join for excluded IDs * No net schema changes --------- Co-authored-by: Juan José Mata <jjmata@jjmata.com>
This commit is contained in:
@@ -15,6 +15,7 @@ class EnableBankingAccount::Transactions::Processor
|
||||
Rails.logger.info "EnableBankingAccount::Transactions::Processor - Processing #{total_count} transactions for enable_banking_account #{enable_banking_account.id}"
|
||||
|
||||
imported_count = 0
|
||||
skipped_count = 0
|
||||
failed_count = 0
|
||||
errors = []
|
||||
|
||||
@@ -22,8 +23,43 @@ class EnableBankingAccount::Transactions::Processor
|
||||
Account::ProviderImportAdapter.new(enable_banking_account.current_account)
|
||||
end
|
||||
|
||||
# Pre-fetch external_ids that were manually merged and must not be re-imported.
|
||||
# One query per sync; O(1) Set lookup per transaction — avoids N+1.
|
||||
# Uses a lateral jsonb_array_elements join to extract only the ID strings in SQL,
|
||||
# avoiding loading full extra blobs into Ruby. Handles both Array (current) and
|
||||
# Hash (legacy) formats via jsonb_typeof.
|
||||
excluded_ids = if enable_banking_account.current_account
|
||||
Transaction.joins(:entry)
|
||||
.where(entries: { account_id: enable_banking_account.current_account.id })
|
||||
.where("transactions.extra ? 'manual_merge'")
|
||||
.joins(
|
||||
Arel.sql(<<~SQL.squish)
|
||||
CROSS JOIN LATERAL jsonb_array_elements(
|
||||
CASE jsonb_typeof(transactions.extra->'manual_merge')
|
||||
WHEN 'array' THEN transactions.extra->'manual_merge'
|
||||
WHEN 'object' THEN jsonb_build_array(transactions.extra->'manual_merge')
|
||||
ELSE '[]'::jsonb
|
||||
END
|
||||
) AS merge_elem
|
||||
SQL
|
||||
)
|
||||
.pluck(Arel.sql("merge_elem->>'merged_from_external_id'"))
|
||||
.compact
|
||||
.to_set
|
||||
else
|
||||
Set.new
|
||||
end
|
||||
|
||||
enable_banking_account.raw_transactions_payload.each_with_index do |transaction_data, index|
|
||||
begin
|
||||
ext_id = EnableBankingEntry::Processor.compute_external_id(transaction_data)
|
||||
|
||||
if ext_id && excluded_ids.include?(ext_id)
|
||||
Rails.logger.info("EnableBankingAccount::Transactions::Processor - Skipping re-import of manually merged pending transaction: #{ext_id}")
|
||||
skipped_count += 1
|
||||
next
|
||||
end
|
||||
|
||||
result = EnableBankingEntry::Processor.new(
|
||||
transaction_data,
|
||||
enable_banking_account: enable_banking_account,
|
||||
@@ -56,6 +92,7 @@ class EnableBankingAccount::Transactions::Processor
|
||||
success: failed_count == 0,
|
||||
total: total_count,
|
||||
imported: imported_count,
|
||||
skipped: skipped_count,
|
||||
failed: failed_count,
|
||||
errors: errors
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user