From 94320a9928fb98787d30e263e362609cce460985 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 11 Aug 2026 16:09:08 +0530 Subject: [PATCH 1/2] perf: rewrite customer and supplier naming setters only on change Every Selling Settings save reran set_by_naming_series for Customer and every Buying Settings save reran it for Supplier, rewriting the naming_series property setters with their cache clears and running the naming_series backfill UPDATE on the master table. Gate both on has_value_changed, following Stock Settings. Naming behaviour is unaffected: Customer.autoname and Supplier.autoname read the master-name default, which is still set on every save. --- .../buying_settings/buying_settings.py | 12 +++++++--- .../selling_settings/selling_settings.py | 22 ++++++++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.py b/erpnext/buying/doctype/buying_settings/buying_settings.py index 91ba900873b..d4f32e7cb2b 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.py +++ b/erpnext/buying/doctype/buying_settings/buying_settings.py @@ -53,6 +53,15 @@ class BuyingSettings(Document): for key in ["supplier_group", "supp_master_name", "maintain_same_rate", "buying_price_list"]: frappe.db.set_default(key, self.get(key, "")) + self.update_supplier_naming_settings() + + if not self.bill_for_rejected_quantity_in_purchase_invoice: + self.set_valuation_rate_for_rejected_materials = 0 + + def update_supplier_naming_settings(self): + if not self.has_value_changed("supp_master_name"): + return + from erpnext.utilities.naming import set_by_naming_series set_by_naming_series( @@ -62,9 +71,6 @@ class BuyingSettings(Document): hide_name_field=False, ) - if not self.bill_for_rejected_quantity_in_purchase_invoice: - self.set_valuation_rate_for_rejected_materials = 0 - def before_save(self): self.check_maintain_same_rate() diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.py b/erpnext/selling/doctype/selling_settings/selling_settings.py index 66e4bf5d93a..70c57579cb2 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.py +++ b/erpnext/selling/doctype/selling_settings/selling_settings.py @@ -84,14 +84,7 @@ class SellingSettings(Document): ]: frappe.db.set_default(key, self.get(key, "")) - from erpnext.utilities.naming import set_by_naming_series - - set_by_naming_series( - "Customer", - "customer_name", - self.get("cust_master_name") == "Naming Series", - hide_name_field=False, - ) + self.update_customer_naming_settings() self.validate_fallback_to_default_price_list() @@ -101,6 +94,19 @@ class SellingSettings(Document): if old_doc and old_doc.enable_utm != self.enable_utm: toggle_utm_analytics_section(not self.enable_utm) + def update_customer_naming_settings(self): + if not self.has_value_changed("cust_master_name"): + return + + from erpnext.utilities.naming import set_by_naming_series + + set_by_naming_series( + "Customer", + "customer_name", + self.get("cust_master_name") == "Naming Series", + hide_name_field=False, + ) + def validate_fallback_to_default_price_list(self): if ( self.fallback_to_default_price_list From 909e7b1457463a759a99a7a441b83b66d10a7624 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Tue, 11 Aug 2026 16:10:15 +0530 Subject: [PATCH 2/2] test: cover customer and supplier naming setter gating --- .../buying_settings/test_buying_settings.py | 25 +++++++++++++++++-- .../selling_settings/test_selling_settings.py | 22 ++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/test_buying_settings.py b/erpnext/buying/doctype/buying_settings/test_buying_settings.py index c884f51e7eb..c5ddef4fe7f 100644 --- a/erpnext/buying/doctype/buying_settings/test_buying_settings.py +++ b/erpnext/buying/doctype/buying_settings/test_buying_settings.py @@ -1,9 +1,30 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -# import frappe + +from unittest.mock import patch + +import frappe from erpnext.tests.utils import ERPNextTestSuite class TestBuyingSettings(ERPNextTestSuite): - pass + def test_unrelated_change_does_not_update_supplier_metadata(self): + settings = frappe.get_single("Buying Settings") + settings.allow_multiple_items = not settings.allow_multiple_items + + with patch("erpnext.utilities.naming.set_by_naming_series") as set_by_naming_series: + settings.save() + + set_by_naming_series.assert_not_called() + + def test_supplier_metadata_updates_when_related_settings_change(self): + settings = frappe.get_single("Buying Settings") + settings.supp_master_name = ( + "Supplier Name" if settings.supp_master_name == "Naming Series" else "Naming Series" + ) + + with patch("erpnext.utilities.naming.set_by_naming_series") as set_by_naming_series: + settings.save() + + set_by_naming_series.assert_called_once() diff --git a/erpnext/selling/doctype/selling_settings/test_selling_settings.py b/erpnext/selling/doctype/selling_settings/test_selling_settings.py index 9a839d3b2f3..6bfa45527b8 100644 --- a/erpnext/selling/doctype/selling_settings/test_selling_settings.py +++ b/erpnext/selling/doctype/selling_settings/test_selling_settings.py @@ -1,6 +1,8 @@ # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt +from unittest.mock import patch + import frappe from erpnext.tests.utils import ERPNextTestSuite @@ -12,3 +14,23 @@ class TestSellingSettings(ERPNextTestSuite): # if setup was completed correctly default = frappe.db.get_single_value("Selling Settings", "maintain_same_rate_action") self.assertEqual("Stop", default) + + def test_unrelated_change_does_not_update_customer_metadata(self): + settings = frappe.get_single("Selling Settings") + settings.allow_multiple_items = not settings.allow_multiple_items + + with patch("erpnext.utilities.naming.set_by_naming_series") as set_by_naming_series: + settings.save() + + set_by_naming_series.assert_not_called() + + def test_customer_metadata_updates_when_related_settings_change(self): + settings = frappe.get_single("Selling Settings") + settings.cust_master_name = ( + "Customer Name" if settings.cust_master_name == "Naming Series" else "Naming Series" + ) + + with patch("erpnext.utilities.naming.set_by_naming_series") as set_by_naming_series: + settings.save() + + set_by_naming_series.assert_called_once()