From ddab962d76c3311ddfb903dff63324ca10a7a463 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Nov 2025 21:27:32 +0000 Subject: [PATCH] Fetch transaction counts during provider setup Add transaction count validation for all banking providers (SimpleFIN, Lunch Flow, and Enable Banking) during the account setup process. This change fetches transaction data for each bank account immediately after provider credentials are configured, allowing users to see warnings about accounts with no transaction history before completing the setup. Key changes: - SimpleFIN: Fetch accounts and check transaction counts after token setup - Lunch Flow: Check transaction availability after API key configuration - Enable Banking: Validate transaction data after OAuth authorization - Display warning messages in provider panels when issues are detected - Warnings show accounts with 0 transactions in the last 90 days The warnings appear in the /settings/providers screen before the "Configured and ready to use" message, giving users early visibility into potential data availability issues. --- .../enable_banking_items_controller.rb | 55 +++++++++++++++++++ app/controllers/lunchflow_items_controller.rb | 53 +++++++++++++++++- app/controllers/simplefin_items_controller.rb | 44 ++++++++++++++- .../providers/_lunchflow_panel.html.erb | 13 +++++ .../providers/_simplefin_panel.html.erb | 12 ++++ 5 files changed, 175 insertions(+), 2 deletions(-) diff --git a/app/controllers/enable_banking_items_controller.rb b/app/controllers/enable_banking_items_controller.rb index 95fc35559..a61e4b478 100644 --- a/app/controllers/enable_banking_items_controller.rb +++ b/app/controllers/enable_banking_items_controller.rb @@ -190,9 +190,17 @@ class EnableBankingItemsController < ApplicationController begin enable_banking_item.complete_authorization(code: code) + # Fetch transaction counts for validation + transaction_warnings = fetch_transaction_counts(enable_banking_item) + # Trigger sync to process accounts enable_banking_item.sync_later + if transaction_warnings.any? + # Store warnings in flash for display on accounts page + flash[:warning] = "Connected successfully, but some issues were found: #{transaction_warnings.join('; ')}" + end + redirect_to accounts_path, notice: t(".success", default: "Successfully connected to your bank. Your accounts are being synced.") rescue Provider::EnableBanking::EnableBankingError => e Rails.logger.error "Enable Banking session creation error: #{e.message}" @@ -419,6 +427,53 @@ class EnableBankingItemsController < ApplicationController ) end + # Fetch transaction counts for all accounts in the Enable Banking item + # Returns an array of warning messages if any accounts have issues + def fetch_transaction_counts(enable_banking_item) + warnings = [] + + begin + provider = enable_banking_item.enable_banking_provider + return warnings unless provider + + accounts = enable_banking_item.enable_banking_accounts + + if accounts.empty? + warnings << "No bank accounts found after authorization." + else + # Check transaction counts for each account (last 90 days) + accounts.each do |enable_banking_account| + account_name = enable_banking_account.name || "Unknown Account" + account_uid = enable_banking_account.uid + + begin + transactions_data = provider.get_account_transactions( + account_id: account_uid, + date_from: 90.days.ago, + date_to: Date.today + ) + transactions = transactions_data[:transactions] || [] + + if transactions.empty? + warnings << "Account '#{account_name}' has 0 transactions available in the last 90 days." + end + rescue Provider::EnableBanking::EnableBankingError => e + Rails.logger.warn("Enable Banking transaction count check failed for account #{account_uid}: #{e.message}") + warnings << "Unable to fetch transactions for '#{account_name}': #{e.message}" + end + end + end + rescue Provider::EnableBanking::EnableBankingError => e + Rails.logger.warn("Enable Banking accounts fetch failed: #{e.message}") + warnings << "Unable to fetch account information: #{e.message}" + rescue => e + Rails.logger.warn("Unexpected error checking Enable Banking transactions: #{e.message}") + warnings << "Unable to verify transaction availability." + end + + warnings + end + # Generate the callback URL for Enable Banking OAuth # In production, uses the standard Rails route # In development, uses DEV_WEBHOOKS_URL if set (e.g., ngrok URL) diff --git a/app/controllers/lunchflow_items_controller.rb b/app/controllers/lunchflow_items_controller.rb index c16cd20ef..705bb0666 100644 --- a/app/controllers/lunchflow_items_controller.rb +++ b/app/controllers/lunchflow_items_controller.rb @@ -411,6 +411,9 @@ class LunchflowItemsController < ApplicationController # Trigger initial sync to fetch accounts @lunchflow_item.sync_later + # Fetch transaction counts for validation + @transaction_warnings = fetch_transaction_counts(@lunchflow_item) + if turbo_frame_request? flash.now[:notice] = t(".success") @lunchflow_items = Current.family.lunchflow_items.ordered @@ -418,7 +421,7 @@ class LunchflowItemsController < ApplicationController turbo_stream.replace( "lunchflow-providers-panel", partial: "settings/providers/lunchflow_panel", - locals: { lunchflow_items: @lunchflow_items } + locals: { lunchflow_items: @lunchflow_items, transaction_warnings: @transaction_warnings } ), *flash_notification_stream_items ] @@ -737,6 +740,54 @@ class LunchflowItemsController < ApplicationController params.require(:lunchflow_item).permit(:name, :sync_start_date, :api_key, :base_url) end + # Fetch transaction counts for all accounts in the Lunchflow item + # Returns an array of warning messages if any accounts have issues + def fetch_transaction_counts(lunchflow_item) + warnings = [] + + begin + provider = lunchflow_item.lunchflow_provider + return warnings unless provider + + accounts_data = provider.get_accounts + accounts = accounts_data[:accounts] || [] + + if accounts.empty? + warnings << "No bank accounts found. Please check your Lunch Flow configuration." + else + # Check transaction counts for each account (last 90 days) + accounts.each do |account_data| + account_name = account_data[:name] || "Unknown Account" + account_id = account_data[:id] + + begin + transactions_data = provider.get_account_transactions( + account_id, + start_date: 90.days.ago, + end_date: Date.today + ) + transactions = transactions_data[:transactions] || [] + + if transactions.empty? + warnings << "Account '#{account_name}' has 0 transactions available in the last 90 days." + end + rescue Provider::Lunchflow::LunchflowError => e + Rails.logger.warn("Lunchflow transaction count check failed for account #{account_id}: #{e.message}") + warnings << "Unable to fetch transactions for '#{account_name}': #{e.message}" + end + end + end + rescue Provider::Lunchflow::LunchflowError => e + Rails.logger.warn("Lunchflow accounts fetch failed: #{e.message}") + warnings << "Unable to fetch account information: #{e.message}" + rescue => e + Rails.logger.warn("Unexpected error checking Lunchflow transactions: #{e.message}") + warnings << "Unable to verify transaction availability." + end + + warnings + end + # Sanitize return_to parameter to prevent XSS attacks # Only allow internal paths, reject external URLs and javascript: URIs def safe_return_to_path diff --git a/app/controllers/simplefin_items_controller.rb b/app/controllers/simplefin_items_controller.rb index dd5c5eea4..ad5ba38ad 100644 --- a/app/controllers/simplefin_items_controller.rb +++ b/app/controllers/simplefin_items_controller.rb @@ -92,6 +92,9 @@ class SimplefinItemsController < ApplicationController item_name: "SimpleFIN Connection" ) + # Fetch transaction counts for validation + @transaction_warnings = fetch_transaction_counts(@simplefin_item) + if turbo_frame_request? flash.now[:notice] = t(".success") @simplefin_items = Current.family.simplefin_items.ordered @@ -99,7 +102,7 @@ class SimplefinItemsController < ApplicationController turbo_stream.replace( "simplefin-providers-panel", partial: "settings/providers/simplefin_panel", - locals: { simplefin_items: @simplefin_items } + locals: { simplefin_items: @simplefin_items, transaction_warnings: @transaction_warnings } ), *flash_notification_stream_items ] @@ -439,6 +442,45 @@ class SimplefinItemsController < ApplicationController s.gsub(NAME_NORM_RE, " ") end + # Fetch transaction counts for all accounts in the SimpleFIN item + # Returns an array of warning messages if any accounts have issues + def fetch_transaction_counts(simplefin_item) + warnings = [] + + begin + # Fetch accounts with a reasonable date range (last 90 days) + provider = simplefin_item.simplefin_provider + accounts_data = provider.get_accounts( + simplefin_item.access_url, + start_date: 90.days.ago, + end_date: Date.today + ) + + accounts = accounts_data[:accounts] || [] + + if accounts.empty? + warnings << "No bank accounts found. Please check your SimpleFIN Bridge setup." + else + accounts.each do |account_data| + account_name = account_data[:name] || "Unknown Account" + transactions = account_data[:transactions] || [] + + if transactions.empty? + warnings << "Account '#{account_name}' has 0 transactions available in the last 90 days." + end + end + end + rescue Provider::Simplefin::SimplefinError => e + Rails.logger.warn("SimpleFin transaction count check failed: #{e.message}") + warnings << "Unable to fetch transaction information: #{e.message}" + rescue => e + Rails.logger.warn("Unexpected error checking SimpleFin transactions: #{e.message}") + warnings << "Unable to verify transaction availability." + end + + warnings + end + def compute_relink_candidates # Best-effort dedup before building candidates @simplefin_item.dedup_simplefin_accounts! rescue nil diff --git a/app/views/settings/providers/_lunchflow_panel.html.erb b/app/views/settings/providers/_lunchflow_panel.html.erb index 35d24c53d..28e9290aa 100644 --- a/app/views/settings/providers/_lunchflow_panel.html.erb +++ b/app/views/settings/providers/_lunchflow_panel.html.erb @@ -53,6 +53,19 @@ <% end %> <% items = local_assigns[:lunchflow_items] || @lunchflow_items || Current.family.lunchflow_items.where.not(api_key: [nil, ""]) %> + + <% transaction_warnings = local_assigns[:transaction_warnings] %> + <% if transaction_warnings&.any? %> +
+

Transaction Data Warnings:

+ +
+ <% end %> +
<% if items&.any? %>
diff --git a/app/views/settings/providers/_simplefin_panel.html.erb b/app/views/settings/providers/_simplefin_panel.html.erb index 1e50a611c..ba0403894 100644 --- a/app/views/settings/providers/_simplefin_panel.html.erb +++ b/app/views/settings/providers/_simplefin_panel.html.erb @@ -36,6 +36,18 @@
<% end %> + <% transaction_warnings = local_assigns[:transaction_warnings] %> + <% if transaction_warnings&.any? %> +
+

Transaction Data Warnings:

+ +
+ <% end %> +
<% if @simplefin_items&.any? %>