mirror of
https://github.com/we-promise/sure
synced 2026-04-25 17:15:07 +02:00
Refactor transaction merging logic to prefer accurate posted dates and improve deduplication (#449)
- Updated `date_utils.rb` to return `nil` for timestamps equal to 0. - Enhanced `SimplefinItem::Importer` to merge transactions by prioritizing non-pending records with valid posted timestamps. - Introduced a comparator and key-builder for resolving transaction conflicts systematically. Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com>
This commit is contained in:
@@ -499,14 +499,68 @@ class SimplefinItem::Importer
|
||||
org_data: account_data[:org]
|
||||
}
|
||||
|
||||
# Merge transactions from chunked imports (accumulate historical data)
|
||||
# Merge transactions from chunked/regular imports (accumulate history).
|
||||
# Prefer non-pending records with a real posted timestamp over earlier
|
||||
# pending placeholders that sometimes come back with posted: 0.
|
||||
if transactions.is_a?(Array) && transactions.any?
|
||||
existing_transactions = simplefin_account.raw_transactions_payload.to_a
|
||||
merged_transactions = (existing_transactions + transactions).uniq do |tx|
|
||||
tx = tx.with_indifferent_access
|
||||
tx[:id] || tx[:fitid] || [ tx[:posted], tx[:amount], tx[:description] ]
|
||||
|
||||
# Build a map of key => best_tx
|
||||
best_by_key = {}
|
||||
|
||||
comparator = lambda do |a, b|
|
||||
ax = a.with_indifferent_access
|
||||
bx = b.with_indifferent_access
|
||||
|
||||
# Key dates
|
||||
a_posted = ax[:posted].to_i
|
||||
b_posted = bx[:posted].to_i
|
||||
a_trans = ax[:transacted_at].to_i
|
||||
b_trans = bx[:transacted_at].to_i
|
||||
|
||||
a_pending = !!ax[:pending]
|
||||
b_pending = !!bx[:pending]
|
||||
|
||||
# 1) Prefer real posted date over 0/blank
|
||||
a_has_posted = a_posted > 0
|
||||
b_has_posted = b_posted > 0
|
||||
return a if a_has_posted && !b_has_posted
|
||||
return b if b_has_posted && !a_has_posted
|
||||
|
||||
# 2) Prefer later posted date
|
||||
if a_posted != b_posted
|
||||
return a_posted > b_posted ? a : b
|
||||
end
|
||||
|
||||
# 3) Prefer non-pending over pending
|
||||
if a_pending != b_pending
|
||||
return a_pending ? b : a
|
||||
end
|
||||
|
||||
# 4) Prefer later transacted_at
|
||||
if a_trans != b_trans
|
||||
return a_trans > b_trans ? a : b
|
||||
end
|
||||
|
||||
# 5) Stable: keep 'a'
|
||||
a
|
||||
end
|
||||
attrs[:raw_transactions_payload] = merged_transactions
|
||||
|
||||
build_key = lambda do |tx|
|
||||
t = tx.with_indifferent_access
|
||||
t[:id] || t[:fitid] || [ t[:posted], t[:amount], t[:description] ]
|
||||
end
|
||||
|
||||
(existing_transactions + transactions).each do |tx|
|
||||
key = build_key.call(tx)
|
||||
if (cur = best_by_key[key])
|
||||
best_by_key[key] = comparator.call(cur, tx)
|
||||
else
|
||||
best_by_key[key] = tx
|
||||
end
|
||||
end
|
||||
|
||||
attrs[:raw_transactions_payload] = best_by_key.values
|
||||
end
|
||||
|
||||
# Track whether incoming holdings are new/changed so we can materialize and refresh balances
|
||||
|
||||
@@ -15,6 +15,7 @@ module Simplefin
|
||||
when Time, DateTime
|
||||
val.to_date
|
||||
when Integer, Float
|
||||
return nil if val.to_i == 0
|
||||
Time.at(val).utc.to_date
|
||||
when String
|
||||
Date.parse(val)
|
||||
|
||||
Reference in New Issue
Block a user