From 6814ced9c96721e3886fb40c4916f41cc693f10a Mon Sep 17 00:00:00 2001 From: Raf Ven Date: Wed, 11 Aug 2021 14:18:33 +0200 Subject: [PATCH] [14.0] [FIX] web_advanced_search: Many2one selection on Filters missing --- .../js/control_panel/custom_filter_item.js | 86 ++++++++++++++ .../static/src/js/relational.js | 111 ++++++++++++++++++ .../static/src/xml/web_advanced_search.xml | 20 +++- web_advanced_search/views/templates.xml | 8 ++ 4 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 web_advanced_search/static/src/js/control_panel/custom_filter_item.js create mode 100644 web_advanced_search/static/src/js/relational.js diff --git a/web_advanced_search/static/src/js/control_panel/custom_filter_item.js b/web_advanced_search/static/src/js/control_panel/custom_filter_item.js new file mode 100644 index 000000000..d592c9fca --- /dev/null +++ b/web_advanced_search/static/src/js/control_panel/custom_filter_item.js @@ -0,0 +1,86 @@ +odoo.define("web_advanced_search.CustomFilterItem", function (require) { + "use strict"; + + const CustomFilterItem = require("web.CustomFilterItem"); + const FieldMany2One = require("web.relational_fields").FieldMany2One; + const Relational = require("web_advanced_search.RelationalOwl"); + const {FIELD_TYPES} = require("web.searchUtils"); + const {useListener} = require("web.custom_hooks"); + + CustomFilterItem.patch("web_advanced_search.CustomFilterItem", (T) => { + class AdvancedCustomFilterItem extends T { + constructor() { + super(...arguments); + this.state.field = false; + this.OPERATORS.relational = this.OPERATORS.char; + this.FIELD_TYPES.many2one = "relational"; + useListener("m2xchange", this._onM2xDataChanged); + } + + _addDefaultCondition() { + super._addDefaultCondition(...arguments); + const condition = this.state.conditions[ + this.state.conditions.length - 1 + ]; + condition.index = _.uniqueId("condition_"); + } + + /** + * @private + * @param {Object} condition + */ + _setDefaultValue(condition) { + const fieldType = this.fields[condition.field].type; + const genericType = FIELD_TYPES[fieldType]; + if (genericType === "relational") { + condition.displayedValue = ""; + } else { + super._setDefaultValue(...arguments); + } + } + + /** + * @private + * @param {Object} condition + * @param {Event} ev + */ + _onFieldSelect(condition, ev) { + super._onFieldSelect(...arguments); + this.state.field = this.fields[ev.target.selectedIndex]; + this.state.fieldindex = ev.target.selectedIndex; + this.state.conditionIndex = condition.index; + } + /** + * @private + * @param {Object} condition + * @param {Event} ev + */ + _onOperatorSelect(condition, ev) { + this.trigger("operatorChange"); + this.state.operator = ev.target[ev.target.selectedIndex].value; + super._onOperatorSelect(...arguments); + } + _onM2xDataChanged(event) { + const fieldindex = this.fields + .map((field) => field.name) + .indexOf(event.detail.field); + const condition = this.state.conditions.filter( + (con) => + con.field === fieldindex && + con.index === this.state.conditionIndex + ); + if (condition.length) { + condition[0].value = event.detail.changes.id; + condition[0].value = event.detail.changes.display_name; + } + } + } + + return AdvancedCustomFilterItem; + }); + // Extends HomeMenuWrapper components + CustomFilterItem.components = Object.assign({}, CustomFilterItem.components, { + FieldMany2One, + Relational, + }); +}); diff --git a/web_advanced_search/static/src/js/relational.js b/web_advanced_search/static/src/js/relational.js new file mode 100644 index 000000000..18c276f7b --- /dev/null +++ b/web_advanced_search/static/src/js/relational.js @@ -0,0 +1,111 @@ +odoo.define("web_advanced_search.RelationalOwl", function (require) { + "use strict"; + + const BasicModel = require("web.BasicModel"); + const patchMixin = require("web.patchMixin"); + const {ComponentAdapter} = require("web.OwlCompatibility"); + const relationalFields = require("web.relational_fields"); + const FieldMany2One = relationalFields.FieldMany2One; + const FieldManagerMixin = require("web.FieldManagerMixin"); + const {useListener} = require("web.custom_hooks"); + /* global owl */ + const {Component} = owl; + const {xml} = owl.tags; + + const AdvancedSearchWidget = FieldMany2One.extend(FieldManagerMixin, { + init: function (parent) { + const field = parent.__owl__.parent.field; + const model = new BasicModel(field.relation); + // Create dummy record with only the field the user is searching + const params = { + fieldNames: [field.name], + modelName: field.relation, + context: field.context, + type: "record", + viewType: "default", + fieldsInfo: { + default: {}, + }, + fields: { + [field.name]: _.omit( + field, + // User needs all records, to actually produce a new domain + "domain", + // Onchanges make no sense in this context, there's no record + "onChange" + ), + }, + }; + if (field.type.endsWith("2many")) { + // X2many fields behave like m2o in the search context + params.fields[field.name].type = "many2one"; + } + params.fieldsInfo.default[field.name] = {}; + // Emulate `model.load()`, without RPC-calling `default_get()` + this.dataPointID = model._makeDataPoint(params).id; + model.generateDefaultValues(this.dataPointID, {}); + this._super(parent, field.name, this._get_record(model), { + mode: "edit", + attrs: { + options: { + no_create_edit: true, + no_create: true, + no_open: true, + no_quick_create: true, + }, + }, + }); + FieldManagerMixin.init.call(this, model); + }, + _get_record: function (model) { + return model.get(this.dataPointID); + }, + /** + * @override + */ + _confirmChange: function (id, fields, event) { + this.trigger_up("m2xchange", { + data: event.data, + changes: event.data.changes[fields[0]], + field: fields[0], + }); + this.dataPointID = id; + return this.reset(this._get_record(this.model), event); + }, + }); + /** + * A search field for relational fields. + * + * It implements and extends the `FieldManagerMixin`, and acts as if it + * were a reduced dummy controller. Some actions "mock" the underlying + * model, since sometimes we use a char widget to fill related fields + * (which is not supported by that widget), and fields need an underlying + * model implementation, which can only hold fake data, given a search view + * has no data on it by definition. + */ + class Relational extends Component { + // eslint-disable-next-line no-unused-vars + constructor(parent, component, props) { + super(...arguments); + this.field = parent.state.field; + this.operator = parent.state.operator; + this.FieldWidget = false; + this.set_widget(); + useListener("operatorChange", this.set_widget); + } + + /** + * @override + */ + set_widget() { + this.FieldWidget = AdvancedSearchWidget; + } + } + + Relational.template = xml` +
+ +
`; + Relational.components = {ComponentAdapter}; + return patchMixin(Relational); +}); diff --git a/web_advanced_search/static/src/xml/web_advanced_search.xml b/web_advanced_search/static/src/xml/web_advanced_search.xml index 431969b5b..5450c945d 100644 --- a/web_advanced_search/static/src/xml/web_advanced_search.xml +++ b/web_advanced_search/static/src/xml/web_advanced_search.xml @@ -1,6 +1,4 @@ - @@ -8,6 +6,24 @@ + + + + + + + + + +