Account Lifecycle Management#
Sure manages account state through an AASM-backed state machine on the Account model . There are four states:
| State | Meaning |
|---|---|
active | Default initial state; fully usable in reports and UI |
draft | Created but not yet activated (e.g., during import) |
disabled | Hidden from active views but preserved in history |
pending_deletion | Queued for background destruction |
State Transitions#
The allowed transitions are:
activate—draft/disabled→activedisable—draft/active→disabledenable—disabled→activemark_for_deletion— any state →pending_deletion
In the controller, toggle_active calls disable! on an active account or enable! on a disabled one .
Scopes and Visibility#
Two named constant arrays drive query scopes :
VISIBLE_STATUSES—[draft, active]— used by thevisiblescope; these accounts appear in the UI.HISTORICAL_STATUSES—[draft, active, disabled]— used by thehistoricalscope; includes disabled accounts for balance history and reporting.pending_deletionaccounts are excluded fromlistable_manualso they don't appear on the manual account list.- The
included_in_reportsscope further filters byexclude_from_reports: false.
Deletion Flow#
Deletion is always asynchronous. Calling destroy_later atomically transitions the account to pending_deletion and enqueues a DestroyJob. If the destruction fails, the destroy override rescues the error and falls back to disable! to ensure a clean recovery state . Before destruction, cleanup_transfers destroys all Transfer records linked to the account's transactions. Linked accounts (those with a provider) cannot be deleted through the UI .
Transfer::Creator — Paired Dual-Transaction Pattern#
A Transfer in Sure is not a single ledger entry — it is a join record linking two Transaction rows: one outflow from the source account and one inflow to the destination account . This double-entry approach keeps each account's transaction history self-consistent.
How Transfer::Creator Works#
Transfer::Creator is a plain Ruby service object instantiated with family, source_account_id, destination_account_id, date, amount, and optional exchange_rate, source_fee_amount, and destination_fee_amount.
Calling #create does the following inside a single database transaction:
- Builds an outflow transaction on the source account — positive
amount(debit) . - Builds an inflow transaction on the destination account — negative
amount(credit), optionally currency-converted viainflow_converted_amount. - Appends optional fee transactions to either side .
- Saves the
Transferrecord linking both transactions, then callssync_lateron both accounts .
Transaction Kind Routing#
The outflow transaction's kind is determined by the destination account type :
| Destination account type | kind |
|---|---|
| Loan | loan_payment |
| CreditCard / other liability | cc_payment |
| Investment or Crypto (when source is not) | investment_contribution |
| Everything else | funds_movement |
The Transfer model itself also exposes kind_for_account as a class method that mirrors this routing — useful for display logic without constructing a creator.
Transfer Validations#
The Transfer model enforces :
- Different accounts — inflow and outflow must belong to different accounts.
- Opposite amounts — inflow entry must be negative, outflow positive; same-currency transfers must sum to zero.
- Same family — both accounts must belong to the same family.
- Date proximity — entries must be within 4 days of each other (pending) or 30 days (confirmed) .
Destroying a Transfer#
Transfer#destroy! resets both linked transactions' kind back to "standard" before calling super, ensuring orphaned transactions don't retain transfer-specific kinds. reject! additionally creates a RejectedTransfer record to suppress re-detection of the same pair.
Key Files#
| File | Purpose |
|---|---|
app/models/account.rb | State machine, scopes, destroy_later, cleanup_transfers |
app/controllers/accounts_controller.rb | toggle_active, destroy UI endpoints |
app/models/transfer.rb | Transfer model, validations, destroy!, reject! |
app/models/transfer/creator.rb | Service object for creating paired dual transactions |