feat: Enhance autoname functionality (#36827)

* feat: Enhance autoname functionality to support expression naming rules with and without dots before dashes

* style: Fix formatting issues

---------

Co-authored-by: Suraj Shetty <surajshetty3416@gmail.com>
This commit is contained in:
Sumit Jain 2026-02-07 11:52:28 +05:30 committed by GitHub
parent 9f7c18029e
commit 1d9eb802fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 86 additions and 1 deletions

View file

@ -230,7 +230,22 @@ def set_name_from_naming_options(autoname, doc):
elif _autoname.startswith("format:"):
doc.name = _format_autoname(autoname, doc)
elif "#" in autoname:
doc.name = make_autoname(autoname, doc=doc)
# For Expression naming rule, first replace braced params, then normalize, then process series
# This handles patterns like {full_name}-{description}-.#####
def get_param_value_for_match(match):
param = match.group()
return parse_naming_series([param[1:-1]], doc=doc)
# Replace braced params first
name_with_params = BRACED_PARAMS_PATTERN.sub(get_param_value_for_match, autoname)
# Normalize pattern: convert '-.#####' to '.-.#####' to support both formats
# This handles cases like {fieldname}-.##### (without dot before dash)
# Pattern matches: dash followed by dot followed by one or more hashes, but only if not preceded by a dot
normalized_autoname = re.sub(r"(?<!\.)(-\.#+)", r".\1", name_with_params)
# Process the series
doc.name = make_autoname(normalized_autoname, doc=doc)
def set_naming_from_document_naming_rule(doc):
@ -584,11 +599,19 @@ def _format_autoname(autoname: str, doc):
Independent of remaining string or separators.
Example pattern: 'format:LOG-{MM}-{fieldname1}-{fieldname2}-{#####}'
Supports both patterns:
- {fieldname}.-.##### (with dot before dash)
- {fieldname}-.##### (without dot before dash)
"""
first_colon_index = autoname.find(":")
autoname_value = autoname[first_colon_index + 1 :]
# Normalize pattern: convert '-.#####' to '.-.#####' to support both formats
# This handles cases like {fieldname}-.##### (without dot before dash)
# Pattern matches: dash followed by dot followed by one or more hashes, but only if not preceded by a dot
autoname_value = re.sub(r"(?<!\.)(-\.#+)", r".\1", autoname_value)
def get_param_value_for_match(match):
param = match.group()
return parse_naming_series([param[1:-1]], doc=doc)
@ -596,4 +619,8 @@ def _format_autoname(autoname: str, doc):
# Replace braced params with their parsed value
name = BRACED_PARAMS_PATTERN.sub(get_param_value_for_match, autoname_value)
# If the result still contains unbraced hash patterns (like .#####), process them as naming series
if "#" in name and "{" not in name:
name = make_autoname(name, doc=doc)
return name

View file

@ -148,6 +148,64 @@ class TestNaming(IntegrationTestCase):
self.assertEqual(todo.name, f"TODO-{week}-{series}")
def test_expression_autoname_multiple_fields_pattern_without_dot_before_dash(self):
"""
Test Expression naming rule: {field_1}-{field_2}-.#####
Should produce: field_1-field_2-00001
"""
doctype = new_doctype(
fields=[
{"fieldname": "test_field_a", "fieldtype": "Data", "label": "Test Field A"},
{"fieldname": "test_field_b", "fieldtype": "Data", "label": "Test Field B"},
],
autoname="{test_field_a}-{test_field_b}-.#####",
).insert()
test_field_a = "Sumit"
test_field_b = "Jain"
doc = frappe.new_doc(doctype.name)
doc.test_field_a = test_field_a
doc.test_field_b = test_field_b
doc.insert()
series = getseries(f"{test_field_a}-{test_field_b}-", 5)
series = int(series) - 1
self.assertEqual(doc.name, f"{test_field_a}-{test_field_b}-{series:05d}")
doc.delete()
doctype.delete()
def test_expression_autoname_multiple_fields_pattern_with_dots_between_fields(self):
"""
Test Expression naming rule: {field_1}.-.{field_2}.-.#####
Should produce: field_1-field_2-00001 (same as pattern without dots)
"""
doctype = new_doctype(
fields=[
{"fieldname": "test_field_x", "fieldtype": "Data", "label": "Test Field X"},
{"fieldname": "test_field_y", "fieldtype": "Data", "label": "Test Field Y"},
],
autoname="{test_field_x}.-.{test_field_y}.-.#####",
).insert()
test_field_x = "Sumit"
test_field_y = "Jain"
doc = frappe.new_doc(doctype.name)
doc.test_field_x = test_field_x
doc.test_field_y = test_field_y
doc.insert()
series = getseries(f"{test_field_x}-{test_field_y}-", 5)
series = int(series) - 1
self.assertEqual(doc.name, f"{test_field_x}-{test_field_y}-{series:05d}")
doc.delete()
doctype.delete()
def test_revert_series(self):
from datetime import datetime