mirror of
https://github.com/we-promise/sure.git
synced 2026-04-20 04:24:06 +00:00
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:
@@ -15,7 +15,12 @@ class PlaidEntry::Processor
|
||||
name: name,
|
||||
source: "plaid",
|
||||
category_id: matched_category&.id,
|
||||
merchant: merchant
|
||||
merchant: merchant,
|
||||
extra: {
|
||||
plaid: {
|
||||
pending: plaid_transaction["pending"]
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
class Provider::Simplefin
|
||||
# Pending: some institutions do not return pending transactions even with `pending=1`.
|
||||
# This is provider variability (not a bug). For troubleshooting, you can set
|
||||
# `SIMPLEFIN_INCLUDE_PENDING=1` and/or `SIMPLEFIN_DEBUG_RAW=1` (both default-off).
|
||||
# These are centralized in `Rails.configuration.x.simplefin.*` via
|
||||
# `config/initializers/simplefin.rb`.
|
||||
include HTTParty
|
||||
|
||||
headers "User-Agent" => "Sure Finance SimpleFin Client"
|
||||
|
||||
@@ -34,6 +34,24 @@ class SimplefinEntry::Processor
|
||||
# Include provider-supplied extra hash if present
|
||||
sf["extra"] = data[:extra] if data[:extra].is_a?(Hash)
|
||||
|
||||
# Pending detection: honor provider flag or infer from missing/zero posted with present transacted_at
|
||||
posted_val = data[:posted]
|
||||
posted_missing = posted_val.blank? || posted_val == 0 || posted_val == "0"
|
||||
if ActiveModel::Type::Boolean.new.cast(data[:pending]) || (posted_missing && data[:transacted_at].present?)
|
||||
sf["pending"] = true
|
||||
Rails.logger.debug("SimpleFIN: flagged pending transaction #{external_id}")
|
||||
end
|
||||
|
||||
# FX metadata: when tx currency differs from account currency
|
||||
tx_currency = parse_currency(data[:currency])
|
||||
acct_currency = account.currency
|
||||
if tx_currency.present? && acct_currency.present? && tx_currency != acct_currency
|
||||
sf["fx_from"] = tx_currency
|
||||
# Prefer transacted_at for fx date, fallback to posted
|
||||
fx_d = transacted_date || posted_date
|
||||
sf["fx_date"] = fx_d&.to_s
|
||||
end
|
||||
|
||||
return nil if sf.empty?
|
||||
{ "simplefin" => sf }
|
||||
end
|
||||
@@ -124,6 +142,8 @@ class SimplefinEntry::Processor
|
||||
|
||||
def posted_date
|
||||
val = data[:posted]
|
||||
# Treat 0 / "0" as missing to avoid Unix epoch 1970-01-01 for pendings
|
||||
return nil if val == 0 || val == "0"
|
||||
Simplefin::DateUtils.parse_provider_date(val)
|
||||
end
|
||||
|
||||
|
||||
@@ -403,6 +403,10 @@ class SimplefinItem::Importer
|
||||
# Returns a Hash payload with keys like :accounts, or nil when an error is
|
||||
# handled internally via `handle_errors`.
|
||||
def fetch_accounts_data(start_date:, end_date: nil, pending: nil)
|
||||
# Determine whether to include pending based on explicit arg or global config.
|
||||
# `Rails.configuration.x.simplefin.include_pending` is ENV-backed.
|
||||
effective_pending = pending.nil? ? Rails.configuration.x.simplefin.include_pending : pending
|
||||
|
||||
# Debug logging to track exactly what's being sent to SimpleFin API
|
||||
start_str = start_date.respond_to?(:strftime) ? start_date.strftime("%Y-%m-%d") : "none"
|
||||
end_str = end_date.respond_to?(:strftime) ? end_date.strftime("%Y-%m-%d") : "current"
|
||||
@@ -411,7 +415,7 @@ class SimplefinItem::Importer
|
||||
else
|
||||
"unknown"
|
||||
end
|
||||
Rails.logger.info "SimplefinItem::Importer - API Request: #{start_str} to #{end_str} (#{days_requested} days)"
|
||||
Rails.logger.info "SimplefinItem::Importer - API Request: #{start_str} to #{end_str} (#{days_requested} days) pending=#{effective_pending ? 1 : 0}"
|
||||
|
||||
begin
|
||||
# Track API request count for quota awareness
|
||||
@@ -420,7 +424,7 @@ class SimplefinItem::Importer
|
||||
simplefin_item.access_url,
|
||||
start_date: start_date,
|
||||
end_date: end_date,
|
||||
pending: pending
|
||||
pending: effective_pending
|
||||
)
|
||||
# Soft warning when approaching SimpleFin daily refresh guidance
|
||||
if stats["api_requests"].to_i >= 20
|
||||
@@ -436,6 +440,11 @@ class SimplefinItem::Importer
|
||||
end
|
||||
end
|
||||
|
||||
# Optional raw payload debug logging (guarded by ENV to avoid spam)
|
||||
if Rails.configuration.x.simplefin.debug_raw
|
||||
Rails.logger.debug("SimpleFIN raw: #{accounts_data.inspect}")
|
||||
end
|
||||
|
||||
# Handle errors if present in response
|
||||
if accounts_data[:errors] && accounts_data[:errors].any?
|
||||
if accounts_data[:accounts].to_a.any?
|
||||
|
||||
@@ -31,4 +31,12 @@ class Transaction < ApplicationRecord
|
||||
|
||||
update!(category: category)
|
||||
end
|
||||
|
||||
def pending?
|
||||
extra_data = extra.is_a?(Hash) ? extra : {}
|
||||
ActiveModel::Type::Boolean.new.cast(extra_data.dig("simplefin", "pending")) ||
|
||||
ActiveModel::Type::Boolean.new.cast(extra_data.dig("plaid", "pending"))
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user