mirror of
https://github.com/we-promise/sure.git
synced 2026-06-01 00:39:01 +00:00
* fix(merchants): preserve manual merchant edits across provider sync Fixes #1977. Merging merchants, converting a synced (provider) merchant to a family merchant, and unlinking a merchant all reassign transactions.merchant_id via update_all without flagging the entries as user_modified. The next provider sync sees the entries as unmodified and reverts the change. Add Entry.mark_user_modified_for_transactions! and call it (before the merchant_id update, so the scope still matches) in Merchant::Merger#merge!, ProviderMerchant#convert_to_family_merchant_for, and #unlink_from_family. The sync skip-guard already honours user_modified, so flagged entries are left untouched on subsequent syncs. * fix(merchants): pass transaction relation to bulk user_modified helper Addresses PR #1981 review (CodeRabbit): mark_user_modified_for_transactions! now accepts an ActiveRecord::Relation and selects ids via subquery, so large merges/unlinks don't materialize ids or hit SQL parameter limits. Array of ids still supported. Callers pass the scope relation directly.
62 lines
1.8 KiB
Ruby
62 lines
1.8 KiB
Ruby
class Merchant::Merger
|
|
class UnauthorizedMerchantError < StandardError; end
|
|
|
|
attr_reader :family, :target_merchant, :source_merchants, :merged_count
|
|
|
|
def initialize(family:, target_merchant:, source_merchants:)
|
|
@family = family
|
|
@target_merchant = target_merchant
|
|
@merged_count = 0
|
|
|
|
validate_merchant_belongs_to_family!(target_merchant, "Target merchant")
|
|
|
|
sources = Array(source_merchants)
|
|
sources.each { |m| validate_merchant_belongs_to_family!(m, "Source merchant '#{m.name}'") }
|
|
|
|
@source_merchants = sources.reject { |m| m.id == target_merchant.id }
|
|
end
|
|
|
|
private
|
|
|
|
def validate_merchant_belongs_to_family!(merchant, label)
|
|
return if family_merchant_ids.include?(merchant.id)
|
|
|
|
raise UnauthorizedMerchantError, "#{label} does not belong to this family"
|
|
end
|
|
|
|
def family_merchant_ids
|
|
@family_merchant_ids ||= begin
|
|
family_ids = family.merchants.pluck(:id)
|
|
assigned_ids = family.assigned_merchants.pluck(:id)
|
|
(family_ids + assigned_ids).uniq
|
|
end
|
|
end
|
|
|
|
public
|
|
|
|
def merge!
|
|
return false if source_merchants.empty?
|
|
|
|
Merchant.transaction do
|
|
source_merchants.each do |source|
|
|
scope = family.transactions.where(merchant_id: source.id)
|
|
|
|
# Protect the manual reassignment from being reverted on the next
|
|
# provider sync (issue #1977). Must run before the merchant_id update
|
|
# so the scope still matches the source merchant.
|
|
Entry.mark_user_modified_for_transactions!(scope)
|
|
|
|
# Reassign family's transactions to target
|
|
scope.update_all(merchant_id: target_merchant.id)
|
|
|
|
# Delete FamilyMerchant, keep ProviderMerchant (it may be used by other families)
|
|
source.destroy! if source.is_a?(FamilyMerchant)
|
|
|
|
@merged_count += 1
|
|
end
|
|
end
|
|
|
|
true
|
|
end
|
|
end
|