diff --git a/app/controllers/simplefin_items_controller.rb b/app/controllers/simplefin_items_controller.rb
index 5a02eb723..9a3bf087e 100644
--- a/app/controllers/simplefin_items_controller.rb
+++ b/app/controllers/simplefin_items_controller.rb
@@ -42,7 +42,7 @@ class SimplefinItemsController < ApplicationController
# Immediately sync to get account and institution data
@simplefin_item.sync_later
- redirect_back_or_to root_path, notice: "SimpleFin connection added successfully! Your accounts will appear shortly as they sync in the background."
+ redirect_to simplefin_items_path, notice: "SimpleFin connection added successfully! Your accounts will appear shortly as they sync in the background."
else
@error_message = @simplefin_item.errors.full_messages.join(", ")
render :new, status: :unprocessable_entity
@@ -87,16 +87,28 @@ class SimplefinItemsController < ApplicationController
['Loan or Mortgage', 'Loan'],
['Other Asset', 'OtherAsset']
]
+
+ # Subtype options for each account type
+ @depository_subtypes = Depository::SUBTYPES.map { |k, v| [v[:long], k] }
+ @credit_card_subtypes = CreditCard::SUBTYPES.map { |k, v| [v[:long], k] }
+ @investment_subtypes = Investment::SUBTYPES.map { |k, v| [v[:long], k] }
+ @loan_subtypes = Loan::SUBTYPES.map { |k, v| [v[:long], k] }
end
def complete_account_setup
account_types = params[:account_types] || {}
+ account_subtypes = params[:account_subtypes] || {}
account_types.each do |simplefin_account_id, selected_type|
simplefin_account = @simplefin_item.simplefin_accounts.find(simplefin_account_id)
+ selected_subtype = account_subtypes[simplefin_account_id]
- # Create account with user-selected type
- account = Account.create_from_simplefin_account_with_type(simplefin_account, selected_type)
+ # Create account with user-selected type and subtype
+ account = Account.create_from_simplefin_account_with_type_and_subtype(
+ simplefin_account,
+ selected_type,
+ selected_subtype
+ )
simplefin_account.update!(account: account)
end
diff --git a/app/javascript/controllers/account_type_selector_controller.js b/app/javascript/controllers/account_type_selector_controller.js
new file mode 100644
index 000000000..db4dc39ce
--- /dev/null
+++ b/app/javascript/controllers/account_type_selector_controller.js
@@ -0,0 +1,43 @@
+import { Controller } from "@hotwired/stimulus"
+
+export default class extends Controller {
+ static targets = ["subtypeContainer"]
+ static values = { accountId: String }
+
+ connect() {
+ console.log('Account type selector connected for account:', this.accountIdValue)
+ // Show initial subtype dropdown based on current selection
+ this.updateSubtype()
+ }
+
+ updateSubtype(event) {
+ const selectElement = this.element.querySelector('select[name^="account_types"]')
+ const selectedType = selectElement ? selectElement.value : ''
+ const container = this.subtypeContainerTarget
+ const accountId = this.accountIdValue
+
+ console.log('Updating subtype for account:', accountId, 'Selected type:', selectedType)
+
+ // Hide all subtype selects
+ const subtypeSelects = container.querySelectorAll('.subtype-select')
+ subtypeSelects.forEach(select => {
+ select.style.display = 'none'
+ // Clear the name attribute so it doesn't get submitted
+ const selectElement = select.querySelector('select')
+ if (selectElement) {
+ selectElement.removeAttribute('name')
+ }
+ })
+
+ // Show the relevant subtype select
+ const relevantSubtype = container.querySelector(`[data-type="${selectedType}"]`)
+ if (relevantSubtype) {
+ relevantSubtype.style.display = 'block'
+ // Re-add the name attribute so it gets submitted
+ const selectElement = relevantSubtype.querySelector('select')
+ if (selectElement) {
+ selectElement.setAttribute('name', `account_subtypes[${accountId}]`)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/models/account.rb b/app/models/account.rb
index e76371586..20bbafd5f 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -27,6 +27,7 @@ class Account < ApplicationRecord
has_one_attached :logo
delegated_type :accountable, types: Accountable::TYPES, dependent: :destroy
+ delegate :subtype, to: :accountable, allow_nil: true
accepts_nested_attributes_for :accountable, update_only: true
@@ -102,6 +103,20 @@ class Account < ApplicationRecord
create_and_sync(attributes)
end
+ def create_from_simplefin_account_with_type_and_subtype(simplefin_account, account_type, subtype)
+ attributes = {
+ family: simplefin_account.simplefin_item.family,
+ name: simplefin_account.name,
+ balance: simplefin_account.current_balance || simplefin_account.available_balance || 0,
+ currency: simplefin_account.currency,
+ accountable_type: account_type,
+ accountable_attributes: build_accountable_attributes_with_subtype(simplefin_account, account_type, subtype),
+ simplefin_account_id: simplefin_account.id
+ }
+
+ create_and_sync(attributes)
+ end
+
def map_simplefin_type_to_accountable_type(simplefin_type, account_name: nil)
# First try to map by explicit type if provided
case simplefin_type&.downcase
@@ -146,6 +161,16 @@ class Account < ApplicationRecord
{}
end
end
+
+ def build_accountable_attributes_with_subtype(simplefin_account, account_type, subtype)
+ base_attributes = build_accountable_attributes(simplefin_account, account_type)
+
+ if subtype.present?
+ base_attributes[:subtype] = subtype
+ end
+
+ base_attributes
+ end
end
def institution_domain
diff --git a/app/views/simplefin_items/setup_accounts.html.erb b/app/views/simplefin_items/setup_accounts.html.erb
index 5213304ac..02ebb6d51 100644
--- a/app/views/simplefin_items/setup_accounts.html.erb
+++ b/app/views/simplefin_items/setup_accounts.html.erb
@@ -45,13 +45,60 @@
-
- <%= label_tag "account_types[#{simplefin_account.id}]", "Account Type:",
- class: "block text-sm font-medium text-primary mb-2" %>
- <%= select_tag "account_types[#{simplefin_account.id}]",
- options_for_select(@account_type_options,
- Account.map_simplefin_type_to_accountable_type(simplefin_account.account_type, account_name: simplefin_account.name)),
- { class: "w-full px-3 py-2 border border-primary rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" } %>
+
+
+ <%= label_tag "account_types[#{simplefin_account.id}]", "Account Type:",
+ class: "block text-sm font-medium text-primary mb-2" %>
+ <%= select_tag "account_types[#{simplefin_account.id}]",
+ options_for_select(@account_type_options,
+ Account.map_simplefin_type_to_accountable_type(simplefin_account.account_type, account_name: simplefin_account.name)),
+ { class: "w-full px-3 py-2 border border-primary rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500",
+ data: {
+ action: "change->account-type-selector#updateSubtype"
+ } } %>
+
+
+
+
+
+ <%= label_tag "account_subtypes[#{simplefin_account.id}]", "Account Subtype:",
+ class: "block text-sm font-medium text-primary mb-2" %>
+ <%= select_tag "account_subtypes[#{simplefin_account.id}]",
+ options_for_select([["Select subtype", ""]] + @depository_subtypes,
+ simplefin_account.name.downcase.include?("checking") ? "checking" :
+ simplefin_account.name.downcase.include?("savings") ? "savings" : ""),
+ { class: "w-full px-3 py-2 border border-primary rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" } %>
+
+
+
+ <%= label_tag "account_subtypes[#{simplefin_account.id}]", "Credit Card Type:",
+ class: "block text-sm font-medium text-primary mb-2" %>
+ <%= select_tag "account_subtypes[#{simplefin_account.id}]",
+ options_for_select([["Select type", ""]] + @credit_card_subtypes),
+ { class: "w-full px-3 py-2 border border-primary rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" } %>
+
+
+
+ <%= label_tag "account_subtypes[#{simplefin_account.id}]", "Investment Type:",
+ class: "block text-sm font-medium text-primary mb-2" %>
+ <%= select_tag "account_subtypes[#{simplefin_account.id}]",
+ options_for_select([["Select type", ""]] + @investment_subtypes),
+ { class: "w-full px-3 py-2 border border-primary rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" } %>
+
+
+
+ <%= label_tag "account_subtypes[#{simplefin_account.id}]", "Loan Type:",
+ class: "block text-sm font-medium text-primary mb-2" %>
+ <%= select_tag "account_subtypes[#{simplefin_account.id}]",
+ options_for_select([["Select type", ""]] + @loan_subtypes),
+ { class: "w-full px-3 py-2 border border-primary rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500" } %>
+
+
+
+
+
No additional options needed for Other Assets.
+
+
<% end %>
diff --git a/db/migrate/20250807170943_add_subtype_to_accountables.rb b/db/migrate/20250807170943_add_subtype_to_accountables.rb
new file mode 100644
index 000000000..854d5a79f
--- /dev/null
+++ b/db/migrate/20250807170943_add_subtype_to_accountables.rb
@@ -0,0 +1,13 @@
+class AddSubtypeToAccountables < ActiveRecord::Migration[7.2]
+ def change
+ add_column :depositories, :subtype, :string
+ add_column :investments, :subtype, :string
+ add_column :loans, :subtype, :string
+ add_column :credit_cards, :subtype, :string
+ add_column :other_assets, :subtype, :string
+ add_column :other_liabilities, :subtype, :string
+ add_column :properties, :subtype, :string
+ add_column :vehicles, :subtype, :string
+ add_column :cryptos, :subtype, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 1969c4c20..b5cc8ae4e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.2].define(version: 2025_08_07_163541) do
+ActiveRecord::Schema[7.2].define(version: 2025_08_07_170943) do
create_schema "sure_dev_schema"
# These are extensions that must be enabled in order to support this database
@@ -195,12 +195,14 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_07_163541) do
t.date "expiration_date"
t.decimal "annual_fee", precision: 10, scale: 2
t.jsonb "locked_attributes", default: {}
+ t.string "subtype"
end
create_table "cryptos", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.jsonb "locked_attributes", default: {}
+ t.string "subtype"
end
create_table "data_enrichments", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@@ -220,6 +222,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_07_163541) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.jsonb "locked_attributes", default: {}
+ t.string "subtype"
end
create_table "entries", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@@ -391,6 +394,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_07_163541) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.jsonb "locked_attributes", default: {}
+ t.string "subtype"
end
create_table "invitations", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@@ -425,6 +429,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_07_163541) do
t.integer "term_months"
t.decimal "initial_balance", precision: 19, scale: 4
t.jsonb "locked_attributes", default: {}
+ t.string "subtype"
end
create_table "merchants", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@@ -523,12 +528,14 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_07_163541) do
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.jsonb "locked_attributes", default: {}
+ t.string "subtype"
end
create_table "other_liabilities", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.jsonb "locked_attributes", default: {}
+ t.string "subtype"
end
create_table "plaid_accounts", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@@ -580,6 +587,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_07_163541) do
t.integer "area_value"
t.string "area_unit"
t.jsonb "locked_attributes", default: {}
+ t.string "subtype"
end
create_table "rejected_transfers", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t|
@@ -863,6 +871,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_07_163541) do
t.string "make"
t.string "model"
t.jsonb "locked_attributes", default: {}
+ t.string "subtype"
end
add_foreign_key "accounts", "families"