diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 0ce6704c39..49a58da314 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -1,6 +1,7 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE +import datetime import re from typing import TYPE_CHECKING, Callable, Optional @@ -23,6 +24,17 @@ NAMING_SERIES_PATTERN = re.compile(r"^[\w\- \/.#{}]+$", re.UNICODE) BRACED_PARAMS_PATTERN = re.compile(r"(\{[\w | #]+\})") +# Types that can be using in naming series fields +NAMING_SERIES_PART_TYPES = ( + int, + str, + datetime.datetime, + datetime.date, + datetime.time, + datetime.timedelta, +) + + class InvalidNamingSeriesError(frappe.ValidationError): pass @@ -298,6 +310,9 @@ def parse_naming_series( series_set = False today = now_datetime() for e in parts: + if not e: + continue + part = "" if e.startswith("#"): if not series_set: @@ -320,14 +335,16 @@ def parse_naming_series( part = frappe.defaults.get_user_default("fiscal_year") elif e.startswith("{") and doc: e = e.replace("{", "").replace("}", "") - part = (cstr(doc.get(e)) or "").strip() + part = doc.get(e) elif doc and doc.get(e): - part = (cstr(doc.get(e)) or "").strip() + part = doc.get(e) else: part = e if isinstance(part, str): name += part + elif isinstance(part, NAMING_SERIES_PART_TYPES): + name += cstr(part).strip() return name diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index 5feaad6f9b..9f5b46a4d1 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -9,6 +9,7 @@ from frappe.model.naming import ( append_number_if_name_exists, determine_consecutive_week_number, getseries, + parse_naming_series, revert_series_if_last, ) from frappe.tests.utils import FrappeTestCase @@ -342,6 +343,32 @@ class TestNaming(FrappeTestCase): name.startswith("KOOH-on_update"), f"incorrect name generated {name}, missing field value" ) + def test_naming_with_empty_part(self): + # check naming with empty part (duplicate dots) + + webhook = frappe.new_doc("Webhook") + webhook.webhook_docevent = "on_update" + + series = "KOOH-..{webhook_docevent}.-.####" + + name = parse_naming_series(series, doc=webhook) + self.assertTrue( + name.startswith("KOOH-on_update"), f"incorrect name generated {name}, missing field value" + ) + + def test_naming_with_unsupported_part(self): + # check naming with empty part (duplicate dots) + + webhook = frappe.new_doc("Webhook") + webhook.webhook_docevent = {"dict": "not supported"} + + series = "KOOH-..{webhook_docevent}.-.####" + + name = parse_naming_series(series, doc=webhook) + self.assertTrue( + name.startswith("KOOH-"), f"incorrect name generated {name}, missing field value" + ) + def make_invalid_todo(): frappe.get_doc({"doctype": "ToDo", "description": "Test"}).insert(set_name="ToDo")