diff --git a/odoo/addons/base/ir/ir_ui_view.py b/odoo/addons/base/ir/ir_ui_view.py
index 386abda72f8..328f3aca56a 100644
--- a/odoo/addons/base/ir/ir_ui_view.py
+++ b/odoo/addons/base/ir/ir_ui_view.py
@@ -172,6 +172,7 @@ xpath_utils['hasclass'] = _hasclass
TRANSLATED_ATTRS_RE = re.compile(r"@(%s)\b" % "|".join(TRANSLATED_ATTRS))
WRONGCLASS = re.compile(r"(@class\s*=|=\s*@class|contains\(@class)")
+READONLY = re.compile(r"\breadonly\b")
class View(models.Model):
@@ -786,12 +787,16 @@ actual arch.
attrs = {}
field = Model._fields.get(node.get('name'))
if field:
+ editable = self.env.context.get('view_is_editable', True) and self._field_is_editable(field, node)
children = False
views = {}
for f in node:
if f.tag in ('form', 'tree', 'graph', 'kanban', 'calendar'):
node.remove(f)
- xarch, xfields = self.with_context(base_model_name=model).postprocess_and_fields(field.comodel_name, f, view_id)
+ xarch, xfields = self.with_context(
+ base_model_name=model,
+ view_is_editable=editable,
+ ).postprocess_and_fields(field.comodel_name, f, view_id)
views[str(f.tag)] = {
'arch': xarch,
'fields': xfields,
@@ -866,6 +871,17 @@ actual arch.
return arch
+ def _view_is_editable(self, node):
+ """ Return whether the node is an editable view. """
+ return node.tag == 'form' or node.tag == 'tree' and node.get('editable')
+
+ def _field_is_editable(self, field, node):
+ """ Return whether a field is editable (not always readonly). """
+ return (
+ (not field.readonly or READONLY.search(str(field.states or ""))) and
+ (node.get('readonly') != "1" or READONLY.search(node.get('attrs') or ""))
+ )
+
def get_attrs_symbols(self):
""" Return a set of predefined symbols for evaluating attrs. """
return {
@@ -885,10 +901,11 @@ actual arch.
'current_date',
}
- def get_attrs_field_names(self, arch):
+ def get_attrs_field_names(self, arch, model, editable):
""" Retrieve the field names appearing in context, domain and attrs, and
return a list of triples ``(field_name, attr_name, attr_value)``.
"""
+ VIEW_TYPES = {item[0] for item in type(self).type.selection}
symbols = self.get_attrs_symbols() | {None}
result = []
@@ -918,8 +935,17 @@ actual arch.
if isinstance(arg, (tuple, list)):
process_expr(str(arg[0]), get, key, expr)
- def process(node, get=get_name):
+ def process(node, model, editable, get=get_name):
""" traverse `node` and collect triples """
+ if node.tag in VIEW_TYPES:
+ # determine whether this view is editable
+ editable = editable and self._view_is_editable(node)
+ elif node.tag == 'field':
+ # determine whether the field is editable
+ field = model._fields.get(node.get('name'))
+ if field:
+ editable = editable and self._field_is_editable(field, node)
+
for key, val in node.items():
if not val:
continue
@@ -927,13 +953,21 @@ actual arch.
process_expr(val, get, key, val)
elif key == 'attrs':
process_attrs(val, get, key, val)
- if node.tag == 'field':
- # retrieve subfields of 'parent'
- get = partial(get_subname, get)
- for child in node:
- process(child, get)
- process(arch)
+ if node.tag == 'field' and field and field.relational:
+ if editable and not node.get('domain'):
+ domain = field._description_domain(self.env)
+ # process the field's domain as if it was in the view
+ if isinstance(domain, pycompat.string_types):
+ process_expr(domain, get, 'domain', domain)
+ # retrieve subfields of 'parent'
+ model = self.env[field.comodel_name]
+ get = partial(get_subname, get)
+
+ for child in node:
+ process(child, model, editable, get)
+
+ process(arch, model, editable)
return result
@api.model
@@ -974,7 +1008,8 @@ actual arch.
attrs_fields = []
if self.env.context.get('check_field_names'):
- attrs_fields = self.get_attrs_field_names(node)
+ editable = self.env.context.get('view_is_editable', True)
+ attrs_fields = self.get_attrs_field_names(node, Model, editable)
fields_def = self.postprocess(model, node, view_id, False, fields)
if node.tag in ('kanban', 'tree', 'form', 'gantt'):
diff --git a/odoo/addons/base/tests/test_views.py b/odoo/addons/base/tests/test_views.py
index db28cce587e..7a1382cb752 100644
--- a/odoo/addons/base/tests/test_views.py
+++ b/odoo/addons/base/tests/test_views.py
@@ -1106,6 +1106,183 @@ class TestViews(ViewCase):
'arch': arch % ('', '