So, I am trying to use your fix.
The client side works after the tweak noticed by @iMoshi .
Alas, the server side fails with:
04:57:40 web.1 | Traceback (most recent call last):
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/desk/form/save.py", line 21, in savedocs
04:57:40 web.1 | doc.save()
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/model/document.py", line 281, in save
04:57:40 web.1 | return self._save(*args, **kwargs)
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/model/document.py", line 319, in _save
04:57:40 web.1 | self._validate()
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/model/document.py", line 489, in _validate
04:57:40 web.1 | self._validate_mandatory()
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/model/document.py", line 778, in _validate_mandatory
04:57:40 web.1 | raise frappe.MandatoryError('[{doctype}, {name}]: {fields}'.format(
04:57:40 web.1 | frappe.exceptions.MandatoryError: [Returnable, RTN-ITM000003]: from_customer
04:57:40 web.1 |
04:57:40 web.1 | Traceback (most recent call last):
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/app.py", line 64, in application
04:57:40 web.1 | response = frappe.api.handle()
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/api.py", line 58, in handle
04:57:40 web.1 | return frappe.handler.handle()
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/handler.py", line 30, in handle
04:57:40 web.1 | data = execute_cmd(cmd)
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/handler.py", line 69, in execute_cmd
04:57:40 web.1 | return frappe.call(method, **frappe.form_dict)
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/__init__.py", line 1086, in call
04:57:40 web.1 | return fn(*args, **newargs)
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/desk/form/save.py", line 21, in savedocs
04:57:40 web.1 | doc.save()
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/model/document.py", line 281, in save
04:57:40 web.1 | return self._save(*args, **kwargs)
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/model/document.py", line 319, in _save
04:57:40 web.1 | self._validate()
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/model/document.py", line 489, in _validate
04:57:40 web.1 | self._validate_mandatory()
04:57:40 web.1 | File "/home/erpdev/frappe-bench-DYPW/apps/frappe/frappe/model/document.py", line 778, in _validate_mandatory
04:57:40 web.1 | raise frappe.MandatoryError('[{doctype}, {name}]: {fields}'.format(
04:57:40 web.1 | frappe.exceptions.MandatoryError: [Returnable, RTN-ITM000003]: from_customer
Here’s the code for _validate_mandatory
:
def _validate_mandatory(self):
if self.flags.ignore_mandatory:
return
missing = self._get_missing_mandatory_fields()
for d in self.get_all_children():
missing.extend(d._get_missing_mandatory_fields())
if not missing:
return
for fieldname, msg in missing:
msgprint(msg)
if frappe.flags.print_messages:
print(self.as_json().encode("utf-8"))
raise frappe.MandatoryError('[{doctype}, {name}]: {fields}'.format(
fields=", ".join((each[0] for each in missing)),
doctype=self.doctype,
name=self.name))
As you can see the Doc collects any missing mandatory fields for itself and then for each child Doc, by calling _get_missing_mandatory_fields
on each of them. Here’s the code for _get_missing_mandatory_fields
:
def _get_missing_mandatory_fields(self):
"""Get mandatory fields that do not have any values"""
def get_msg(df):
if df.fieldtype in table_fields:
return "{}: {}: {}".format(_("Error"), _("Data missing in table"), _(df.label))
elif self.parentfield:
return "{}: {} {} #{}: {}: {}".format(_("Error"), frappe.bold(_(self.doctype)),
_("Row"), self.idx, _("Value missing for"), _(df.label))
else:
return _("Error: Value missing for {0}: {1}").format(_(df.parent), _(df.label))
missing = []
for df in self.meta.get("fields", {"reqd": ('=', 1)}):
if self.get(df.fieldname) in (None, []) or not strip_html(cstr(self.get(df.fieldname))).strip():
missing.append((df.fieldname, get_msg(df)))
# check for missing parent and parenttype
if self.meta.istable:
for fieldname in ("parent", "parenttype"):
if not self.get(fieldname):
missing.append((fieldname, get_msg(frappe._dict(label=fieldname))))
return missing
Specifically, any field, where reqd
is set, must have data, or it is thrown on the missing
pile:
for df in self.meta.get("fields", {"reqd": ('=', 1)}):
if self.get(df.fieldname) in (None, []) or not strip_html(cstr(self.get(df.fieldname))).strip():
missing.append((df.fieldname, get_msg(df)))
I have yet to dig deeper into the code but I can see that the model
never even tries to work with mandatory_depends_on
:
erpdev@erpserver:~/frappe-bench/apps/frappe/frappe/model$ grep -R mandatory_depends_on
erpdev@erpserver:~/frappe-bench/apps/frappe/frappe/model$
I find this hard to understand, since, while there are literally dozens of places that set the value of mandatory_depends_on
, there doesn’t seem to be any code anywhere that actually handles it:
erpdev@erpserver:~/frappe-bench/apps$ grep --include="*.py" -R mandatory_depends_on;
frappe/frappe/custom/doctype/customize_form/customize_form.py: 'mandatory_depends_on': 'Data',
frappe/frappe/core/doctype/doctype/test_doctype.py: "ifnull(mandatory_depends_on, '')": ("!=", ''),
frappe/frappe/core/doctype/doctype/test_doctype.py: fields=["parent", "depends_on", "collapsible_depends_on", "mandatory_depends_on",\
frappe/frappe/core/doctype/doctype/test_doctype.py: for depends_on in ["depends_on", "collapsible_depends_on", "mandatory_depends_on", "read_only_depends_on"]:
frappe/frappe/core/doctype/doctype/doctype.py: depends_on_fields = ["depends_on", "collapsible_depends_on", "mandatory_depends_on", "read_only_depends_on"]
erpdev@erpserver:~/frappe-bench/apps$
My questions:
- Am I using the wrong code version?
- Is this how the code is expected to be from now on?
- if so: will
mandatory_depends_on
be deprecated?
Platform:
erpdev@erpserver:~/frappe-bench$ lsb_release -d
Description: Ubuntu 20.04.1 LTS
erpdev@erpserver:~/frappe-bench$ bench --version
5.2.1
erpdev@erpserver:~/frappe-bench$ bench version
erpnext 13.0.0-beta.4
frappe 13.0.0-beta.5
erpdev@erpserver:~$