mirror of
https://github.com/we-promise/sure.git
synced 2026-08-05 00:22:17 +00:00
* add redbark provider integration - per family api key provider, built like the lunchflow integration - syncs accounts, balances and transactions from api.redbark.com - account setup flow, settings panel, locales and routes - tests and fixtures * harden redbark integration based on prior provider pr feedback - use DebugLogEntry.capture for sync/import/unlink failures - retry 429s and 5xxs with backoff, raise on page cap instead of truncating - keep raw response bodies out of logs and errors - not null constraints on account columns, migration base 7.2 - persist ignored flag for skipped accounts so they stop nagging setup - validate api key on every save, re-arm status on key rotation - destroy aborts if unlink fails, atomic account create and link - require_admin on mutating actions, see_other on error redirects - single grouped query for item account counts - i18n default connection name, blank password field value - controller and provider tests * fix issues found in second review sweep - add missing syncable scope, without it every family sync raises - kick off a sync on connection create and on key rotation - setup dialog fetches accounts inline for fresh connections and shows api errors - skip balance write when no balance has been fetched yet, never anchor a false zero - exclude stale and non banking accounts from the batched balances call, per account fallback if the batch is rejected - detect the server row ceiling and empty pages instead of silently truncating history - user sync start date only governs the initial backfill, incremental after that - fetch connections before the per account loop so auth errors propagate once - drop untemplated index/show/new/edit routes and dead preload/link_accounts actions - stable dom id on the settings panel so repeat turbo replaces keep working * skip brokerage connections, found in live testing - the transactions endpoint 400s for brokerage connections, they belong to /v1/trades - only import accounts from banking and documents connections - guard transaction fetches for any legacy linked non banking account * address review feedback - treat the truncation header as a pagination signal: split the date window and refetch instead of failing the account - prune stale pending rows from the snapshot so settled pendings cant come back as duplicates - block linking a sure account that already has another provider feed - count setup failures separately from skips and surface an error instead of "all skipped" - add not nulls on redbark_items name and api key - enqueue the destroy job after the flag commits, not inside the transaction - swap bg-gray-400 for bg-surface-inset, drop amounts from info logs, remove i18n default fallbacks - tests for window splitting, pending pruning and encrypted payload round trip * fix issues from convention review - benign skips (unlinked account, blank id, unparseable rows) no longer count as failures, tracked separately so a clean batch reports success - currency parsing goes through extract_currency so hash shaped payloads resolve instead of falling to the default - merchant ids use truncated sha256 instead of md5 - debug log entries for import failures and account sync scheduling failures * bound the raw transactions snapshot to the fetch window - trim raw_transactions_payload to the current fetch window on merge, same as brex - keep rows without a parseable date, drop settled pendings as before - surface skipped rows in the aggregate debug log entry with imported/skipped counts
105 lines
3.3 KiB
Ruby
105 lines
3.3 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class RedbarkAccount < ApplicationRecord
|
|
include CurrencyNormalizable, Encryptable
|
|
include RedbarkAccount::DataHelpers
|
|
|
|
# Encrypt raw payloads if ActiveRecord encryption is configured
|
|
if encryption_ready?
|
|
encrypts :raw_payload
|
|
encrypts :raw_transactions_payload
|
|
end
|
|
|
|
belongs_to :redbark_item
|
|
|
|
# Association through account_providers
|
|
has_one :account_provider, as: :provider, dependent: :destroy
|
|
has_one :account, through: :account_provider, source: :account
|
|
has_one :linked_account, through: :account_provider, source: :account
|
|
|
|
validates :name, :currency, :redbark_account_id, presence: true
|
|
validates :redbark_account_id, uniqueness: { scope: :redbark_item_id }
|
|
|
|
# Scopes
|
|
scope :with_linked, -> { joins(:account_provider) }
|
|
scope :without_linked, -> { left_joins(:account_provider).where(account_providers: { id: nil }) }
|
|
scope :needs_setup, -> { without_linked.where(ignored: false) }
|
|
scope :ordered, -> { order(created_at: :desc) }
|
|
|
|
# Callbacks
|
|
after_destroy :enqueue_connection_cleanup
|
|
|
|
# Helper to get account using account_providers system
|
|
def current_account
|
|
account
|
|
end
|
|
|
|
# Idempotently create or update AccountProvider link
|
|
# CRITICAL: After creation, reload association to avoid stale nil
|
|
def ensure_account_provider!(linked_account)
|
|
return nil unless linked_account
|
|
|
|
provider = account_provider || build_account_provider
|
|
provider.account = linked_account
|
|
provider.save!
|
|
|
|
# Reload to clear cached nil value
|
|
reload_account_provider
|
|
account_provider
|
|
end
|
|
|
|
# Redbark accounts endpoint returns:
|
|
# { id, connectionId, provider, name, type, institutionName, accountNumber, currency }
|
|
# Balance is not included there - it comes from the balances endpoint and is
|
|
# written separately by the importer, so it is deliberately not touched here.
|
|
def upsert_from_redbark!(account_data, connection_data: nil)
|
|
data = sdk_object_to_hash(account_data).with_indifferent_access
|
|
connection = connection_data.present? ? sdk_object_to_hash(connection_data).with_indifferent_access : {}
|
|
|
|
display_name = if data[:institutionName].present?
|
|
"#{data[:institutionName]} - #{data[:name]}"
|
|
else
|
|
data[:name]
|
|
end
|
|
|
|
update!(
|
|
redbark_account_id: data[:id]&.to_s,
|
|
connection_id: data[:connectionId]&.to_s,
|
|
name: display_name,
|
|
account_number: data[:accountNumber],
|
|
currency: extract_currency(data, fallback: parse_currency(currency) || "AUD"),
|
|
account_status: connection[:status],
|
|
account_type: data[:type],
|
|
provider: data[:provider],
|
|
institution_metadata: {
|
|
name: data[:institutionName] || connection[:institutionName],
|
|
logo: connection[:institutionLogo]
|
|
}.compact,
|
|
raw_payload: account_data
|
|
)
|
|
end
|
|
|
|
def upsert_redbark_transactions_snapshot!(transactions_snapshot)
|
|
assign_attributes(
|
|
raw_transactions_payload: transactions_snapshot
|
|
)
|
|
|
|
save!
|
|
end
|
|
|
|
private
|
|
|
|
def enqueue_connection_cleanup
|
|
return unless redbark_item
|
|
|
|
RedbarkConnectionCleanupJob.perform_later(
|
|
redbark_item_id: redbark_item.id,
|
|
account_id: id
|
|
)
|
|
end
|
|
|
|
def log_invalid_currency(currency_value)
|
|
Rails.logger.warn("Invalid currency code '#{currency_value}' for Redbark account #{id}, defaulting to AUD")
|
|
end
|
|
end
|