fix(crm): scope create_customer rollback so a contact/address failure keeps the Customer

create_customer wrapped customer.insert() + create_contacts() + create_address() in one try whose except
did a full frappe.db.rollback(), so a failure while linking contacts/address discarded the Customer just
created (MariaDB kept it pre-migration). Split the try: the customer insert keeps its full rollback (safe
-- nothing precedes it), and contact/address linking runs under a savepoint so its failure rolls back only
the links, preserving the Customer and healing the Postgres txn.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Mihir Kandoi
2026-07-01 11:55:42 +05:30
parent c58a4026a7
commit 76b31d9269

View File

@@ -154,15 +154,23 @@ def create_customer(customer_data: dict | None = None):
customer.set(field, customer_data.get(field))
customer.insert(ignore_permissions=True)
customer_name = customer.name
contacts = frappe.parse_json(customer_data.get("contacts"))
create_contacts(contacts, customer_name, "Customer", customer_name)
create_address("Customer", customer_name, customer_data.get("address"))
return customer_name
except Exception:
frappe.db.rollback()
frappe.log_error(frappe.get_traceback(), "Error while creating customer against Frappe CRM Deal")
pass
return
# Link contacts/address under a savepoint so a failure here does NOT discard the Customer just
# created (a full rollback would; MariaDB kept it pre-migration). Linking is best-effort.
frappe.db.savepoint("crm_customer_links")
try:
contacts = frappe.parse_json(customer_data.get("contacts"))
create_contacts(contacts, customer_name, "Customer", customer_name)
create_address("Customer", customer_name, customer_data.get("address"))
except Exception:
frappe.db.rollback(save_point="crm_customer_links")
frappe.log_error(frappe.get_traceback(), "Error while linking contacts/address to new Customer")
return customer_name
def validate_frappe_crm_sync():