Investment Tax Treatment Classification#
Overview#
Sure assigns a tax_treatment value to every investment and crypto account subtype at the model layer. These classifications drive the investment gains by tax treatment card on the Reports page and the budget/cashflow exclusion logic that keeps retirement contributions out of spending reports.
There are four treatment values :
| Value | Meaning |
|---|---|
:taxable | Gains are taxed when realized (default) |
:tax_deferred | Taxes owed but deferred until withdrawal (e.g., 401k, RRSP) |
:tax_exempt | Qualified gains are tax-free (e.g., Roth IRA, ISA, TFSA) |
:tax_advantaged | Special conditions apply (e.g., 529, HSA, RESP) |
Investment Accounts — Static Subtype Map#
app/models/investment.rb defines SUBTYPES — a frozen hash of 70+ entries keyed by subtype string, each carrying short, long, region, and tax_treatment. Treatment is statically baked into the map, not stored in the database.
The tax_treatment instance method reads directly from the map — unknown/unrecognized subtypes fall back to :taxable.
Regional breakdown :
| Region | Subtypes (examples) |
|---|---|
| US | brokerage (taxable), 401k/ira/tsp/403b/457b (deferred), roth_ira/roth_401k (exempt), 529_plan/hsa (advantaged) |
| UK | isa/lisa (exempt), sipp/workplace_pension_uk (deferred) |
| Canada | tfsa/fhsa (exempt), rrsp/rrif/lif/lira and 8 more (deferred), rdsp/resp (advantaged), non-registered (taxable) |
| Australia | super/smsf (deferred) |
| Europe | assurance_vie/pea (advantaged), pillar_3a/riester (deferred) |
| India | ppf/ssy/tax_free_bond (exempt), nps/apy/life_insurance/sgb and others (advantaged), equity/bond instruments (taxable) |
| Generic | pension/retirement (deferred), mutual_fund/gold/trust/other (taxable) |
Crypto Accounts — Persisted Enum#
app/models/crypto.rb stores tax_treatment as a database-backed enum with values taxable (default), tax_deferred, and tax_exempt. Unlike Investment, the treatment on a Crypto account is mutable per-record — useful for edge cases like a self-directed IRA holding crypto .
Depository — HSA Special Case#
app/models/depository.rb adds a narrow carve-out: only the hsa subtype returns :tax_advantaged . This is required because Plaid routes depository.hsa to Depository (not Investment), so without this override, linked HSA cash accounts would be invisible to tax-advantaged filters . All other depository subtypes return nil, which the rest of the system treats as taxable.
TaxTreatable Concern — Account-Level API#
app/models/concerns/tax_treatable.rb is included on Account and delegates to the accountable :
tax_treatment— returns the symbol (:taxable,:tax_deferred, etc.) ornilif unsupportedtax_treatment_label— i18n string viaaccounts.tax_treatments.*tax_advantaged?— true for:tax_deferred,:tax_exempt, or:tax_advantagedtaxable?— true for:taxableornil
How Classification Is Used#
1. Investment Gains Report (ReportsController)#
build_gains_by_tax_treatment groups current holdings and sell trades by treatment, computing unrealized and realized gains per bucket. Unrealized gains use today's FX rates; realized gains use entry-date rates . This populates the "Gains by Tax Treatment" breakdown on the investment performance report .
2. Budget & Cashflow Exclusion (Family#tax_advantaged_account_ids)#
Family#tax_advantaged_account_ids aggregates IDs from all three accountable types and memoizes the result. It is used to exclude tax-advantaged accounts from the transactions breakdown and income statement :
- Investment: joins
investmentstable, selects subtypes whosetax_treatmentistax_deferred,tax_exempt, ortax_advantaged - Crypto: queries the
cryptos.tax_treatmentcolumn fortax_deferredortax_exempt - Depository: delegates to
tax_advantaged_depository_account_ids(covers HSA)
3. UI Display — Region-Ordered Subtype Picker#
Investment.subtypes_grouped_for_select uses CURRENCY_REGION_MAP to surface the user's likely region first (e.g., USD → "us") in account-creation dropdowns. Tax treatment is not shown in the picker but is set implicitly based on the selected subtype.
Related Topics#
- Account Type Architecture — covers the
delegated_typepolymorphic structure connectingAccounttoInvestment,Crypto,Depository, etc. - Investment Activity Labels — covers transaction-level classification (trade kinds, activity types).