fix: stop storing "{supplier_name}" / "{customer_name}" as the document title

Purchase Order, Sales Order and Subcontracting Order point title_field at
the party name field, so Document.set_title_field() never rendered their
title template and every new record stored the placeholder verbatim. On
Purchase Order the field is also mandatory, so the junk value is guaranteed.

Drop the dead defaults (and Purchase Order's reqd, which would otherwise
make an always-empty field mandatory) and backfill the affected rows.
This commit is contained in:
Mihir Kandoi
2026-07-28 10:56:06 +05:30
parent 5a1a9b2034
commit d96999de7f
7 changed files with 41 additions and 9 deletions

View File

@@ -171,14 +171,12 @@
},
{
"allow_on_submit": 1,
"default": "{supplier_name}",
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
"label": "Title",
"no_copy": 1,
"print_hide": 1,
"reqd": 1
"print_hide": 1
},
{
"fieldname": "naming_series",
@@ -1309,7 +1307,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2025-07-31 17:19:40.816883",
"modified": "2026-07-28 12:20:11.284370",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@@ -162,7 +162,7 @@ class PurchaseOrder(BuyingController):
taxes_and_charges_deducted: DF.Currency
tc_name: DF.Link | None
terms: DF.TextEditor | None
title: DF.Data
title: DF.Data | None
to_date: DF.Date | None
total: DF.Currency
total_net_weight: DF.Float

View File

@@ -444,3 +444,4 @@ erpnext.patches.v16_0.crm_settings_handle_allowed_users_for_frappe_crm
erpnext.patches.v16_0.backfill_pick_list_transferred_qty
erpnext.patches.v16_0.access_control_for_project_users
erpnext.patches.v16_0.rename_ar_ap_ageing_filter
erpnext.patches.v15_0.fix_titles

View File

@@ -0,0 +1,20 @@
import frappe
def execute():
"""
These doctypes point `title_field` at the party name field, so their `title`
default was never rendered and got stored as the literal template string.
"""
for doctype, source_field in (
("Purchase Order", "supplier_name"),
("Subcontracting Order", "supplier_name"),
("Sales Order", "customer_name"),
):
table = frappe.qb.DocType(doctype)
(
frappe.qb.update(table)
.set(table.title, table[source_field])
.where(table.title == f"{{{source_field}}}")
).run()

View File

@@ -187,7 +187,6 @@
},
{
"allow_on_submit": 1,
"default": "{customer_name}",
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
@@ -1680,7 +1679,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2026-03-06 15:33:49.059029",
"modified": "2026-07-28 12:20:44.130918",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",

View File

@@ -66,7 +66,6 @@
"fields": [
{
"allow_on_submit": 1,
"default": "{supplier_name}",
"fieldname": "title",
"fieldtype": "Data",
"hidden": 1,
@@ -465,7 +464,7 @@
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
"modified": "2024-12-06 15:21:49.924146",
"modified": "2026-07-28 12:21:09.663812",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Order",

View File

@@ -49,3 +49,18 @@ class TestInit(unittest.TestCase):
from frappe.tests.test_patches import check_patch_files
check_patch_files("erpnext")
def test_no_unrendered_title_templates(self):
modules = frappe.get_all("Module Def", filters={"app_name": "erpnext"}, pluck="name")
for doctype in frappe.get_all("DocType", filters={"module": ("in", modules)}, pluck="name"):
meta = frappe.get_meta(doctype)
field = meta.get_field("title")
if not field or not field.default or "{" not in field.default:
continue
self.assertEqual(
meta.title_field,
"title",
f"{doctype}: title default {field.default!r} is stored verbatim because "
"Document.set_title_field() only renders it when title_field is 'title'",
)