mirror of
https://github.com/we-promise/sure.git
synced 2026-07-19 08:15:21 +00:00
feat(up): flag internal transfers and round-ups as funds_movement (#2460)
* feat(up): flag internal transfers and round-ups as funds_movement Up populates relationships.transferAccount on transactions that move money between the user's own accounts (including round-ups swept into a Saver), but flatten_transaction dropped it, so these imported as ordinary income/expense and distorted budgets and cashflow. - Provider::Up#flatten_transaction: lift transfer_account_id from relationships.transferAccount.data.id. - UpEntry::Processor: import transfers as funds_movement and persist transfer_account_id in extra["up"]. - Account::ProviderImportAdapter#import_transaction: optional kind: param; an explicit provider kind takes precedence over account-type auto-detection and is applied after the sync-protection check, so user re-categorisations survive re-sync. Complementary to Family#auto_match_transfers!: two-sided transfers between linked accounts are still paired into a Transfer (the matcher does not filter on kind); one-sided movements and round-ups, which the matcher cannot pair, are the cases this fixes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(up): account-type kind wins over provider transfer hint Codex review caught that Up HOME_LOAN accounts map to a Loan account, so a repayment carrying transferAccount would be reclassified from loan_payment to funds_movement (budget-excluded). Make the provider kind: a fallback: activity-label and account-type classification now take precedence, so loan_payment and cc_payment survive. Adds a regression test (loan repayment stays loan_payment), a depository-applies test, and a note that the up_test stub ignores query: intentionally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Gavin Matthews <matthews.gav@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -113,4 +113,42 @@ class UpEntry::ProcessorTest < ActiveSupport::TestCase
|
||||
|
||||
assert_equal BigDecimal("-2500.0"), entry.amount
|
||||
end
|
||||
|
||||
test "marks internal transfers (transferAccount present) as funds_movement" do
|
||||
entry = UpEntry::Processor.new(
|
||||
{
|
||||
id: "tx_transfer_1",
|
||||
account_id: "acc_123",
|
||||
status: "SETTLED",
|
||||
description: "Transfer to 2Up Spending",
|
||||
amount: { currencyCode: "AUD", value: "-500.00", valueInBaseUnits: -50000 },
|
||||
settledAt: "2026-01-22T00:00:00+11:00",
|
||||
createdAt: "2026-01-22T00:00:00+11:00",
|
||||
transfer_account_id: "acc_other"
|
||||
},
|
||||
up_account: @up_account
|
||||
).process
|
||||
|
||||
transaction = entry.entryable
|
||||
assert_equal "funds_movement", transaction.kind
|
||||
assert_equal "acc_other", transaction.extra.dig("up", "transfer_account_id")
|
||||
end
|
||||
|
||||
test "ordinary transactions (no transferAccount) keep the standard kind" do
|
||||
entry = UpEntry::Processor.new(
|
||||
{
|
||||
id: "tx_standard_1",
|
||||
account_id: "acc_123",
|
||||
status: "SETTLED",
|
||||
description: "Coffee Shop",
|
||||
amount: { currencyCode: "AUD", value: "-4.50", valueInBaseUnits: -450 },
|
||||
settledAt: "2026-01-22T00:00:00+11:00",
|
||||
createdAt: "2026-01-22T00:00:00+11:00"
|
||||
},
|
||||
up_account: @up_account
|
||||
).process
|
||||
|
||||
assert_equal "standard", entry.entryable.kind
|
||||
assert_nil entry.entryable.extra.dig("up", "transfer_account_id")
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user