diff --git a/app/controllers/simplefin_items_controller.rb b/app/controllers/simplefin_items_controller.rb index 6a8675955..e3873ca1a 100644 --- a/app/controllers/simplefin_items_controller.rb +++ b/app/controllers/simplefin_items_controller.rb @@ -16,13 +16,7 @@ class SimplefinItemsController < ApplicationController def create setup_token = simplefin_params[:setup_token] - # Validate the token format first - if setup_token.blank? - @simplefin_item = Current.family.simplefin_items.build - @error_message = "Please enter a SimpleFin setup token." - render :new, status: :unprocessable_entity - return - end + return render_error("Please enter a SimpleFin setup token.") if setup_token.blank? begin @simplefin_item = Current.family.create_simplefin_item!( @@ -31,24 +25,19 @@ class SimplefinItemsController < ApplicationController ) redirect_to simplefin_items_path, notice: "SimpleFin connection added successfully! Your accounts will appear shortly as they sync in the background." - rescue ArgumentError, URI::InvalidURIError => e - @simplefin_item = Current.family.simplefin_items.build(setup_token: setup_token) - @error_message = "Invalid setup token. Please check that you copied the complete token from SimpleFin Bridge." - render :new, status: :unprocessable_entity + rescue ArgumentError, URI::InvalidURIError + render_error("Invalid setup token. Please check that you copied the complete token from SimpleFin Bridge.", setup_token) rescue Provider::Simplefin::SimplefinError => e - @simplefin_item = Current.family.simplefin_items.build(setup_token: setup_token) - case e.error_type + error_message = case e.error_type when :token_compromised - @error_message = "The setup token may be compromised, expired, or already used. Please create a new one." + "The setup token may be compromised, expired, or already used. Please create a new one." else - @error_message = "Failed to connect: #{e.message}" + "Failed to connect: #{e.message}" end - render :new, status: :unprocessable_entity + render_error(error_message, setup_token) rescue => e Rails.logger.error("SimpleFin connection error: #{e.message}") - @simplefin_item = Current.family.simplefin_items.build(setup_token: setup_token) - @error_message = "An unexpected error occurred. Please try again or contact support." - render :new, status: :unprocessable_entity + render_error("An unexpected error occurred. Please try again or contact support.", setup_token) end end @@ -73,10 +62,29 @@ class SimplefinItemsController < ApplicationController ] # 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 ] } + @subtype_options = { + "Depository" => { + label: "Account Subtype:", + options: Depository::SUBTYPES.map { |k, v| [ v[:long], k ] } + }, + "CreditCard" => { + label: "Credit Card Type:", + options: CreditCard::SUBTYPES.map { |k, v| [ v[:long], k ] } + }, + "Investment" => { + label: "Investment Type:", + options: Investment::SUBTYPES.map { |k, v| [ v[:long], k ] } + }, + "Loan" => { + label: "Loan Type:", + options: Loan::SUBTYPES.map { |k, v| [ v[:long], k ] } + }, + "OtherAsset" => { + label: nil, + options: [], + message: "No additional options needed for Other Assets." + } + } end def complete_account_setup @@ -88,7 +96,7 @@ class SimplefinItemsController < ApplicationController selected_subtype = account_subtypes[simplefin_account_id] # Create account with user-selected type and subtype - account = Account.create_from_simplefin_account_with_type_and_subtype( + account = Account.create_from_simplefin_account( simplefin_account, selected_type, selected_subtype @@ -115,4 +123,9 @@ class SimplefinItemsController < ApplicationController params.require(:simplefin_item).permit(:setup_token) end + def render_error(message, setup_token = nil) + @simplefin_item = Current.family.simplefin_items.build(setup_token: setup_token) + @error_message = message + render :new, status: :unprocessable_entity + end end diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb index eedb86c33..de809abe4 100644 --- a/app/helpers/accounts_helper.rb +++ b/app/helpers/accounts_helper.rb @@ -3,4 +3,14 @@ module AccountsHelper content = capture(&block) render "accounts/summary_card", title: title, content: content end + + def sync_path_for(account) + if account.plaid_account_id.present? + sync_plaid_item_path(account.plaid_account.plaid_item) + elsif account.simplefin_account_id.present? + sync_simplefin_item_path(account.simplefin_account.simplefin_item) + else + sync_account_path(account) + end + end end diff --git a/app/javascript/controllers/account_type_selector_controller.js b/app/javascript/controllers/account_type_selector_controller.js index db4dc39ce..a0f492075 100644 --- a/app/javascript/controllers/account_type_selector_controller.js +++ b/app/javascript/controllers/account_type_selector_controller.js @@ -5,7 +5,6 @@ export default class extends Controller { 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() } @@ -16,8 +15,6 @@ export default class extends Controller { 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 => { diff --git a/app/models/account.rb b/app/models/account.rb index ba1d3ea39..f71a95262 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -74,28 +74,14 @@ class Account < ApplicationRecord end - def create_from_simplefin_account_with_type(simplefin_account, account_type) + def create_from_simplefin_account(simplefin_account, account_type, subtype = nil) 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(simplefin_account, account_type), - simplefin_account_id: simplefin_account.id - } - - 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), + accountable_attributes: build_simplefin_accountable_attributes(simplefin_account, account_type, subtype), simplefin_account_id: simplefin_account.id } @@ -105,23 +91,11 @@ class Account < ApplicationRecord private - def build_accountable_attributes(simplefin_account, account_type) - case account_type - when "CreditCard", "Loan" - { balance: simplefin_account.current_balance } - else - {} - 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 + def build_simplefin_accountable_attributes(simplefin_account, account_type, subtype) + attributes = {} + attributes[:balance] = simplefin_account.current_balance if %w[CreditCard Loan].include?(account_type) + attributes[:subtype] = subtype if subtype.present? + attributes end end diff --git a/app/models/provider/simplefin.rb b/app/models/provider/simplefin.rb index 78de36cc8..e64a19baa 100644 --- a/app/models/provider/simplefin.rb +++ b/app/models/provider/simplefin.rb @@ -2,22 +2,16 @@ class Provider::Simplefin include HTTParty headers "User-Agent" => "Sure Finance SimpleFin Client" + default_options.merge!(verify: true, ssl_verify_mode: :peer) def initialize - self.class.default_options.merge!(verify: true, ssl_verify_mode: :peer) end def claim_access_url(setup_token) # Decode the base64 setup token to get the claim URL claim_url = Base64.decode64(setup_token) - response = HTTParty.post(claim_url, { - headers: { - "User-Agent" => "Sure Finance SimpleFin Client" - }, - verify: true, - ssl_verify_mode: :peer - }) + response = HTTParty.post(claim_url) case response.code when 200 @@ -41,13 +35,7 @@ class Provider::Simplefin accounts_url += "?#{URI.encode_www_form(query_params)}" unless query_params.empty? # The access URL already contains HTTP Basic Auth credentials - response = HTTParty.get(accounts_url, { - headers: { - "User-Agent" => "Sure Finance SimpleFin Client" - }, - verify: true, - ssl_verify_mode: :peer - }) + response = HTTParty.get(accounts_url) case response.code when 200 @@ -62,13 +50,7 @@ class Provider::Simplefin end def get_info(base_url) - response = HTTParty.get("#{base_url}/info", { - headers: { - "User-Agent" => "Sure Finance SimpleFin Client" - }, - verify: true, - ssl_verify_mode: :peer - }) + response = HTTParty.get("#{base_url}/info") case response.code when 200 diff --git a/app/models/simplefin_account.rb b/app/models/simplefin_account.rb index a123ed9a8..56af534f2 100644 --- a/app/models/simplefin_account.rb +++ b/app/models/simplefin_account.rb @@ -55,7 +55,12 @@ class SimplefinAccount < ApplicationRecord if currency_value.start_with?("http") # For custom currency URLs, we'll just use the last part as currency code # This is a simplification - in production you might want to fetch the currency info - URI.parse(currency_value).path.split("/").last.upcase rescue "USD" + begin + URI.parse(currency_value).path.split("/").last.upcase + rescue URI::InvalidURIError => e + Rails.logger.warn("Invalid currency URI for SimpleFin account: #{currency_value}, error: #{e.message}") + "USD" + end else currency_value.upcase end diff --git a/app/views/accounts/show/_header.html.erb b/app/views/accounts/show/_header.html.erb index e34236e64..6d151059a 100644 --- a/app/views/accounts/show/_header.html.erb +++ b/app/views/accounts/show/_header.html.erb @@ -32,9 +32,7 @@ "refresh-cw", as_button: true, size: "sm", - href: account.plaid_account_id.present? ? sync_plaid_item_path(account.plaid_account.plaid_item) : - account.simplefin_account_id.present? ? sync_simplefin_item_path(account.simplefin_account.simplefin_item) : - sync_account_path(account), + href: sync_path_for(account), disabled: account.syncing?, frame: :_top ) %> diff --git a/app/views/simplefin_items/_subtype_select.html.erb b/app/views/simplefin_items/_subtype_select.html.erb new file mode 100644 index 000000000..47fa52949 --- /dev/null +++ b/app/views/simplefin_items/_subtype_select.html.erb @@ -0,0 +1,14 @@ +
diff --git a/app/views/simplefin_items/setup_accounts.html.erb b/app/views/simplefin_items/setup_accounts.html.erb index cebceb861..04e388be8 100644 --- a/app/views/simplefin_items/setup_accounts.html.erb +++ b/app/views/simplefin_items/setup_accounts.html.erb @@ -59,44 +59,9 @@