Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion app/models/account/provider_import_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,25 @@ def import_transaction(external_id:, amount:, currency:, date:, name:, source:,
# Track if this is a new posted transaction (for fuzzy suggestion after save)
is_new_posted = entry.new_record? && !incoming_pending

# Preserve the original pending date across all syncs:
# - First claim: pending_entry_date is captured from the pending match above
# - Subsequent syncs: entry already exists (no pending_match found), so check
# auto_claimed_pending_ids which signals it was previously auto-claimed and
# keep entry.date (the pending date stored on first claim) unchanged
effective_date = if pending_entry_date
pending_entry_date
elsif !entry.new_record? &&
entry.entryable.is_a?(Transaction) &&
entry.transaction.extra&.key?("auto_claimed_pending_ids")
entry.date
else
date
end

entry.assign_attributes(
amount: amount,
currency: currency,
date: pending_entry_date || date
date: effective_date
)

# Use enrichment pattern to respect user overrides
Expand Down
47 changes: 47 additions & 0 deletions test/models/account/provider_import_adapter_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,53 @@ class Account::ProviderImportAdapterTest < ActiveSupport::TestCase
end
end

test "preserves pending date on subsequent syncs after auto-claim" do
# On the first sync the pending date is captured from the pending match.
# On subsequent syncs the entry already exists (found by booked external_id),
# so pending_match is nil. auto_claimed_pending_ids signals the prior claim
# and the stored date must not be overwritten with the booked date.
pending_date = Date.today - 3.days
booked_date = Date.today

@adapter.import_transaction(
external_id: "eb_pending_subseq",
amount: 75.00,
currency: "EUR",
date: pending_date,
name: "Coffee Shop",
source: "enable_banking",
extra: { "enable_banking" => { "pending" => true } }
)

# First sync: claim the pending entry
@adapter.import_transaction(
external_id: "eb_booked_subseq",
amount: 75.00,
currency: "EUR",
date: booked_date,
name: "Coffee Shop Posted",
source: "enable_banking",
extra: nil
)

# Simulate a subsequent sync: same booked transaction arrives again
assert_no_difference "@account.entries.count" do
re_synced = @adapter.import_transaction(
external_id: "eb_booked_subseq",
amount: 75.00,
currency: "EUR",
date: booked_date,
name: "Coffee Shop Posted",
source: "enable_banking",
extra: nil
)

re_synced.reload
assert_equal pending_date, re_synced.date,
"pending date must be preserved on subsequent syncs, not overwritten with booked date"
end
end

test "does not reconcile when posted transaction has same external_id as pending" do
# When external_id matches, normal dedup should handle it
pending_entry = @adapter.import_transaction(
Expand Down
Loading