[IMP] web: consider FieldDependencies given on custom widget

With this commit we support adding FieldDependencies on custom widget, consider
FieldDependencies given on custom widget and add it to fieldInfo so that when
modal does calls to server to fetch data it consider those fields while reading

this will let us to design custom widget which may have some other fields in
dependency, say for example weekly recurrence widget which uses sun, mon etc.
fields, so with this we can fetch data of those dependent fields without adding
it to view.

task-2335399

closes odoo/odoo#60277

Related: odoo/upgrade#2021
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
Co-authored-by: Mohammed Shekha <msh@odoo.com>
This commit is contained in:
yograj tandel
2021-03-26 12:25:44 +00:00
committed by Yannick Tivisse
co-authored by Mohammed Shekha
parent 7da9c296eb
commit ef62df2bfd
5 changed files with 119 additions and 32 deletions
-7
View File
@@ -182,13 +182,6 @@
<field name="until" attrs="{'invisible': [('end_type', '!=', 'end_date')], 'required': [('end_type', '=', 'end_date'), ('recurrency', '=', True)]}"/>
</div>
</group>
<field name="mon" invisible="1"/>
<field name="tue" invisible="1"/>
<field name="wed" invisible="1"/>
<field name="thu" invisible="1"/>
<field name="fri" invisible="1"/>
<field name="sat" invisible="1"/>
<field name="sun" invisible="1"/>
<group attrs="{'invisible': [('rrule_type', '!=', 'monthly')]}">
<label string="Day of Month" for="month_by"/>
<div class="o_row">
-7
View File
@@ -750,13 +750,6 @@
<span attrs="{'invisible': ['|', ('repeat_show_week', '=', False), ('repeat_show_month', '=', False)]}">of</span>
<field name="repeat_month" attrs="{'invisible': [('repeat_show_month', '=', False)], 'required': [('repeat_show_month', '=', True)]}" />
</div>
<field name="mon" invisible="1"/>
<field name="tue" invisible="1"/>
<field name="wed" invisible="1"/>
<field name="thu" invisible="1"/>
<field name="fri" invisible="1"/>
<field name="sat" invisible="1"/>
<field name="sun" invisible="1"/>
<label for="repeat_type" />
<div class="o_row">
@@ -18,6 +18,8 @@ var fieldRegistry = require('web.field_registry');
var fieldRegistryOwl = require('web.field_registry_owl');
var pyUtils = require('web.py_utils');
var utils = require('web.utils');
const widgetRegistry = require('web.widget_registry');
const widgetRegistryOwl = require('web.widgetRegistry');
var BasicView = AbstractView.extend({
config: _.extend({}, AbstractView.prototype.config, {
@@ -372,6 +374,28 @@ var BasicView = AbstractView.extend({
* @returns {boolean} false iff subnodes must not be visited.
*/
_processNode: function (node, fv) {
const viewType = fv.type;
const fieldsInfo = fv.fieldsInfo[viewType];
const fields = fv.viewFields;
const _addFieldDependencies = (deps) => {
for (const dependencyName in deps) {
const dependencyDict = { name: dependencyName, type: deps[dependencyName].type };
if (!(dependencyName in fieldsInfo)) {
fieldsInfo[dependencyName] = _.extend({}, dependencyDict, {
options: deps[dependencyName].options || {},
});
}
if (!(dependencyName in fields)) {
fields[dependencyName] = dependencyDict;
}
if (fv.fields && !(dependencyName in fv.fields)) {
fv.fields[dependencyName] = dependencyDict;
}
}
};
if (typeof node === 'string') {
return false;
}
@@ -382,29 +406,27 @@ var BasicView = AbstractView.extend({
node.attrs.options = node.attrs.options ? JSON.parse(node.attrs.options) : {};
}
if (node.tag === 'field') {
var viewType = fv.type;
var fieldsInfo = fv.fieldsInfo[viewType];
var fields = fv.viewFields;
fieldsInfo[node.attrs.name] = this._processField(viewType,
fields[node.attrs.name], node.attrs ? _.clone(node.attrs) : {});
if (fieldsInfo[node.attrs.name].fieldDependencies) {
var deps = fieldsInfo[node.attrs.name].fieldDependencies;
for (var dependency_name in deps) {
var dependency_dict = {name: dependency_name, type: deps[dependency_name].type};
if (!(dependency_name in fieldsInfo)) {
fieldsInfo[dependency_name] = _.extend({}, dependency_dict, {
options: deps[dependency_name].options || {},
});
}
if (!(dependency_name in fields)) {
fields[dependency_name] = dependency_dict;
}
if (fv.fields && !(dependency_name in fv.fields)) {
fv.fields[dependency_name] = dependency_dict;
}
}
_addFieldDependencies(deps);
}
return false;
}
// custom widget may have fieldDependencies so add it to fields of fields_view
if (node.tag === 'widget') {
const Widget = widgetRegistryOwl.get(node.attrs.name) || widgetRegistry.get(node.attrs.name);
const legacy = !(Widget.prototype instanceof owl.Component);
let deps;
if (legacy && Widget.prototype.fieldDependencies) {
deps = Widget.prototype.fieldDependencies;
} else if (Widget.fieldDependencies) {
deps = Widget.fieldDependencies;
}
if (deps) {
_addFieldDependencies(deps);
}
return false;
}
@@ -0,0 +1,78 @@
odoo.define('web.basic_view_tests', function (require) {
"use strict";
const BasicView = require('web.BasicView');
const BasicRenderer = require("web.BasicRenderer");
const testUtils = require('web.test_utils');
const widgetRegistryOwl = require('web.widgetRegistry');
const { xml } = owl.tags;
const createView = testUtils.createView;
QUnit.module('Views', {
beforeEach: function () {
this.data = {
fake_model: {
fields: {},
record: [],
},
foo: {
fields: {
foo: { string: "Foo", type: "char" },
bar: { string: "Bar", type: "boolean" },
},
records: [
{ id: 1, bar: true, foo: "yop" },
{ id: 2, bar: true, foo: "blip" },
]
},
};
},
}, function () {
QUnit.module('BasicView');
QUnit.test('fields given in fieldDependencies of custom widget are loaded', async function (assert) {
assert.expect(1);
const basicView = BasicView.extend({
viewType: "test",
config: Object.assign({}, BasicView.prototype.config, {
Renderer: BasicRenderer,
})
});
class MyWidget extends owl.Component {}
MyWidget.fieldDependencies = {
foo: { type: 'char' },
bar: { type: 'boolean' },
};
MyWidget.template = xml/* xml */`
<div class="custom-widget">Hello World!</div>
`;
widgetRegistryOwl.add('testWidget', MyWidget);
const view = await createView({
View: basicView,
data: this.data,
model: "foo",
arch:
`<test>
<widget name="testWidget"/>
</test>`,
mockRPC: function (route, args) {
if (route === "/web/dataset/search_read") {
assert.deepEqual(args.fields, ["foo", "bar"],
"search_read should be called with dependent fields");
return Promise.resolve();
}
return this._super.apply(this, arguments);
}
});
view.destroy();
delete widgetRegistryOwl.map.testWidget;
});
});
});
+1
View File
@@ -720,6 +720,7 @@
<script type="text/javascript" src="/web/static/tests/views/sample_server_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/abstract_controller_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/abstract_view_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/basic_view_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/form_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/graph_tests.js"></script>
<script type="text/javascript" src="/web/static/tests/views/list_tests.js"></script>