fix(pos): preserve contacts and enforce permissions in set_customer_info (#55463)

This commit is contained in:
Diptanil Saha
2026-06-01 04:30:01 +05:30
committed by GitHub
parent 32594c97c6
commit 45d9af9430
2 changed files with 71 additions and 32 deletions

View File

@@ -427,42 +427,80 @@ def get_past_order_list(search_term: str, status: str, limit: int = 20):
@frappe.whitelist() @frappe.whitelist()
def set_customer_info(fieldname: str, customer: str, value: str = ""): def set_customer_info(fieldname: str, customer: str, value: str = ""):
customer_doc = frappe.get_doc("Customer", customer)
customer_doc.check_permission("write")
if fieldname == "loyalty_program": if fieldname == "loyalty_program":
frappe.db.set_value("Customer", customer, "loyalty_program", value) customer_doc.loyalty_program = value
else:
contact = customer_doc.get("customer_primary_contact")
if not contact:
Contact = DocType("Contact")
DynamicLink = DocType("Dynamic Link")
contact = frappe.get_cached_value("Customer", customer, "customer_primary_contact") # Inner join with Contact DocType, to priorities records that have is_primary_contact set.
if not contact: query = (
contact = frappe.db.sql( frappe.qb.from_(DynamicLink)
""" .join(Contact)
SELECT parent FROM `tabDynamic Link` .on(DynamicLink.parent == Contact.name)
WHERE .select(DynamicLink.parent)
parenttype = 'Contact' AND .where(
parentfield = 'links' AND (DynamicLink.link_name == customer)
link_doctype = 'Customer' AND & (DynamicLink.parentfield == "links")
link_name = %s & (DynamicLink.parenttype == "Contact")
""", & (DynamicLink.link_doctype == "Customer")
(customer), )
as_dict=1, .orderby(Contact.is_primary_contact, order=Order.desc)
) )
contact = contact[0].get("parent") if contact else None
if not contact: contacts = query.run(pluck=DynamicLink.parent)
new_contact = frappe.new_doc("Contact")
new_contact.is_primary_contact = 1
new_contact.first_name = customer
new_contact.set("links", [{"link_doctype": "Customer", "link_name": customer}])
new_contact.save()
contact = new_contact.name
frappe.db.set_value("Customer", customer, "customer_primary_contact", contact)
contact_doc = frappe.get_doc("Contact", contact) contact = contacts[0] if contacts else None
if fieldname == "email_id":
contact_doc.set("email_ids", [{"email_id": value, "is_primary": 1}]) if not contact:
frappe.db.set_value("Customer", customer, "email_id", value) new_contact = frappe.new_doc("Contact")
elif fieldname == "mobile_no": new_contact.is_primary_contact = 1
contact_doc.set("phone_nos", [{"phone": value, "is_primary_mobile_no": 1}]) new_contact.first_name = customer
frappe.db.set_value("Customer", customer, "mobile_no", value) new_contact.set("links", [{"link_doctype": "Customer", "link_name": customer}])
contact_doc.save() new_contact.save()
contact = new_contact.name
def set_primary_phone_no_email(field, value):
# Create new record instead deleting existing email or phone_no and setting the new row as primary.
field_mapper = {
"email_ids": {"field": "email_id", "primary": "is_primary"},
"phone_nos": {"field": "phone", "primary": "is_primary_mobile_no"},
}
value_already_exists = False
for d in contact_doc.get(field):
if d.get(field_mapper[field].get("field")) == value and not value_already_exists:
d.set(field_mapper[field]["primary"], 1)
value_already_exists = True
continue
d.set(field_mapper[field]["primary"], 0)
if not value_already_exists:
contact_doc.append(
field, {field_mapper[field]["field"]: value, field_mapper[field]["primary"]: 1}
)
contact_doc = frappe.get_doc("Contact", contact)
# setting is_primary_contact = 1 on Contact to refetch the same contact incase it's removed from Customer records.
contact_doc.set("is_primary_contact", 1)
if fieldname == "email_id":
set_primary_phone_no_email("email_ids", value)
elif fieldname == "mobile_no":
set_primary_phone_no_email("phone_nos", value)
# Saving contact_doc to set mobile_no and email.
contact_doc.save()
# Auto-fetches from Contact DocType, no need to set values separately.
customer_doc.customer_primary_contact = contact
# using save method instead db.set_value which bypasses the validation for loyalty program
# and auto sets the mobile_no and email field on customer records.
customer_doc.save()
@frappe.whitelist() @frappe.whitelist()

View File

@@ -987,6 +987,7 @@ erpnext.PointOfSale.ItemCart = class {
customer: current_customer, customer: current_customer,
value: this.value, value: this.value,
}, },
freeze: true,
callback: (r) => { callback: (r) => {
if (!r.exc) { if (!r.exc) {
me.customer_info[this.df.fieldname] = this.value; me.customer_info[this.df.fieldname] = this.value;