Pending detection, FX metadata, Pending UI badge. (#374)

* - Add support for `SIMPLEFIN_INCLUDE_PENDING` to control pending behavior via ENV.
- Enhance debug logging for SimpleFin API requests and raw payloads.
- Refine pending flag handling in `SimplefinEntry::Processor` based on provider data and inferred conditions.
- Improve FX metadata processing for transactions with currency mismatches.
- Add new tests for pending detection, FX metadata, and edge cases involving `posted` values.
- Add pending indicator UI to transaction view.

* Document pending transaction detection, storage, and UI behavior for SimpleFIN and Plaid integrations. Add debug flags for troubleshooting.

* Add `pending?` method to `Transaction` model, refactor UI indicator, and centralize SimpleFIN configuration

- Introduced `pending?` method in `Transaction` for unified pending state detection.
- Refactored transaction pending indicator in the UI to use `pending?` method.
- Centralized SimpleFIN configuration in initializer with ENV-backed toggles.
- Updated tests for `pending?` behavior and clarified docs for pending detection logic

* Add SimpleFIN debug and runtime flags to `.env.local.example` and `.env.test.example`

- Introduced `SIMPLEFIN_INCLUDE_PENDING` and `SIMPLEFIN_DEBUG_RAW` flags for controlling pending behavior and debugging.
- Updated example environment files with descriptions for new configuration options.

* Normalize formatting for `SIMPLEFIN_INCLUDE_PENDING` and `SIMPLEFIN_DEBUG_RAW` flags in `.env.local.example` and `.env.test.example`.

---------

Co-authored-by: Josh Waldrep <joshua.waldrep5+github@gmail.com>
This commit is contained in:
LPW
2025-12-19 17:24:48 -05:00
committed by GitHub
parent febbf42e1b
commit 664c6c2b7c
13 changed files with 229 additions and 3 deletions

View File

@@ -53,4 +53,90 @@ class SimplefinEntry::ProcessorTest < ActiveSupport::TestCase
assert_equal "Order #1234", sf["description"]
assert_equal({ "category" => "restaurants", "check_number" => nil }, sf["extra"])
end
test "flags pending transaction when posted is nil and transacted_at present" do
tx = {
id: "tx_pending_1",
amount: "-20.00",
currency: "USD",
payee: "Coffee Shop",
description: "Latte",
memo: "Morning run",
posted: nil,
transacted_at: (Date.today - 3).to_s
}
SimplefinEntry::Processor.new(tx, simplefin_account: @simplefin_account).process
entry = @account.entries.find_by!(external_id: "simplefin_tx_pending_1", source: "simplefin")
sf = entry.transaction.extra.fetch("simplefin")
assert_equal true, sf["pending"], "expected pending flag to be true"
end
test "captures FX metadata when tx currency differs from account currency" do
# Account is USD from setup; use EUR for tx
t_date = (Date.today - 5)
p_date = Date.today
tx = {
id: "tx_fx_1",
amount: "-42.00",
currency: "EUR",
payee: "Boulangerie",
description: "Croissant",
posted: p_date.to_s,
transacted_at: t_date.to_s
}
SimplefinEntry::Processor.new(tx, simplefin_account: @simplefin_account).process
entry = @account.entries.find_by!(external_id: "simplefin_tx_fx_1", source: "simplefin")
sf = entry.transaction.extra.fetch("simplefin")
assert_equal "EUR", sf["fx_from"]
assert_equal t_date.to_s, sf["fx_date"], "fx_date should prefer transacted_at"
end
test "flags pending when provider pending flag is true (even if posted provided)" do
tx = {
id: "tx_pending_flag_1",
amount: "-9.99",
currency: "USD",
payee: "Test Store",
description: "Auth",
memo: "",
posted: Date.today.to_s, # provider says pending=true should still flag
transacted_at: (Date.today - 1).to_s,
pending: true
}
SimplefinEntry::Processor.new(tx, simplefin_account: @simplefin_account).process
entry = @account.entries.find_by!(external_id: "simplefin_tx_pending_flag_1", source: "simplefin")
sf = entry.transaction.extra.fetch("simplefin")
assert_equal true, sf["pending"], "expected pending flag to be true when provider sends pending=true"
end
test "posted==0 treated as missing, entry uses transacted_at date and flags pending" do
# Simulate provider sending epoch-like zeros for posted and an integer transacted_at
t_epoch = (Date.today - 2).to_time.to_i
tx = {
id: "tx_pending_zero_posted_1",
amount: "-6.48",
currency: "USD",
payee: "Dunkin'",
description: "DUNKIN #358863",
memo: "",
posted: 0,
transacted_at: t_epoch,
pending: true
}
SimplefinEntry::Processor.new(tx, simplefin_account: @simplefin_account).process
entry = @account.entries.find_by!(external_id: "simplefin_tx_pending_zero_posted_1", source: "simplefin")
# For depository accounts, processor prefers posted, then transacted; posted==0 should be treated as missing
assert_equal Time.at(t_epoch).to_date, entry.date, "expected entry.date to use transacted_at when posted==0"
sf = entry.transaction.extra.fetch("simplefin")
assert_equal true, sf["pending"], "expected pending flag to be true when posted==0 and/or pending=true"
end
end

View File

@@ -0,0 +1,21 @@
require "test_helper"
class TransactionTest < ActiveSupport::TestCase
test "pending? is true when extra.simplefin.pending is truthy" do
transaction = Transaction.new(extra: { "simplefin" => { "pending" => true } })
assert transaction.pending?
end
test "pending? is true when extra.plaid.pending is truthy" do
transaction = Transaction.new(extra: { "plaid" => { "pending" => "true" } })
assert transaction.pending?
end
test "pending? is false when no provider pending metadata is present" do
transaction = Transaction.new(extra: { "plaid" => { "pending" => false } })
assert_not transaction.pending?
end
end