mirror of
https://github.com/we-promise/sure.git
synced 2026-08-03 23:52:13 +00:00
feat(sync): add toggle to parse Enable Banking CC balance as available credit (#2512)
* feat(sync): allow EnableBanking credit card balances to be parsed as available credit * feat(ui): expose Enable Banking balance interpretation toggle on credit card form Adds a toggle to the credit card edit dialog, shown only when the account is linked to an Enable Banking provider account. Toggling persists treat_balance_as_available_credit and enqueues an item sync so the balance is reinterpreted immediately. * fix(sync): keep existing balance when credit limit is missing for available-credit cards When treat_balance_as_available_credit is on and the API omits credit_limit, the reported balance is known to be available credit, so recording it as debt fabricates a liability. Skip the balance write in that case and only update the available credit metadata. Also extract the credit card branching into a helper and include provider_key, account_provider and metadata in the debug log entries so support can filter /settings/debug by the affected connection. * refactor(ui): move provider lookup to Account and label the toggle for assistive tech Adds Account#provider_account_for so the view no longer queries account_providers directly, and wires aria-labelledby from the toggle to its visible label. * fix(ui): apply Enable Banking setting only after a successful account update Runs the provider flag update after super and only on the redirect path, so a failed account save no longer persists the flag or enqueues a sync. Also permits the enable_banking params so malformed input is filtered instead of raising. * test(sync): extract relink helper in Enable Banking processor test * feat(sync): fall back to manually set available credit as the credit limit When the toggle is on, the accountable's available_credit field now holds the credit limit (API-provided, or user-entered when the API omits it) and is never overwritten with the reported balance. This lets users whose bank reports available credit without a credit limit compute the outstanding debt by entering their limit in the Available credit field, while keeping the field stable across syncs so a stale reported balance can never be mistaken for a limit. --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com> Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
This commit is contained in:
@@ -55,47 +55,140 @@ class EnableBankingAccount::ProcessorTest < ActiveSupport::TestCase
|
||||
|
||||
test "sets CC balance as absolute debt and tracks available_credit when limit is present" do
|
||||
cc_account = accounts(:credit_card)
|
||||
|
||||
@enable_banking_account.update!(
|
||||
current_balance: 450.00,
|
||||
credit_limit: 1000.00
|
||||
current_balance: 900.00,
|
||||
credit_limit: 1000.00,
|
||||
treat_balance_as_available_credit: true
|
||||
)
|
||||
AccountProvider.find_by(provider: @enable_banking_account)&.destroy
|
||||
AccountProvider.create!(account: cc_account, provider: @enable_banking_account)
|
||||
relink_provider_to(cc_account)
|
||||
|
||||
EnableBankingAccount::Processor.new(@enable_banking_account).process
|
||||
|
||||
assert_equal 450.0, cc_account.reload.cash_balance
|
||||
assert_equal 100.0, cc_account.reload.cash_balance
|
||||
if cc_account.accountable.respond_to?(:available_credit)
|
||||
assert_equal 550.0, cc_account.accountable.reload.available_credit
|
||||
assert_equal 1000.0, cc_account.accountable.reload.available_credit
|
||||
end
|
||||
end
|
||||
|
||||
test "sets CC balance as absolute debt and keeps stored available_credit when limit absent" do
|
||||
test "when treat_balance_as_available_credit is true and card is overpaid, floors debt at zero" do
|
||||
cc_account = accounts(:credit_card)
|
||||
cc_account.accountable.update!(available_credit: 1000.0)
|
||||
|
||||
@enable_banking_account.update!(current_balance: 300.00, credit_limit: nil)
|
||||
|
||||
AccountProvider.find_by(provider: @enable_banking_account)&.destroy
|
||||
AccountProvider.create!(account: cc_account, provider: @enable_banking_account)
|
||||
@enable_banking_account.update!(
|
||||
current_balance: 1050.00, # overpaid by 50
|
||||
credit_limit: 1000.00,
|
||||
treat_balance_as_available_credit: true
|
||||
)
|
||||
relink_provider_to(cc_account)
|
||||
|
||||
EnableBankingAccount::Processor.new(@enable_banking_account).process
|
||||
|
||||
assert_equal 300.0, cc_account.reload.cash_balance
|
||||
assert_equal 0.0, cc_account.reload.cash_balance
|
||||
if cc_account.accountable.respond_to?(:available_credit)
|
||||
assert_equal 1000.0, cc_account.accountable.reload.available_credit
|
||||
end
|
||||
end
|
||||
|
||||
test "when treat_balance_as_available_credit is true and API limit absent, derives debt from manually set available_credit" do
|
||||
cc_account = accounts(:credit_card)
|
||||
|
||||
cc_account.accountable.update!(available_credit: 1000.0)
|
||||
|
||||
@enable_banking_account.update!(
|
||||
current_balance: 900.00,
|
||||
credit_limit: nil,
|
||||
treat_balance_as_available_credit: true
|
||||
)
|
||||
|
||||
relink_provider_to(cc_account)
|
||||
|
||||
EnableBankingAccount::Processor.new(@enable_banking_account).process
|
||||
|
||||
# The manually configured available_credit acts as the credit limit and
|
||||
# must survive the sync unchanged.
|
||||
assert_equal 100.0, cc_account.reload.cash_balance
|
||||
assert_equal 1000.0, cc_account.accountable.reload.available_credit
|
||||
end
|
||||
|
||||
test "when treat_balance_as_available_credit is true but no limit is known, keeps existing balance" do
|
||||
cc_account = accounts(:credit_card)
|
||||
|
||||
cc_account.accountable.update!(available_credit: nil)
|
||||
|
||||
@enable_banking_account.update!(
|
||||
current_balance: 900.00,
|
||||
credit_limit: nil,
|
||||
treat_balance_as_available_credit: true
|
||||
)
|
||||
|
||||
relink_provider_to(cc_account)
|
||||
|
||||
balance_before = cc_account.cash_balance
|
||||
|
||||
EnableBankingAccount::Processor.new(@enable_banking_account).process
|
||||
|
||||
# The reported balance is available credit and there's no limit to reverse
|
||||
# from, so the debt is unknown. The existing balance must not be overwritten,
|
||||
# and available_credit must stay blank so a later sync can't mistake a
|
||||
# stale reported balance for a credit limit.
|
||||
assert_equal balance_before, cc_account.reload.cash_balance
|
||||
assert_nil cc_account.accountable.reload.available_credit
|
||||
end
|
||||
|
||||
test "when treat_balance_as_available_credit is false, treats balance as absolute debt natively" do
|
||||
cc_account = accounts(:credit_card)
|
||||
|
||||
# API sends current_balance as debt (e.g. 100) and credit limit (e.g. 1000)
|
||||
# Debt should remain 100, available credit becomes 900
|
||||
@enable_banking_account.update!(
|
||||
current_balance: 100.00,
|
||||
credit_limit: 1000.00,
|
||||
treat_balance_as_available_credit: false
|
||||
)
|
||||
relink_provider_to(cc_account)
|
||||
|
||||
EnableBankingAccount::Processor.new(@enable_banking_account).process
|
||||
|
||||
assert_equal 100.0, cc_account.reload.cash_balance
|
||||
if cc_account.accountable.respond_to?(:available_credit)
|
||||
assert_equal 900.0, cc_account.accountable.reload.available_credit
|
||||
end
|
||||
end
|
||||
|
||||
test "sets CC balance to absolute debt when both limit and stored available_credit are absent" do
|
||||
cc_account = accounts(:credit_card)
|
||||
cc_account.accountable.update!(available_credit: nil)
|
||||
|
||||
@enable_banking_account.update!(current_balance: 300.00, credit_limit: nil)
|
||||
|
||||
AccountProvider.find_by(provider: @enable_banking_account)&.destroy
|
||||
AccountProvider.create!(account: cc_account, provider: @enable_banking_account)
|
||||
relink_provider_to(cc_account)
|
||||
|
||||
EnableBankingAccount::Processor.new(@enable_banking_account).process
|
||||
|
||||
assert_equal 300.0, cc_account.reload.cash_balance
|
||||
end
|
||||
|
||||
test "treat_balance_as_available_credit flag is a no-op on Loan accounts" do
|
||||
loan_account = accounts(:loan)
|
||||
|
||||
# Even with the flag set to true, loans should only ever process as absolute debt
|
||||
@enable_banking_account.update!(
|
||||
current_balance: 50000.00,
|
||||
credit_limit: 100000.00,
|
||||
treat_balance_as_available_credit: true
|
||||
)
|
||||
|
||||
relink_provider_to(loan_account)
|
||||
|
||||
EnableBankingAccount::Processor.new(@enable_banking_account).process
|
||||
|
||||
# Balance should match the absolute incoming balance, no credit limit math applied
|
||||
assert_equal 50000.0, loan_account.reload.cash_balance
|
||||
end
|
||||
|
||||
private
|
||||
def relink_provider_to(account)
|
||||
AccountProvider.find_by(provider: @enable_banking_account)&.destroy
|
||||
AccountProvider.create!(account: account, provider: @enable_banking_account)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user