[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:
@@ -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) => {
|
||||
|
||||
@@ -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
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user