[FIX] base, web: transfer readonly/required modifiers for kanban/tree

Since odoo/odoo@69c3d5aa25
the readonly and required modifiers were no longer passed
for non-editable views. Which means:
- kanban: never passed,
- tree: passed only when `editable="1"` or `multi_edit="1"`,
- form: always passed.

The rationale behind that is that a view which is considered
as readonly doesn't need to know if its field are readonly or required,
as by definition, if the view is readonly, the user shouldn't
be able to modify anything in the view.
This was to make the views sent to the web client more light-weight,
not sending unused information, and save KB of transfers.

However, some widgets, used in the kanban and non-editable tree,
allow to modify fields, even in readonly considered views,
and therefore need to know these readonly/required modifiers
from the field definition in the Python model.

e.g. the `widget="color"`, odoo/odoo#103478

Ideally, these readonly/required modifiers should be transferred
only when the widget requires it, to avoid transferring the
readonly/required modifiers
when they are not useful in kanban/tree views
(which is 99% of the time).
However, this would require a bigger refactoring,
which is considered too risky compared to the added value
for a stable release.

task-3013110

closes odoo/odoo#106838

X-original-commit: 5a68d254949f1d0ff545023cfd14890efff10f5f
Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
Denis Ledoux
2022-11-30 20:03:15 +01:00
parent 98285b7ee1
commit 59e3269988
4 changed files with 41 additions and 5 deletions
+11 -1
View File
@@ -252,6 +252,7 @@ export class MockServer {
}
const editableView = editable && this._editableNode(doc, modelName);
const onchangeAbleView = this._onchangeAbleView(doc);
const modifiersFromModel = this._modifiersFromModel(doc);
const inTreeView = ["tree", "list"].includes(doc.tagName);
const inFormView = doc.tagName === "form";
// mock _postprocess_access_rights
@@ -286,7 +287,8 @@ export class MockServer {
}
const defaultValues = {};
const stateExceptions = {}; // what is this ?
((editableView && modifiersNames) || ["invisible"]).forEach((attr) => {
modifiersFromModel.forEach((attr) => {
stateExceptions[attr] = [];
defaultValues[attr] = !!field[attr];
});
@@ -496,6 +498,14 @@ export class MockServer {
}
}
_modifiersFromModel(node) {
const modifiersNames = ['invisible'];
if (['kanban', 'tree', 'form'].includes(node.tagName)) {
modifiersNames.push(...['readonly', 'required']);
}
return modifiersNames;
}
/**
* Converts an Object representing a record to actual return Object of the
* python `onchange` method.
@@ -119,6 +119,26 @@ QUnit.module("Fields", (hooks) => {
);
});
QUnit.test("color field read-only in model definition, in non-editable list", async function (assert) {
serverData.models.partner.fields.hex_color.readonly = true;
await makeView({
type: "list",
serverData,
resModel: "partner",
arch: `
<tree>
<field name="hex_color" widget="color" />
</tree>`,
});
assert.containsN(
target,
'.o_field_color input:disabled',
2,
"the field should not be editable"
);
});
QUnit.test("color field change via another field's onchange", async (assert) => {
serverData.models.partner.onchanges = {
foo: (rec) => {
+9 -3
View File
@@ -59,10 +59,9 @@ def att_names(name):
yield f"t-attf-{name}"
def transfer_field_to_modifiers(field, modifiers, view_editable=True):
def transfer_field_to_modifiers(field, modifiers, attributes):
default_values = {}
state_exceptions = {}
attributes = ('invisible', 'readonly', 'required') if view_editable else ('invisible',)
for attr in attributes:
state_exceptions[attr] = []
default_values[attr] = bool(field.get(attr))
@@ -1155,6 +1154,7 @@ actual arch.
root_info = {
'view_type': root.tag,
'view_editable': editable and self._editable_node(root, name_manager),
'view_modifiers_from_model': self._modifiers_from_model(root),
'mobile': options.get('mobile'),
}
@@ -1301,7 +1301,7 @@ actual arch.
field_info = name_manager.field_info.get(node.get('name'))
if field_info:
transfer_field_to_modifiers(field_info, node_info['modifiers'], node_info['view_editable'])
transfer_field_to_modifiers(field_info, node_info['modifiers'], node_info['view_modifiers_from_model'])
def _postprocess_tag_form(self, node, name_manager, node_info):
result = name_manager.model.view_header_get(False, node.tag)
@@ -1382,6 +1382,12 @@ actual arch.
def _onchange_able_view_kanban(self, node):
return True
def _modifiers_from_model(self, node):
modifier_names = ['invisible']
if node.tag in ('kanban', 'tree', 'form'):
modifier_names += ['readonly', 'required']
return modifier_names
#-------------------------------------------------------------------
# view validation
#-------------------------------------------------------------------
+1 -1
View File
@@ -1738,7 +1738,7 @@ class TestViews(ViewCase):
def _test_modifiers(what, expected):
modifiers = {}
if isinstance(what, dict):
transfer_field_to_modifiers(what, modifiers)
transfer_field_to_modifiers(what, modifiers, ['invisible', 'readonly', 'required'])
else:
node = etree.fromstring(what) if isinstance(what, str) else what
transfer_node_to_modifiers(node, modifiers)