From 3ac4a582f5301c3beba0ae1b3462811c144d067c Mon Sep 17 00:00:00 2001 From: Hussain Nagaria Date: Mon, 29 Sep 2025 18:59:55 +0530 Subject: [PATCH] feat: map fields for syncing --- .../__init__.py | 0 .../facebook_lead_form/facebook_lead_form.js | 8 ++ .../facebook_lead_form.json | 81 +++++++++++++++++++ .../facebook_lead_form/facebook_lead_form.py | 24 ++++++ .../test_facebook_lead_form.py | 22 +++++ .../facebook_lead_form_question.json | 12 ++- .../facebook_lead_form_question.py | 1 + .../lead_form_field_mapping.json | 47 ----------- .../lead_form_field_mapping.py | 9 --- .../lead_sync_source/lead_sync_source.json | 14 +--- .../lead_sync_source/lead_sync_source.py | 36 ++++++++- 11 files changed, 181 insertions(+), 73 deletions(-) rename crm/lead_syncing/doctype/{lead_form_field_mapping => facebook_lead_form}/__init__.py (100%) create mode 100644 crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.js create mode 100644 crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.json create mode 100644 crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.py create mode 100644 crm/lead_syncing/doctype/facebook_lead_form/test_facebook_lead_form.py delete mode 100644 crm/lead_syncing/doctype/lead_form_field_mapping/lead_form_field_mapping.json delete mode 100644 crm/lead_syncing/doctype/lead_form_field_mapping/lead_form_field_mapping.py diff --git a/crm/lead_syncing/doctype/lead_form_field_mapping/__init__.py b/crm/lead_syncing/doctype/facebook_lead_form/__init__.py similarity index 100% rename from crm/lead_syncing/doctype/lead_form_field_mapping/__init__.py rename to crm/lead_syncing/doctype/facebook_lead_form/__init__.py diff --git a/crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.js b/crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.js new file mode 100644 index 00000000..dcfd10c4 --- /dev/null +++ b/crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.js @@ -0,0 +1,8 @@ +// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Facebook Lead Form", { + refresh(frm) { + // + }, +}); diff --git a/crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.json b/crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.json new file mode 100644 index 00000000..585a18e8 --- /dev/null +++ b/crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.json @@ -0,0 +1,81 @@ +{ + "actions": [], + "allow_rename": 1, + "autoname": "field:id", + "creation": "2025-09-26 19:01:48.325681", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "page", + "id", + "column_break_ahyo", + "form_name", + "section_break_iqhq", + "questions" + ], + "fields": [ + { + "fieldname": "page", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Page", + "options": "Facebook Page", + "reqd": 1 + }, + { + "fieldname": "id", + "fieldtype": "Data", + "label": "ID", + "unique": 1 + }, + { + "fieldname": "column_break_ahyo", + "fieldtype": "Column Break" + }, + { + "fieldname": "form_name", + "fieldtype": "Data", + "label": "Form Name" + }, + { + "fieldname": "section_break_iqhq", + "fieldtype": "Section Break" + }, + { + "fieldname": "questions", + "fieldtype": "Table", + "label": "Questions", + "options": "Facebook Lead Form Question" + } + ], + "grid_page_length": 50, + "in_create": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2025-09-29 18:50:19.215513", + "modified_by": "Administrator", + "module": "Lead Syncing", + "name": "Facebook Lead Form", + "naming_rule": "By fieldname", + "owner": "hussain@frappe.io", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "row_format": "Dynamic", + "show_title_field_in_link": 1, + "sort_field": "creation", + "sort_order": "DESC", + "states": [], + "title_field": "form_name" +} diff --git a/crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.py b/crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.py new file mode 100644 index 00000000..c507ac26 --- /dev/null +++ b/crm/lead_syncing/doctype/facebook_lead_form/facebook_lead_form.py @@ -0,0 +1,24 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class FacebookLeadForm(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from crm.lead_syncing.doctype.facebook_lead_form_question.facebook_lead_form_question import FacebookLeadFormQuestion + from frappe.types import DF + + form_name: DF.Data | None + id: DF.Data | None + page: DF.Link + questions: DF.Table[FacebookLeadFormQuestion] + # end: auto-generated types + + pass diff --git a/crm/lead_syncing/doctype/facebook_lead_form/test_facebook_lead_form.py b/crm/lead_syncing/doctype/facebook_lead_form/test_facebook_lead_form.py new file mode 100644 index 00000000..72d39d99 --- /dev/null +++ b/crm/lead_syncing/doctype/facebook_lead_form/test_facebook_lead_form.py @@ -0,0 +1,22 @@ +# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests import IntegrationTestCase + + +# On IntegrationTestCase, the doctype test records and all +# link-field test record dependencies are recursively loaded +# Use these module variables to add/remove to/from that list +EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] +IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"] + + + +class IntegrationTestFacebookLeadForm(IntegrationTestCase): + """ + Integration tests for FacebookLeadForm. + Use this class for testing interactions between multiple components. + """ + + pass diff --git a/crm/lead_syncing/doctype/facebook_lead_form_question/facebook_lead_form_question.json b/crm/lead_syncing/doctype/facebook_lead_form_question/facebook_lead_form_question.json index 0db97c60..f8c10638 100644 --- a/crm/lead_syncing/doctype/facebook_lead_form_question/facebook_lead_form_question.json +++ b/crm/lead_syncing/doctype/facebook_lead_form_question/facebook_lead_form_question.json @@ -10,7 +10,8 @@ "id", "column_break_hgde", "key", - "type" + "type", + "mapped_to_crm_field" ], "fields": [ { @@ -40,13 +41,20 @@ "fieldname": "id", "fieldtype": "Data", "label": "ID" + }, + { + "default": "Not Synced", + "fieldname": "mapped_to_crm_field", + "fieldtype": "Autocomplete", + "in_list_view": 1, + "label": "Mapped to CRM Field" } ], "grid_page_length": 50, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-09-26 19:48:30.897092", + "modified": "2025-09-29 18:45:21.800960", "modified_by": "hussain@frappe.io", "module": "Lead Syncing", "name": "Facebook Lead Form Question", diff --git a/crm/lead_syncing/doctype/facebook_lead_form_question/facebook_lead_form_question.py b/crm/lead_syncing/doctype/facebook_lead_form_question/facebook_lead_form_question.py index 960e7c6d..edfe924c 100644 --- a/crm/lead_syncing/doctype/facebook_lead_form_question/facebook_lead_form_question.py +++ b/crm/lead_syncing/doctype/facebook_lead_form_question/facebook_lead_form_question.py @@ -17,6 +17,7 @@ class FacebookLeadFormQuestion(Document): id: DF.Data | None key: DF.Data label: DF.Data | None + mapped_to_crm_field: DF.Autocomplete | None parent: DF.Data parentfield: DF.Data parenttype: DF.Data diff --git a/crm/lead_syncing/doctype/lead_form_field_mapping/lead_form_field_mapping.json b/crm/lead_syncing/doctype/lead_form_field_mapping/lead_form_field_mapping.json deleted file mode 100644 index a31a6f02..00000000 --- a/crm/lead_syncing/doctype/lead_form_field_mapping/lead_form_field_mapping.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "actions": [], - "allow_rename": 1, - "creation": "2025-09-26 18:54:57.313880", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "source_field_name", - "column_break_zbml", - "crm_field_name" - ], - "fields": [ - { - "fieldname": "source_field_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Source Field Name", - "reqd": 1 - }, - { - "fieldname": "column_break_zbml", - "fieldtype": "Column Break" - }, - { - "fieldname": "crm_field_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "CRM Field Name", - "reqd": 1 - } - ], - "grid_page_length": 50, - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2025-09-26 18:55:59.773584", - "modified_by": "hussain@frappe.io", - "module": "Lead Syncing", - "name": "Lead Form Field Mapping", - "owner": "hussain@frappe.io", - "permissions": [], - "row_format": "Dynamic", - "sort_field": "creation", - "sort_order": "DESC", - "states": [] -} diff --git a/crm/lead_syncing/doctype/lead_form_field_mapping/lead_form_field_mapping.py b/crm/lead_syncing/doctype/lead_form_field_mapping/lead_form_field_mapping.py deleted file mode 100644 index 3f9acc15..00000000 --- a/crm/lead_syncing/doctype/lead_form_field_mapping/lead_form_field_mapping.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -# import frappe -from frappe.model.document import Document - - -class LeadFormFieldMapping(Document): - pass diff --git a/crm/lead_syncing/doctype/lead_sync_source/lead_sync_source.json b/crm/lead_syncing/doctype/lead_sync_source/lead_sync_source.json index 47d9d25f..931fff67 100644 --- a/crm/lead_syncing/doctype/lead_sync_source/lead_sync_source.json +++ b/crm/lead_syncing/doctype/lead_sync_source/lead_sync_source.json @@ -10,8 +10,6 @@ "access_token", "column_break_lwcw", "last_synced_at", - "section_break_itlk", - "field_mapping", "facebook_tab", "facebook_page", "column_break_uxlr", @@ -40,16 +38,6 @@ "fieldtype": "Small Text", "label": "Access Token" }, - { - "fieldname": "section_break_itlk", - "fieldtype": "Section Break" - }, - { - "fieldname": "field_mapping", - "fieldtype": "Table", - "label": "Field Mapping", - "options": "Lead Form Field Mapping" - }, { "depends_on": "eval:doc.type===\"Facebook\"", "fieldname": "facebook_tab", @@ -76,7 +64,7 @@ "grid_page_length": 50, "index_web_pages_for_search": 1, "links": [], - "modified": "2025-09-26 19:04:31.584094", + "modified": "2025-09-29 18:45:32.305860", "modified_by": "hussain@frappe.io", "module": "Lead Syncing", "name": "Lead Sync Source", diff --git a/crm/lead_syncing/doctype/lead_sync_source/lead_sync_source.py b/crm/lead_syncing/doctype/lead_sync_source/lead_sync_source.py index e438aa56..c9436414 100644 --- a/crm/lead_syncing/doctype/lead_sync_source/lead_sync_source.py +++ b/crm/lead_syncing/doctype/lead_sync_source/lead_sync_source.py @@ -17,6 +17,22 @@ def get_fb_graph_api_url(endpoint: str) -> str: class LeadSyncSource(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + access_token: DF.SmallText | None + facebook_lead_form: DF.Link | None + facebook_page: DF.Link | None + last_synced_at: DF.Datetime | None + name: DF.Int | None + type: DF.Literal["Facebook"] + # end: auto-generated types + def before_save(self): if self.type == "Facebook" and self.access_token: fetch_and_store_pages_from_facebook(self.access_token) @@ -38,12 +54,28 @@ def sync_leads_from_facebook(access_token: str, lead_form_id: str) -> None: "limit": 15000, }, ).get("data", []) + + form_questions = frappe.db.get_all( + "Facebook Lead Form Question", filters={"parent": lead_form_id}, fields=["key", "mapped_to_crm_field"] + ) + + # Map form questions to CRM Lead fields + question_to_field_map = {q["key"]: q["mapped_to_crm_field"] + for q in form_questions + if q["mapped_to_crm_field"] + } + for lead in leads: + lead_data = {item["name"]: item["values"][0] for item in lead["field_data"]} + crm_lead_data = {question_to_field_map.get(k): v for + k, v in lead_data.items() if k in question_to_field_map + } + crm_lead_data["source"] = "Facebook" + frappe.get_doc( { "doctype": "CRM Lead", - "first_name": lead["field_data"][0]["values"][0], - "source": "Facebook", + **crm_lead_data, } ).insert(ignore_permissions=True)