# frozen_string_literal: true module SimplefinItems module MapsHelper extend ActiveSupport::Concern # Build per-item maps consumed by the simplefin_item partial. # Accepts a single SimplefinItem or a collection. def build_simplefin_maps_for(items) items = Array(items).compact return if items.empty? @simplefin_sync_stats_map ||= {} @simplefin_has_unlinked_map ||= {} @simplefin_unlinked_count_map ||= {} @simplefin_duplicate_only_map ||= {} @simplefin_show_relink_map ||= {} # Batch-check if ANY family has manual accounts (same result for all items from same family) family_ids = items.map { |i| i.family_id }.uniq families_with_manuals = Account .visible_manual .where(family_id: family_ids) .distinct .pluck(:family_id) .to_set # Batch-fetch unlinked counts for all items in one query unlinked_counts = SimplefinAccount .where(simplefin_item_id: items.map(&:id)) .left_joins(:account, :account_provider) .where(accounts: { id: nil }, account_providers: { id: nil }) .group(:simplefin_item_id) .count items.each do |item| # Latest sync stats (avoid N+1; rely on includes(:syncs) where appropriate) latest_sync = if item.syncs.loaded? item.syncs.max_by(&:created_at) else item.syncs.ordered.first end stats = (latest_sync&.sync_stats || {}) @simplefin_sync_stats_map[item.id] = stats # Whether the family has any manual accounts available to link (from batch query) @simplefin_has_unlinked_map[item.id] = families_with_manuals.include?(item.family_id) # Count from batch query (defaults to 0 if not found) @simplefin_unlinked_count_map[item.id] = unlinked_counts[item.id] || 0 # Whether all reported errors for this item are duplicate-account warnings @simplefin_duplicate_only_map[item.id] = compute_duplicate_only_flag(stats) # Compute CTA visibility: show relink only when there are zero unlinked SFAs, # there exist manual accounts to link, and the item has at least one SFA begin unlinked_count = @simplefin_unlinked_count_map[item.id] || 0 manuals_exist = @simplefin_has_unlinked_map[item.id] sfa_any = if item.simplefin_accounts.loaded? item.simplefin_accounts.any? else item.simplefin_accounts.exists? end @simplefin_show_relink_map[item.id] = (unlinked_count.to_i == 0 && manuals_exist && sfa_any) rescue StandardError => e Rails.logger.warn("SimpleFin card: CTA computation failed for item #{item.id}: #{e.class} - #{e.message}") @simplefin_show_relink_map[item.id] = false end end # Ensure maps are hashes even when items empty @simplefin_sync_stats_map ||= {} @simplefin_has_unlinked_map ||= {} @simplefin_unlinked_count_map ||= {} @simplefin_duplicate_only_map ||= {} @simplefin_show_relink_map ||= {} end private def compute_duplicate_only_flag(stats) errs = Array(stats && stats["errors"]).map do |e| if e.is_a?(Hash) e["message"] || e[:message] else e.to_s end end errs.present? && errs.all? { |m| m.to_s.downcase.include?("duplicate upstream account detected") } rescue StandardError => e Rails.logger.warn("SimpleFin maps: compute_duplicate_only_flag failed: #{e.class} - #{e.message}") false end end end