Allow LLM to enhance provider merchants (#455)

* Allow LLM to enhance provider merchants

* FIX match uniqueness constraint
This commit is contained in:
soky srm
2025-12-17 15:26:35 +01:00
committed by GitHub
parent 7be799fac7
commit a3e13a632b

View File

@@ -30,32 +30,29 @@ class Family::AutoMerchantDetector
modified_count = 0
scope.each do |transaction|
auto_detection = result.data.find { |c| c.transaction_id == transaction.id }
next unless auto_detection&.business_name.present? && auto_detection&.business_url.present?
merchant_id = user_merchants_input.find { |m| m[:name] == auto_detection&.business_name }&.dig(:id)
existing_merchant = transaction.merchant
if merchant_id.nil? && auto_detection&.business_url.present? && auto_detection&.business_name.present? && Setting.brand_fetch_client_id.present?
ai_provider_merchant = ProviderMerchant.find_or_create_by!(
source: "ai",
name: auto_detection.business_name,
website_url: auto_detection.business_url,
) do |pm|
pm.logo_url = "#{default_logo_provider_url}/#{auto_detection.business_url}/icon/fallback/lettermark/w/40/h/40?c=#{Setting.brand_fetch_client_id}"
if existing_merchant.nil?
# Case 1: No merchant - create/find AI merchant and assign
merchant_id = find_matching_user_merchant(auto_detection)
merchant_id ||= find_or_create_ai_merchant(auto_detection)&.id
if merchant_id.present?
was_modified = transaction.enrich_attribute(:merchant_id, merchant_id, source: "ai")
transaction.lock_attr!(:merchant_id)
modified_count += 1 if was_modified
end
elsif existing_merchant.is_a?(ProviderMerchant) && existing_merchant.source != "ai"
# Case 2: Has provider merchant (non-AI) - enhance it with AI data
if enhance_provider_merchant(existing_merchant, auto_detection)
transaction.lock_attr!(:merchant_id)
modified_count += 1
end
end
merchant_id = merchant_id || ai_provider_merchant&.id
if merchant_id.present?
was_modified = transaction.enrich_attribute(
:merchant_id,
merchant_id,
source: "ai"
)
# We lock the attribute so that this Rule doesn't try to run again
transaction.lock_attr!(:merchant_id)
# enrich_attribute returns true if the transaction was actually modified
modified_count += 1 if was_modified
end
# Case 3: AI merchant or FamilyMerchant - skip (already good or user-set)
end
modified_count
@@ -95,8 +92,50 @@ class Family::AutoMerchantDetector
end
def scope
family.transactions.where(id: transaction_ids, merchant_id: nil)
family.transactions.where(id: transaction_ids)
.enrichable(:merchant_id)
.includes(:merchant, :entry)
end
def find_matching_user_merchant(auto_detection)
user_merchants_input.find { |m| m[:name] == auto_detection.business_name }&.dig(:id)
end
def find_or_create_ai_merchant(auto_detection)
# Only use (source, name) for find_or_create since that's the uniqueness constraint
ProviderMerchant.find_or_create_by!(
source: "ai",
name: auto_detection.business_name
) do |pm|
pm.website_url = auto_detection.business_url
if Setting.brand_fetch_client_id.present?
pm.logo_url = "#{default_logo_provider_url}/#{auto_detection.business_url}/icon/fallback/lettermark/w/40/h/40?c=#{Setting.brand_fetch_client_id}"
end
end
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
# Race condition: another process created the merchant between our find and create
ProviderMerchant.find_by(source: "ai", name: auto_detection.business_name)
end
def enhance_provider_merchant(merchant, auto_detection)
updates = {}
# Add website_url if missing
if merchant.website_url.blank? && auto_detection.business_url.present?
updates[:website_url] = auto_detection.business_url
# Add logo if BrandFetch is configured
if Setting.brand_fetch_client_id.present?
updates[:logo_url] = "#{default_logo_provider_url}/#{auto_detection.business_url}/icon/fallback/lettermark/w/40/h/40?c=#{Setting.brand_fetch_client_id}"
end
end
return false if updates.empty?
merchant.update!(updates)
true
rescue ActiveRecord::RecordInvalid => e
Rails.logger.error("Failed to enhance merchant #{merchant.id}: #{e.message}")
false
end
end