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? %>