feat: field based de-dupe
This commit is contained in:
parent
1b87635d3d
commit
38e10f91cb
@ -9,7 +9,9 @@
|
|||||||
"column_break_dhay",
|
"column_break_dhay",
|
||||||
"source",
|
"source",
|
||||||
"section_break_fhot",
|
"section_break_fhot",
|
||||||
"lead_data"
|
"lead_data",
|
||||||
|
"section_break_knec",
|
||||||
|
"traceback"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -43,13 +45,23 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "section_break_fhot",
|
"fieldname": "section_break_fhot",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_knec",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "traceback",
|
||||||
|
"fieldtype": "Code",
|
||||||
|
"label": "Traceback",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-10-19 18:30:15.381205",
|
"modified": "2025-10-19 18:53:32.192551",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Lead Syncing",
|
"module": "Lead Syncing",
|
||||||
"name": "Failed Lead Sync Log",
|
"name": "Failed Lead Sync Log",
|
||||||
|
|||||||
@ -16,6 +16,7 @@ class FailedLeadSyncLog(Document):
|
|||||||
|
|
||||||
lead_data: DF.Code | None
|
lead_data: DF.Code | None
|
||||||
source: DF.Link | None
|
source: DF.Link | None
|
||||||
|
traceback: DF.Code | None
|
||||||
type: DF.Literal["Duplicate", "Failure"]
|
type: DF.Literal["Duplicate", "Failure"]
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,15 @@
|
|||||||
import frappe
|
import frappe
|
||||||
|
from frappe.exceptions import ValidationError
|
||||||
from frappe.integrations.utils import make_get_request
|
from frappe.integrations.utils import make_get_request
|
||||||
|
|
||||||
FB_GRAPH_API_BASE = "https://graph.facebook.com"
|
FB_GRAPH_API_BASE = "https://graph.facebook.com"
|
||||||
FB_GRAPH_API_VERSION = "v23.0"
|
FB_GRAPH_API_VERSION = "v23.0"
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateLeadError(ValidationError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def get_fb_graph_api_url(endpoint: str) -> str:
|
def get_fb_graph_api_url(endpoint: str) -> str:
|
||||||
if endpoint.startswith("/"):
|
if endpoint.startswith("/"):
|
||||||
endpoint = endpoint[1:]
|
endpoint = endpoint[1:]
|
||||||
@ -40,18 +45,17 @@ class FacebookSyncSource:
|
|||||||
crm_lead_data["facebook_form_id"] = self.form_id
|
crm_lead_data["facebook_form_id"] = self.form_id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
self.validate_duplicate_lead(crm_lead_data, question_to_field_map)
|
||||||
frappe.get_doc(
|
frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "CRM Lead",
|
"doctype": "CRM Lead",
|
||||||
**crm_lead_data,
|
**crm_lead_data,
|
||||||
}
|
}
|
||||||
).insert(ignore_permissions=True)
|
).insert(ignore_permissions=True)
|
||||||
except frappe.UniqueValidationError:
|
except (frappe.UniqueValidationError, DuplicateLeadError):
|
||||||
# Skip duplicate leads based on facebook_lead_id
|
|
||||||
# TODO: de-duplication based on field values
|
|
||||||
self.create_failure_log(lead, "Duplicate")
|
self.create_failure_log(lead, "Duplicate")
|
||||||
except Exception:
|
except Exception:
|
||||||
self.create_failure_log(lead)
|
self.create_failure_log(lead, traceback=frappe.get_traceback(with_context=True))
|
||||||
|
|
||||||
self.update_last_synced_at()
|
self.update_last_synced_at()
|
||||||
|
|
||||||
@ -89,13 +93,16 @@ class FacebookSyncSource:
|
|||||||
"Lead Sync Source", self.source_name or {"facebook_lead_form": self.form_id}, "last_synced_at"
|
"Lead Sync Source", self.source_name or {"facebook_lead_form": self.form_id}, "last_synced_at"
|
||||||
)
|
)
|
||||||
|
|
||||||
def create_failure_log(self, lead_data: dict | None = None, type: str = "Failure"):
|
def create_failure_log(
|
||||||
|
self, lead_data: dict | None = None, type: str = "Failure", traceback: str | None = None
|
||||||
|
):
|
||||||
return frappe.get_doc(
|
return frappe.get_doc(
|
||||||
{
|
{
|
||||||
"doctype": "Failed Lead Sync Log",
|
"doctype": "Failed Lead Sync Log",
|
||||||
"type": type,
|
"type": type,
|
||||||
"lead_data": frappe.as_json(lead_data),
|
"lead_data": frappe.as_json(lead_data),
|
||||||
"source": self.get_source_name()
|
"source": self.get_source_name(),
|
||||||
|
"traceback": traceback,
|
||||||
}
|
}
|
||||||
).insert(ignore_permissions=True)
|
).insert(ignore_permissions=True)
|
||||||
|
|
||||||
@ -113,6 +120,12 @@ class FacebookSyncSource:
|
|||||||
|
|
||||||
return frappe.db.get_value("Lead Sync Source", {"facebook_lead_form": self.form_id}, "name")
|
return frappe.db.get_value("Lead Sync Source", {"facebook_lead_form": self.form_id}, "name")
|
||||||
|
|
||||||
|
def validate_duplicate_lead(self, lead_data: dict, field_mapping: dict):
|
||||||
|
validation_filters = {crm_field: lead_data[crm_field] for crm_field in field_mapping.values()}
|
||||||
|
validation_filters["facebook_form_id"] = lead_data["facebook_form_id"] # only for this campaign
|
||||||
|
if frappe.db.exists("CRM Lead", validation_filters):
|
||||||
|
raise DuplicateLeadError
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def fetch_and_store_pages_from_facebook(access_token: str) -> list[dict]:
|
def fetch_and_store_pages_from_facebook(access_token: str) -> list[dict]:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user