Merge pull request #28363 from frappe/expr-series
fix(NamingExpression): series should be separate for different format expressions instead of global
This commit is contained in:
commit
91d553c9cf
5 changed files with 90 additions and 12 deletions
|
|
@ -1085,6 +1085,12 @@ def validate_series(dt, autoname=None, name=None):
|
|||
df.unique = 1
|
||||
break
|
||||
|
||||
if autoname and autoname.startswith("format:"):
|
||||
from frappe.model.naming import BRACED_PARAMS_HASH_PATTERN
|
||||
|
||||
if len(BRACED_PARAMS_HASH_PATTERN.findall(autoname)) > 1:
|
||||
frappe.throw(_("Only one set of {#} pattern is allowed in the format string"))
|
||||
|
||||
if (
|
||||
autoname
|
||||
and (not autoname.startswith("field:"))
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ if TYPE_CHECKING:
|
|||
|
||||
|
||||
NAMING_SERIES_PATTERN = re.compile(r"^[\w\- \/.#{}]+$", re.UNICODE)
|
||||
BRACED_PARAMS_PATTERN = re.compile(r"(\{[\w | #]+\})")
|
||||
BRACED_PARAMS_WORD_PATTERN = re.compile(r"(\{[\w]+\})")
|
||||
BRACED_PARAMS_HASH_PATTERN = re.compile(r"(\{[#]+\})")
|
||||
|
||||
|
||||
# Types that can be using in naming series fields
|
||||
|
|
@ -314,6 +315,7 @@ def parse_naming_series(
|
|||
doctype=None,
|
||||
doc: Optional["Document"] = None,
|
||||
number_generator: Callable[[str, int], str] | None = None,
|
||||
key: str | None = None,
|
||||
) -> str:
|
||||
"""Parse the naming series and get next name.
|
||||
|
||||
|
|
@ -341,7 +343,10 @@ def parse_naming_series(
|
|||
if e.startswith("#"):
|
||||
if not series_set:
|
||||
digits = len(e)
|
||||
part = number_generator(name, digits)
|
||||
if key:
|
||||
part = number_generator(key, digits)
|
||||
else:
|
||||
part = number_generator(name, digits)
|
||||
series_set = True
|
||||
elif e == "YY":
|
||||
part = today.strftime("%y")
|
||||
|
|
@ -575,11 +580,19 @@ def _format_autoname(autoname: str, doc):
|
|||
first_colon_index = autoname.find(":")
|
||||
autoname_value = autoname[first_colon_index + 1 :]
|
||||
|
||||
def get_param_value_for_match(match):
|
||||
def get_param_value_for_word_match(match):
|
||||
param = match.group()
|
||||
return parse_naming_series([param[1:-1]], doc=doc)
|
||||
|
||||
# Replace braced params with their parsed value
|
||||
name = BRACED_PARAMS_PATTERN.sub(get_param_value_for_match, autoname_value)
|
||||
def get_param_value_for_hash_match(patterned_string: str):
|
||||
def get_param_value(match):
|
||||
param = match.group()
|
||||
key = patterned_string[: patterned_string.find(param)]
|
||||
|
||||
return name
|
||||
return parse_naming_series([param[1:-1]], doc=doc, key=key)
|
||||
|
||||
return get_param_value
|
||||
|
||||
# Replace braced params with their parsed value
|
||||
autoname_value = BRACED_PARAMS_WORD_PATTERN.sub(get_param_value_for_word_match, autoname_value)
|
||||
return BRACED_PARAMS_HASH_PATTERN.sub(get_param_value_for_hash_match(autoname_value), autoname_value)
|
||||
|
|
|
|||
|
|
@ -242,3 +242,4 @@ execute:frappe.db.set_single_value("Workspace Settings", "workspace_setup_comple
|
|||
frappe.patches.v16_0.add_app_launcher_in_navbar_settings
|
||||
frappe.desk.doctype.workspace.patches.update_app
|
||||
frappe.patches.v16_0.move_role_desk_settings_to_user
|
||||
frappe.patches.v16_0.update_expression_series
|
||||
|
|
|
|||
59
frappe/patches/v16_0/update_expression_series.py
Normal file
59
frappe/patches/v16_0/update_expression_series.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
import frappe
|
||||
from frappe.model.naming import (
|
||||
BRACED_PARAMS_WORD_PATTERN,
|
||||
determine_consecutive_week_number,
|
||||
has_custom_parser,
|
||||
)
|
||||
from frappe.query_builder import DocType
|
||||
|
||||
|
||||
def execute():
|
||||
Series = DocType("Series")
|
||||
doctypes = frappe.get_all("DocType", filters={"naming_rule": "expression"}, fields=["name", "autoname"])
|
||||
uniq_exprs = set()
|
||||
|
||||
def get_param_value_for_word_match(doc):
|
||||
def get_param_value(match):
|
||||
e = match.group()[1:-1]
|
||||
creation = doc.creation
|
||||
_sentinel = object()
|
||||
part = ""
|
||||
if e == "YY":
|
||||
part = creation.strftime("%y")
|
||||
elif e == "MM":
|
||||
part = creation.strftime("%m")
|
||||
elif e == "DD":
|
||||
part = creation.strftime("%d")
|
||||
elif e == "YYYY":
|
||||
part = creation.strftime("%Y")
|
||||
elif e == "WW":
|
||||
part = determine_consecutive_week_number(creation)
|
||||
elif e == "timestamp":
|
||||
part = str(creation)
|
||||
elif doc and (e.startswith("{") or doc.get(e, _sentinel) is not _sentinel):
|
||||
e = e.replace("{", "").replace("}", "")
|
||||
part = doc.get(e)
|
||||
elif method := has_custom_parser(e):
|
||||
part = frappe.get_attr(method[0])(doc, e)
|
||||
else:
|
||||
part = e
|
||||
return part
|
||||
|
||||
return get_param_value
|
||||
|
||||
for doctype in doctypes:
|
||||
if "#" in doctype.autoname:
|
||||
docs = frappe.get_all(doctype.name)
|
||||
if docs:
|
||||
for doc in docs:
|
||||
_doc = frappe.get_doc(doctype.name, doc.name)
|
||||
expr = doctype.autoname[7 : doctype.autoname.find("{#")]
|
||||
key = BRACED_PARAMS_WORD_PATTERN.sub(get_param_value_for_word_match(_doc), expr)
|
||||
uniq_exprs.add(key)
|
||||
|
||||
current = (frappe.qb.from_(Series).select("*").where(Series.name == "")).run(as_dict=True)
|
||||
if current:
|
||||
current = current[0].get("current")
|
||||
|
||||
for uniq_expr in uniq_exprs:
|
||||
(frappe.qb.into(Series).columns("name", "current").insert(uniq_expr, current + 1)).run()
|
||||
|
|
@ -103,7 +103,8 @@ class TestNaming(IntegrationTestCase):
|
|||
doc.some_fieldname = description
|
||||
doc.insert()
|
||||
|
||||
series = getseries("", 2)
|
||||
series = getseries(f"TODO-{now_datetime().strftime('%m')}-{description}-", 2)
|
||||
|
||||
series = int(series) - 1
|
||||
|
||||
self.assertEqual(doc.name, f"TODO-{now_datetime().strftime('%m')}-{description}-{series:02}")
|
||||
|
|
@ -117,7 +118,7 @@ class TestNaming(IntegrationTestCase):
|
|||
doc.field = field
|
||||
doc.insert()
|
||||
|
||||
series = getseries("", 2)
|
||||
series = getseries(f"TODO-{field}-", 2)
|
||||
series = int(series) - 1
|
||||
|
||||
self.assertEqual(doc.name, f"TODO-{field}-{series:02}")
|
||||
|
|
@ -138,15 +139,13 @@ class TestNaming(IntegrationTestCase):
|
|||
todo.description = description
|
||||
todo.insert()
|
||||
|
||||
series = getseries("", 2)
|
||||
|
||||
week = determine_consecutive_week_number(now_datetime())
|
||||
series = getseries(f"TODO-{week}-", 2)
|
||||
series = str(int(series) - 1)
|
||||
|
||||
if len(series) < 2:
|
||||
series = "0" + series
|
||||
|
||||
week = determine_consecutive_week_number(now_datetime())
|
||||
|
||||
self.assertEqual(todo.name, f"TODO-{week}-{series}")
|
||||
|
||||
def test_revert_series(self):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue