LeadSquared sync now recovers automatically when a contact's cached lead has been deleted or merged on the LeadSquared side. Previously the stale lead id was never cleared, so every new conversation or contact update for that contact failed with "Lead not found" (`MXInvalidEntityReferenceException`) indefinitely. ## What changed - Activity sync: on a "Lead not found" error while posting a conversation/transcript activity, clear the cached `leadsquared_id`, re-resolve the contact to a fresh lead, and retry the activity once (guarded against loops and duplicate leads). - Contact sync: on the same error while updating an existing lead, clear the cached id and create a fresh lead instead. - Fix `get_lead_id` to actually return early for unidentifiable contacts (the guard previously fell through). ## How to reproduce 1. For a LeadSquared-enabled account, point a contact's cached lead id at a lead that no longer exists in LeadSquared. 2. Update the contact, or create/resolve a conversation for it. 3. Before: the sync fails repeatedly with "Lead not found" and never self-corrects. After: the stale id is cleared, a fresh lead is resolved/created, and subsequent syncs reuse the healed id. --------- Co-authored-by: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com>
101 lines
3.2 KiB
Ruby
101 lines
3.2 KiB
Ruby
class Crm::BaseProcessorService
|
|
def initialize(hook)
|
|
@hook = hook
|
|
@account = hook.account
|
|
end
|
|
|
|
# Class method to be overridden by subclasses
|
|
def self.crm_name
|
|
raise NotImplementedError, 'Subclasses must define self.crm_name'
|
|
end
|
|
|
|
# Instance method that calls the class method
|
|
def crm_name
|
|
self.class.crm_name
|
|
end
|
|
|
|
def process_event(event_name, event_data)
|
|
case event_name
|
|
when 'contact.created'
|
|
handle_contact_created(event_data)
|
|
when 'contact.updated'
|
|
handle_contact_updated(event_data)
|
|
when 'conversation.created'
|
|
handle_conversation_created(event_data)
|
|
when 'conversation.updated'
|
|
handle_conversation_updated(event_data)
|
|
else
|
|
{ success: false, error: "Unsupported event: #{event_name}" }
|
|
end
|
|
rescue StandardError => e
|
|
Rails.logger.error "#{crm_name} Processor Error: #{e.message}"
|
|
Rails.logger.error e.backtrace.join("\n")
|
|
{ success: false, error: e.message }
|
|
end
|
|
|
|
# Abstract methods that subclasses must implement
|
|
def handle_contact_created(contact)
|
|
raise NotImplementedError, 'Subclasses must implement #handle_contact_created'
|
|
end
|
|
|
|
def handle_contact_updated(contact)
|
|
raise NotImplementedError, 'Subclasses must implement #handle_contact_updated'
|
|
end
|
|
|
|
def handle_conversation_created(conversation)
|
|
raise NotImplementedError, 'Subclasses must implement #handle_conversation_created'
|
|
end
|
|
|
|
def handle_conversation_resolved(conversation)
|
|
raise NotImplementedError, 'Subclasses must implement #handle_conversation_resolved'
|
|
end
|
|
|
|
# Common helper methods for all CRM processors
|
|
|
|
protected
|
|
|
|
def identifiable_contact?(contact)
|
|
has_social_profile = contact.additional_attributes['social_profiles'].present?
|
|
contact.present? && (contact.email.present? || contact.phone_number.present? || has_social_profile)
|
|
end
|
|
|
|
def get_external_id(contact)
|
|
return nil if contact.additional_attributes.blank?
|
|
return nil if contact.additional_attributes['external'].blank?
|
|
|
|
contact.additional_attributes.dig('external', "#{crm_name}_id")
|
|
end
|
|
|
|
def store_external_id(contact, external_id)
|
|
# Initialize additional_attributes if it's nil
|
|
contact.additional_attributes = {} if contact.additional_attributes.nil?
|
|
|
|
# Initialize external hash if it doesn't exist
|
|
contact.additional_attributes['external'] = {} if contact.additional_attributes['external'].blank?
|
|
|
|
# Store the external ID
|
|
contact.additional_attributes['external']["#{crm_name}_id"] = external_id
|
|
contact.save!
|
|
end
|
|
|
|
def clear_external_id(contact)
|
|
return if contact.additional_attributes.blank?
|
|
return if contact.additional_attributes['external'].blank?
|
|
|
|
contact.additional_attributes['external'].delete("#{crm_name}_id")
|
|
contact.save!
|
|
end
|
|
|
|
def store_conversation_metadata(conversation, metadata)
|
|
# Initialize additional_attributes if it's nil
|
|
conversation.additional_attributes = {} if conversation.additional_attributes.nil?
|
|
|
|
# Initialize CRM-specific hash in additional_attributes
|
|
conversation.additional_attributes[crm_name] = {} if conversation.additional_attributes[crm_name].blank?
|
|
|
|
# Store the metadata
|
|
conversation.additional_attributes[crm_name].merge!(metadata)
|
|
conversation.save!
|
|
end
|
|
end
|