[IMP] base, web: set can_create/can_write only on editable nodes
Setting the attributes `can_create` and `can_write` in views
which are not editable is useless as you cannot edit the record
in these views.
This allow to gain some KB when serving the view to the web client,
as well as making the view cleaner.
e.g. the graph view in the CRM > My Pipeline menu
Before:
```xml
<graph string="Opportunities" sample="1">
<field name="stage_id" can_create="true" can_write="true"/>
<field name="user_id" on_change="1" can_create="true" can_write="true"/>
<field name="color" modifiers="{"invisible": true}"/>
</graph>
```
After:
```xml
<graph string="Opportunities" sample="1">
<field name="stage_id"/>
<field name="user_id" on_change="1"/>
<field name="color" modifiers="{"invisible": true}"/>
</graph>
```
This revision takes the opportunity to port the `editable` concept
from the server to the web client MockServer,
in order to be able to test the removal of these attributes from the views
in the QUnit tests.
The `_editableNode` JS function added here is the translation of the
existing `def _editable_node` method in `addons/base/models/ir_ui_view.py`
https://github.com/odoo/odoo/blob/f5edde3624f4fe40f87cc7fed7dcfc4bbce1f19f/odoo/addons/base/models/ir_ui_view.py#L1282-L1301
closes odoo/odoo#101902
X-original-commit: f0beddf534a57e972ac3a7bef6afa1caae1dcff3
Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
@@ -234,6 +234,7 @@ export class MockServer {
|
||||
const processedNodes = params.processedNodes || [];
|
||||
const { arch, context, modelName } = params;
|
||||
const level = params.level || 0;
|
||||
const editable = params.editable || true;
|
||||
const fields = deepCopy(params.fields);
|
||||
function isNodeProcessed(node) {
|
||||
return processedNodes.findIndex((n) => n.isSameNode(node)) > -1;
|
||||
@@ -249,6 +250,7 @@ export class MockServer {
|
||||
} else {
|
||||
doc = arch;
|
||||
}
|
||||
const editableView = editable && this._editableNode(doc, modelName);
|
||||
const inTreeView = doc.tagName === "tree";
|
||||
const inFormView = doc.tagName === "form";
|
||||
// mock _postprocess_access_rights
|
||||
@@ -271,7 +273,11 @@ export class MockServer {
|
||||
const isGroupby = node.tagName === "groupby";
|
||||
if (isField) {
|
||||
const fieldName = node.getAttribute("name");
|
||||
fieldNodes[fieldName] = { node, isInvisible: node.getAttribute("invisible") };
|
||||
fieldNodes[fieldName] = {
|
||||
node,
|
||||
isInvisible: node.getAttribute("invisible"),
|
||||
isEditable: editableView && this._editableNode(node, modelName),
|
||||
};
|
||||
// 'transfer_field_to_modifiers' simulation
|
||||
const field = fields[fieldName];
|
||||
if (!field) {
|
||||
@@ -352,9 +358,9 @@ export class MockServer {
|
||||
});
|
||||
Object.keys(fieldNodes).forEach((field) => relatedModels[modelName].add(field));
|
||||
let relModel, relFields;
|
||||
Object.entries(fieldNodes).forEach(([name, { node, isInvisible }]) => {
|
||||
Object.entries(fieldNodes).forEach(([name, { node, isInvisible, isEditable }]) => {
|
||||
const field = fields[name];
|
||||
if (field.type === "many2one" || field.type === "many2many") {
|
||||
if (isEditable && (field.type === "many2one" || field.type === "many2many")) {
|
||||
const canCreate = node.getAttribute("can_create");
|
||||
node.setAttribute("can_create", canCreate || "true");
|
||||
const canWrite = node.getAttribute("can_write");
|
||||
@@ -401,6 +407,7 @@ export class MockServer {
|
||||
context,
|
||||
processedNodes,
|
||||
level: level + 1,
|
||||
editable: editableView,
|
||||
});
|
||||
Object.entries(models).forEach(([modelName, fields]) => {
|
||||
relatedModels[modelName] = relatedModels[modelName] || new Set();
|
||||
@@ -431,6 +438,7 @@ export class MockServer {
|
||||
fields: relFields,
|
||||
context,
|
||||
processedNodes,
|
||||
editable: false,
|
||||
});
|
||||
Object.entries(models).forEach(([modelName, fields]) => {
|
||||
relatedModels[modelName] = relatedModels[modelName] || new Set();
|
||||
@@ -453,6 +461,30 @@ export class MockServer {
|
||||
};
|
||||
}
|
||||
|
||||
_editableNode(node, modelName) {
|
||||
switch (node.tagName) {
|
||||
case "form":
|
||||
return true;
|
||||
case "tree":
|
||||
return !!node.getAttribute("editable");
|
||||
case "field": {
|
||||
const fname = node.getAttribute("name");
|
||||
const field = this.models[modelName].fields[fname];
|
||||
return (
|
||||
(!field.readonly ||
|
||||
(field.states &&
|
||||
Object.values(field.states).some((item) =>
|
||||
item.includes("readonly")
|
||||
))) &&
|
||||
(!["1", "True"].includes(node.getAttribute("readonly")) ||
|
||||
!_.isEmpty(evaluateExpr(node.getAttribute("attrs") || "{}")))
|
||||
);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an Object representing a record to actual return Object of the
|
||||
* python `onchange` method.
|
||||
|
||||
@@ -1251,7 +1251,7 @@ actual arch.
|
||||
self._postprocess_view(
|
||||
child, field.comodel_name, editable=node_info['editable'], parent_name_manager=name_manager,
|
||||
)
|
||||
if field.type in ('many2one', 'many2many'):
|
||||
if node_info['editable'] and field.type in ('many2one', 'many2many'):
|
||||
node.set('model_access_rights', field.comodel_name)
|
||||
|
||||
name_manager.has_field(node, node.get('name'), attrs)
|
||||
|
||||
Reference in New Issue
Block a user