Browse Source
[MIG][REF] web_advanced_search: Rename, refactor, migrate
[MIG][REF] web_advanced_search: Rename, refactor, migrate
* Complete migration to v11 * Refactor to use the new v11 decoupled widgets system * Advanced search is now a high-level feature from the filters menu; it simplifies code a lot, and the UX is even better * Split README system * Add fun to ROADMAP * Addon is renamed to web_advanced_search, since it enhaces the searching experience for all kind of fields nowpull/1197/head
Jairo Llopis
6 years ago
committed by
Pedro M. Baeza
16 changed files with 462 additions and 241 deletions
-
7web_advanced_search/README.rst
-
3web_advanced_search/__init__.py
-
13web_advanced_search/__manifest__.py
-
0web_advanced_search/i18n/web_advanced_search.pot
-
5web_advanced_search/readme/CONTRIBUTORS.rst
-
1web_advanced_search/readme/DESCRIPTION.rst
-
14web_advanced_search/readme/ROADMAP.rst
-
21web_advanced_search/readme/USAGE.rst
-
13web_advanced_search/static/src/css/web_advanced_search.less
-
27web_advanced_search/static/src/css/web_advanced_search_x2x.less
-
86web_advanced_search/static/src/js/human_domain.js
-
293web_advanced_search/static/src/js/web_advanced_search.js
-
185web_advanced_search/static/src/js/web_advanced_search_x2x.js
-
13web_advanced_search/static/src/xml/web_advanced_search.xml
-
13web_advanced_search/static/src/xml/web_advanced_search_x2x.xml
-
9web_advanced_search/views/templates.xml
@ -1,3 +0,0 @@ |
|||||
# -*- coding: utf-8 -*- |
|
||||
# Copyright 2015 Therp BV <http://therp.nl> |
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|
@ -1,24 +1,27 @@ |
|||||
# Copyright 2015 Therp BV <http://therp.nl> |
# Copyright 2015 Therp BV <http://therp.nl> |
||||
# Copyright 2017 Tecnativa - Vicent Cubells |
# Copyright 2017 Tecnativa - Vicent Cubells |
||||
|
# Copyright 2018 Tecnativa - Jairo Llopis |
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
||||
|
|
||||
{ |
{ |
||||
"name": "Search x2x fields", |
|
||||
|
"name": "Advanced search", |
||||
"version": "11.0.1.0.0", |
"version": "11.0.1.0.0", |
||||
"author": "Therp BV, " |
"author": "Therp BV, " |
||||
"Tecnativa, " |
"Tecnativa, " |
||||
"Odoo Community Association (OCA)", |
"Odoo Community Association (OCA)", |
||||
"license": "AGPL-3", |
"license": "AGPL-3", |
||||
"category": "Usability", |
"category": "Usability", |
||||
"summary": "Use a search widget in advanced search for x2x fields", |
|
||||
"depends": [], |
|
||||
|
"summary": "Easier and more powerful searching tools", |
||||
|
"website": "https://github.com/OCA/web", |
||||
|
"depends": [ |
||||
|
'web', |
||||
|
], |
||||
"data": [ |
"data": [ |
||||
'views/templates.xml', |
'views/templates.xml', |
||||
], |
], |
||||
"qweb": [ |
"qweb": [ |
||||
'static/src/xml/web_advanced_search_x2x.xml', |
|
||||
|
'static/src/xml/web_advanced_search.xml', |
||||
], |
], |
||||
"auto_install": False, |
|
||||
"installable": True, |
"installable": True, |
||||
"application": False, |
"application": False, |
||||
} |
} |
@ -0,0 +1,5 @@ |
|||||
|
* Holger Brunn <hbrunn@therp.nl> |
||||
|
* Vicent Cubells <vicent.cubells@tecnativa.com> |
||||
|
* Jairo Llopis <jairo.llopis@tecnativa.com> |
||||
|
* Rami Alwafaie <rami.alwafaie@initos.com> |
||||
|
* Jose Mª Bernet <josemaria.bernet@guadaltech.es> |
@ -0,0 +1 @@ |
|||||
|
More powerful and easy to use search, especially for related fields. |
@ -0,0 +1,14 @@ |
|||||
|
Improvements to the ``domain`` widget, not exclusively related to this addon: |
||||
|
|
||||
|
* Use relational widgets when filtering a relational field |
||||
|
* Allow to filter field names |
||||
|
|
||||
|
Improvements to the search view in this addon: |
||||
|
|
||||
|
* Use widgets ``one2many_tags`` when searching ``one2many`` fields |
||||
|
* Use widgets ``many2many_tags`` when searching ``many2many`` fields |
||||
|
* Allow to edit current full search using the advanced domain editor |
||||
|
* Allow to edit individually any facet from current search using the |
||||
|
advanced domain editor |
||||
|
* Beautiful, human-readable, domain representation when adding an |
||||
|
advanced filter |
@ -0,0 +1,21 @@ |
|||||
|
To use this module, you need to: |
||||
|
|
||||
|
* Open *Filters* in a search view |
||||
|
* Select any relational field |
||||
|
* Select operator `is equal to` or `is not equal to` |
||||
|
* The text field changes to a relational selection field where you |
||||
|
can search for the record in question |
||||
|
* Click *Apply* |
||||
|
|
||||
|
To search for properties of linked records (ie invoices for customers |
||||
|
with a credit limit higher than X): |
||||
|
|
||||
|
* Open *Filters* in a search view |
||||
|
* Select *Add Advanced Filter* |
||||
|
* Edit the advanced filter |
||||
|
* Click *Save* |
||||
|
|
||||
|
Note that you can stack searching for properties: Simply add another |
||||
|
advanced search in the selection search window. You can do |
||||
|
this indefinetely, so it is possible to search for moves belonging |
||||
|
to a journal which has a user who is member of a certain group etc. |
@ -0,0 +1,13 @@ |
|||||
|
.o_search_options { |
||||
|
.o_filters_menu { |
||||
|
.o_filter_condition { |
||||
|
max-width: inherit; |
||||
|
|
||||
|
.o_searchview_extended_prop_value { |
||||
|
.o_field_domain { |
||||
|
min-width: 30vw; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -1,27 +0,0 @@ |
|||||
.o_search_options { |
|
||||
|
|
||||
.o_filters_menu { |
|
||||
.o_filter_condition { |
|
||||
max-width: inherit; |
|
||||
|
|
||||
.o_searchview_extended_prop_value { |
|
||||
.ui-autocomplete-input { |
|
||||
.form-control(); |
|
||||
} |
|
||||
|
|
||||
.oe_m2o_drop_down_button { |
|
||||
top: 6px; |
|
||||
right: 2px; |
|
||||
} |
|
||||
|
|
||||
.o_form_field_domain { |
|
||||
min-width: 400px; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.x2x_container { |
|
||||
min-width: 60ex; |
|
||||
} |
|
@ -0,0 +1,86 @@ |
|||||
|
/* Copyright 2018 Tecnativa - Jairo Llopis |
||||
|
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
||||
|
|
||||
|
odoo.define("web_advanced_search.human_domain", function (require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var DomainSelector = require("web.DomainSelector"); |
||||
|
|
||||
|
var join_mapping = { |
||||
|
"&": _(" and "), |
||||
|
"|": _(" or "), |
||||
|
"!": _(" is not "), |
||||
|
}; |
||||
|
|
||||
|
// HACK I should extend classes, but they are not exposed
|
||||
|
// TODO Remove file when merged https://github.com/odoo/odoo/pull/25922
|
||||
|
var human_domain_methods = { |
||||
|
DomainTree: function () { |
||||
|
var human_domains = []; |
||||
|
_.each(this.children, function (child) { |
||||
|
human_domains.push( |
||||
|
human_domain_methods[child.template].apply(child) |
||||
|
); |
||||
|
}); |
||||
|
return _.str.sprintf( |
||||
|
"(%s)", |
||||
|
human_domains.join(join_mapping[this.operator]) |
||||
|
); |
||||
|
}, |
||||
|
|
||||
|
DomainSelector: function () { |
||||
|
var result = human_domain_methods.DomainTree.apply(this, arguments); |
||||
|
// Remove surrounding parenthesis
|
||||
|
return result.slice(1, -1); |
||||
|
}, |
||||
|
|
||||
|
DomainLeaf: function () { |
||||
|
var chain = [], |
||||
|
operator = this.operator_mapping[this.operator], |
||||
|
value = _.str.sprintf('"%s"', this.value); |
||||
|
// Humanize chain
|
||||
|
this.chain.split(".").forEach(function (element, index) { |
||||
|
chain.push( |
||||
|
_.findWhere( |
||||
|
this.fieldSelector.pages[index], |
||||
|
{name: element} |
||||
|
).string || element |
||||
|
); |
||||
|
}, this); |
||||
|
// Special beautiness for some values
|
||||
|
if (this.operator === "=" && _.isBoolean(this.value)) { |
||||
|
operator = this.operator_mapping[this.value ? "set" : "not set"]; |
||||
|
value = ""; |
||||
|
} else if (_.isArray(this.value)) { |
||||
|
value = _.str.sprintf('["%s"]', this.value.join('", "')); |
||||
|
} |
||||
|
return _.str.sprintf( |
||||
|
"%s %s %s", |
||||
|
chain.join("→"), |
||||
|
operator || this.operator, |
||||
|
value |
||||
|
).trim(); |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
function getHumanDomain (parent, model, domain, options) { |
||||
|
var domain_selector = new DomainSelector( |
||||
|
parent, |
||||
|
model, |
||||
|
domain, |
||||
|
options |
||||
|
); |
||||
|
var dummy_parent = $("<div>"); |
||||
|
domain_selector.appendTo(dummy_parent); |
||||
|
var result = human_domain_methods.DomainSelector.apply( |
||||
|
domain_selector |
||||
|
); |
||||
|
domain_selector.destroy(); |
||||
|
dummy_parent.destroy(); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
getHumanDomain: getHumanDomain, |
||||
|
}; |
||||
|
}); |
@ -0,0 +1,293 @@ |
|||||
|
/* Copyright 2015 Therp BV <http://therp.nl> |
||||
|
* Copyright 2017-2018 Jairo Llopis <jairo.llopis@tecnativa.com> |
||||
|
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
||||
|
|
||||
|
odoo.define("web_advanced_search", function (require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var core = require("web.core"); |
||||
|
var Domain = require("web.Domain"); |
||||
|
var DomainSelectorDialog = require("web.DomainSelectorDialog"); |
||||
|
var field_registry = require("web.field_registry"); |
||||
|
var FieldManagerMixin = require("web.FieldManagerMixin"); |
||||
|
var FilterMenu = require("web.FilterMenu"); |
||||
|
var human_domain = require("web_advanced_search.human_domain"); |
||||
|
var SearchView = require("web.SearchView"); |
||||
|
var Widget = require("web.Widget"); |
||||
|
var Char = core.search_filters_registry.get("char"); |
||||
|
|
||||
|
SearchView.include({ |
||||
|
custom_events: _.extend({}, SearchView.prototype.custom_events, { |
||||
|
"get_dataset": "_on_get_dataset", |
||||
|
}), |
||||
|
|
||||
|
/** |
||||
|
* Add or update a `dataset` attribute in event target |
||||
|
* |
||||
|
* The search view dataset includes things such as the model, which |
||||
|
* is required to make some parts of search views smarter. |
||||
|
* |
||||
|
* @param {OdooEvent} event The target will get the dataset. |
||||
|
*/ |
||||
|
_on_get_dataset: function (event) { |
||||
|
event.target.dataset = this.dataset; |
||||
|
event.stopPropagation(); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* An almost dummy search proposition, to use with domain widget |
||||
|
*/ |
||||
|
var AdvancedSearchProposition = Widget.extend({ |
||||
|
init: function (parent, model, domain) { |
||||
|
this._super(parent); |
||||
|
this.model = model; |
||||
|
this.domain = new Domain(domain); |
||||
|
}, |
||||
|
|
||||
|
get_filter: function () { |
||||
|
var domain_array = this.domain.toArray(); |
||||
|
return { |
||||
|
attrs: { |
||||
|
domain: domain_array, |
||||
|
// TODO Remove when merged
|
||||
|
// https://github.com/odoo/odoo/pull/25922
|
||||
|
string: human_domain.getHumanDomain( |
||||
|
this, |
||||
|
this.model, |
||||
|
domain_array |
||||
|
), |
||||
|
}, |
||||
|
children: [], |
||||
|
tag: "filter", |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
// Add advanced search features
|
||||
|
FilterMenu.include({ |
||||
|
custom_events: _.extend({}, FilterMenu.prototype.custom_events, { |
||||
|
"domain_selected": "advanced_search_commit", |
||||
|
}), |
||||
|
|
||||
|
events: _.extend({}, FilterMenu.prototype.events, { |
||||
|
"click .o_add_advanced_search": "advanced_search_open", |
||||
|
}), |
||||
|
|
||||
|
init: function () { |
||||
|
this._super.apply(this, arguments); |
||||
|
this.trigger_up("get_dataset"); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Open advanced search dialog |
||||
|
* |
||||
|
* @returns {$.Deferred} The opening dialog itself. |
||||
|
*/ |
||||
|
advanced_search_open: function () { |
||||
|
var domain_selector_dialog = new DomainSelectorDialog( |
||||
|
this, |
||||
|
this.dataset.model, |
||||
|
"[]", |
||||
|
{ |
||||
|
debugMode: core.debug, |
||||
|
readonly: false, |
||||
|
} |
||||
|
); |
||||
|
// Add 1st domain node by default
|
||||
|
domain_selector_dialog.domainSelector._onAddFirstButtonClick(); |
||||
|
return domain_selector_dialog.open(); |
||||
|
}, |
||||
|
|
||||
|
/** |
||||
|
* Apply advanced search on dialog save |
||||
|
* |
||||
|
* @param {OdooEvent} event A `domain_selected` event from the dialog. |
||||
|
*/ |
||||
|
advanced_search_commit: function (event) { |
||||
|
_.invoke(this.propositions, "destroy"); |
||||
|
var proposition = new AdvancedSearchProposition( |
||||
|
this, |
||||
|
this.dataset.model, |
||||
|
event.data.domain |
||||
|
); |
||||
|
this.propositions = [proposition]; |
||||
|
this.commit_search(); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 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. |
||||
|
*/ |
||||
|
var Relational = Char.extend(FieldManagerMixin, { |
||||
|
tagName: "div", |
||||
|
className: "x2x_container", |
||||
|
attributes: {}, |
||||
|
|
||||
|
init: function () { |
||||
|
this._super.apply(this, arguments); |
||||
|
// To make widgets work, we need a model and an empty record
|
||||
|
FieldManagerMixin.init.call(this); |
||||
|
this.trigger_up("get_dataset"); |
||||
|
// Make equal and not equal appear 1st and 2nd
|
||||
|
this.operators = _.sortBy( |
||||
|
this.operators, |
||||
|
function(op) { |
||||
|
switch(op.value) { |
||||
|
case "=": |
||||
|
return -2; |
||||
|
case "!=": |
||||
|
return -1; |
||||
|
default: |
||||
|
return 0; |
||||
|
} |
||||
|
}); |
||||
|
// Create dummy record with only the field the user is searching
|
||||
|
var params = { |
||||
|
fieldNames: [this.field.name], |
||||
|
modelName: this.dataset.model, |
||||
|
context: this.dataset.context, |
||||
|
// res_id: "virtual_0",
|
||||
|
fields: {}, |
||||
|
type: "record", |
||||
|
viewType: "default", |
||||
|
fieldsInfo: { |
||||
|
default: {}, |
||||
|
}, |
||||
|
}; |
||||
|
// See https://stackoverflow.com/a/11508530/1468388
|
||||
|
params.fields[this.field.name] = _.omit(this.field, "onChange"); |
||||
|
params.fieldsInfo.default[this.field.name] = {}; |
||||
|
// Emulate `model.load()`, without RPC-calling `default_get()`
|
||||
|
this.datapoint_id = this.model._makeDataPoint(params).id; |
||||
|
this.model.applyDefaultValues( |
||||
|
this.datapoint_id, |
||||
|
{}, |
||||
|
params.fieldNames |
||||
|
); |
||||
|
// To generate a new fake ID
|
||||
|
this._fake_id = -1; |
||||
|
}, |
||||
|
|
||||
|
start: function () { |
||||
|
var result = this._super.apply(this, arguments); |
||||
|
// Render the initial widget
|
||||
|
result.done($.proxy(this, "show_inputs", $("<input value='='/>"))); |
||||
|
return result; |
||||
|
}, |
||||
|
|
||||
|
destroy: function () { |
||||
|
if (this._field_widget) { |
||||
|
this._field_widget.destroy(); |
||||
|
} |
||||
|
this.model.destroy(); |
||||
|
delete this.record; |
||||
|
return this._super.apply(this, arguments); |
||||
|
}, |
||||
|
|
||||
|
_get_record: function () { |
||||
|
return this.model.get(this.datapoint_id); |
||||
|
}, |
||||
|
|
||||
|
show_inputs: function ($operator) { |
||||
|
// Get widget class to be used
|
||||
|
switch ($operator.val()) { |
||||
|
case "=": |
||||
|
case "!=": |
||||
|
this._field_widget_name = "many2one"; |
||||
|
break; |
||||
|
default: |
||||
|
this._field_widget_name = "char"; |
||||
|
} |
||||
|
var _Widget = field_registry.get(this._field_widget_name); |
||||
|
// Destroy previous widget, if any
|
||||
|
if (this._field_widget) { |
||||
|
this._field_widget.destroy(); |
||||
|
delete this._field_widget; |
||||
|
} |
||||
|
// Create new widget
|
||||
|
var options = { |
||||
|
mode: "edit", |
||||
|
attrs: { |
||||
|
options: { |
||||
|
no_create_edit: true, |
||||
|
no_create: true, |
||||
|
no_open: true, |
||||
|
no_quick_create: true, |
||||
|
}, |
||||
|
}, |
||||
|
}; |
||||
|
this._field_widget = new _Widget( |
||||
|
this, |
||||
|
this.field.name, |
||||
|
this._get_record(), |
||||
|
options |
||||
|
); |
||||
|
this._field_widget.appendTo(this.$el); |
||||
|
return this._super.apply(this, arguments); |
||||
|
}, |
||||
|
|
||||
|
_applyChanges: function (dataPointID, changes, event) { |
||||
|
// Make char updates look like valid x2one updates
|
||||
|
if (_.isNaN(changes[this.field.name].id)) { |
||||
|
changes[this.field.name] = { |
||||
|
id: this._fake_id--, |
||||
|
display_name: event.target.lastSetValue, |
||||
|
}; |
||||
|
} |
||||
|
return FieldManagerMixin._applyChanges.apply(this, arguments); |
||||
|
}, |
||||
|
|
||||
|
_confirmChange: function (id, fields, event) { |
||||
|
this.datapoint_id = id; |
||||
|
return this._field_widget.reset(this._get_record(), event); |
||||
|
}, |
||||
|
|
||||
|
get_value: function () { |
||||
|
try { |
||||
|
switch (this._field_widget_name) { |
||||
|
case "many2one": |
||||
|
return this._field_widget.value.res_id; |
||||
|
default: |
||||
|
return this._field_widget.value.data.display_name; |
||||
|
} |
||||
|
} catch (error) { |
||||
|
if (error.name === "TypeError") { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
toString: function () { |
||||
|
try { |
||||
|
switch (this._field_widget_name) { |
||||
|
case "many2one": |
||||
|
return this._field_widget.value.data.display_name; |
||||
|
} |
||||
|
return this._super.apply(this, arguments); |
||||
|
} catch (error) { |
||||
|
if (error.name === "TypeError") { |
||||
|
return ""; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
// Register search filter widgets
|
||||
|
core.search_filters_registry |
||||
|
.add("many2many", Relational) |
||||
|
.add("many2one", Relational) |
||||
|
.add("one2many", Relational); |
||||
|
|
||||
|
return { |
||||
|
AdvancedSearchProposition: AdvancedSearchProposition, |
||||
|
Relational: Relational, |
||||
|
}; |
||||
|
}); |
@ -1,185 +0,0 @@ |
|||||
/* Copyright 2015 Therp BV <http://therp.nl> |
|
||||
* Copyright 2017 Jairo Llopis <jairo.llopis@tecnativa.com> |
|
||||
* Copyright 2018 Jose Mª Bernet <josemaria.bernet@guadaltech.es> |
|
||||
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
|
|
||||
|
|
||||
odoo.define('web_advanced_search_x2x', function (require) { |
|
||||
"use strict"; |
|
||||
|
|
||||
var core = require('web.core'); |
|
||||
var DomainSelector = require('web.DomainSelector'); |
|
||||
var Domain = require("web.Domain"); |
|
||||
var FieldManagerMixin = require('web.FieldManagerMixin'); |
|
||||
var Char = core.search_filters_registry.get("char"); |
|
||||
|
|
||||
var X2XAdvancedSearchPropositionMixin = { |
|
||||
template: "web_advanced_search_x2x.proposition", |
|
||||
events: { |
|
||||
// If click on the node add or delete button, notify the parent and let
|
|
||||
// it handle the addition/removal
|
|
||||
"click .o_domain_tree_operator_caret": "_openCaret" |
|
||||
}, |
|
||||
|
|
||||
_openCaret: function (e) { |
|
||||
var selectorClass = $('.o_domain_tree_operator_selector'); |
|
||||
if (selectorClass.hasClass('open')) { |
|
||||
selectorClass.removeClass('open'); |
|
||||
} else { |
|
||||
selectorClass.addClass('open'); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
init: function (parent, options) { |
|
||||
// Make equal and not equal appear 1st and 2nd
|
|
||||
this.relation = options.relation; |
|
||||
this.type = options.type; |
|
||||
this.field_name = options.name; |
|
||||
this.name = parent.name; |
|
||||
|
|
||||
this.operators = _.sortBy( |
|
||||
this.operators, |
|
||||
function (op) { |
|
||||
switch (op.value) { |
|
||||
case '=': |
|
||||
return -2; |
|
||||
case '!=': |
|
||||
return -1; |
|
||||
default: |
|
||||
return 0; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// Append domain operator
|
|
||||
this.operators.push({ |
|
||||
'value': 'domain', 'text': core._lt('is in selection'), |
|
||||
}); |
|
||||
// Avoid hiding filter when using special widgets
|
|
||||
this.events = $.extend({}, this.events, { |
|
||||
click: function (event) { |
|
||||
event.stopPropagation(); |
|
||||
} |
|
||||
}); |
|
||||
return this._super.apply(this, arguments); |
|
||||
}, |
|
||||
|
|
||||
get_field_desc: function () { |
|
||||
return this.field; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Add x2x widget after rendering. |
|
||||
*/ |
|
||||
renderElement: function () { |
|
||||
var result = this._super.apply(this, arguments); |
|
||||
if (this.x2x_widget_name()) { |
|
||||
this.x2x_field().appendTo(this.$el); |
|
||||
} |
|
||||
return result; |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Re-render widget when operator changes. |
|
||||
*/ |
|
||||
show_inputs: function () { |
|
||||
this.renderElement(); |
|
||||
return this._super.apply(this, arguments); |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Create a relational field for the user. |
|
||||
* |
|
||||
* @return {Field} |
|
||||
*/ |
|
||||
x2x_field: function () { |
|
||||
if (this._x2x_field) { |
|
||||
this._x2x_field.destroy(); |
|
||||
delete this._x2x_field; |
|
||||
} |
|
||||
var widget = this.x2x_widget(); |
|
||||
if (!widget) return; |
|
||||
this._x2x_field = new DomainSelector(this, this.relation, [], {readonly: false}); |
|
||||
return this._x2x_field; |
|
||||
}, |
|
||||
x2x_value_changed: function () { |
|
||||
switch (this.x2x_widget_name()) { |
|
||||
case "char": |
|
||||
// Apply domain when selected
|
|
||||
this.getParent().getParent().commit_search(); |
|
||||
break; |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
x2x_widget: function () { |
|
||||
var name = this.x2x_widget_name(); |
|
||||
return name && core.search_filters_registry.get(name); |
|
||||
}, |
|
||||
|
|
||||
/** |
|
||||
* Return the widget that should be used to render this proposition. |
|
||||
* |
|
||||
* If it returns `undefined`, it means you should use a simple |
|
||||
* `<input type="text"/>`. |
|
||||
*/ |
|
||||
|
|
||||
x2x_widget_name: function () { |
|
||||
switch (this.get_operator()) { |
|
||||
case "=": |
|
||||
case "!=": |
|
||||
return undefined; |
|
||||
case "domain": |
|
||||
return "many2one"; |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
get_domain: function () { |
|
||||
// Special way to get domain if user chose "domain" filter
|
|
||||
if (this.get_operator() == "domain") { |
|
||||
var domain = this._x2x_field.getDomain(); |
|
||||
var field_name = this.field_name; |
|
||||
|
|
||||
$.each(domain, function (index, value) { |
|
||||
if (domain[index].constructor == Array) { |
|
||||
domain[index][0] = field_name + '.' + domain[index][0] |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
return domain; |
|
||||
} else { |
|
||||
return this._super.apply(this, arguments); |
|
||||
} |
|
||||
}, |
|
||||
|
|
||||
get_operator: function () { |
|
||||
return !this.isDestroyed() && |
|
||||
this.getParent().$('.o_searchview_extended_prop_op').val(); |
|
||||
}, |
|
||||
|
|
||||
get_value: function () { |
|
||||
try { |
|
||||
if (!this.x2x_widget_name()) { |
|
||||
throw "No x2x widget, fallback to default"; |
|
||||
} |
|
||||
var domain = this._x2x_field.getDomain(); |
|
||||
return Domain.prototype.arrayToString(domain) |
|
||||
} catch (error) { |
|
||||
return this._super.apply(this, arguments); |
|
||||
} |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
var affected_types = ["one2many", "many2one", "many2many"], |
|
||||
X2XAdvancedSearchProposition = Char.extend( |
|
||||
FieldManagerMixin, |
|
||||
X2XAdvancedSearchPropositionMixin |
|
||||
); |
|
||||
|
|
||||
// Register this search proposition for relational fields
|
|
||||
$.each(affected_types, function (index, value) { |
|
||||
core.search_filters_registry.add(value, X2XAdvancedSearchProposition); |
|
||||
}); |
|
||||
|
|
||||
return { |
|
||||
X2XAdvancedSearchPropositionMixin: X2XAdvancedSearchPropositionMixin, |
|
||||
X2XAdvancedSearchProposition: X2XAdvancedSearchProposition, |
|
||||
}; |
|
||||
}); |
|
@ -0,0 +1,13 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<!-- Copyright 2017-2018 Jairo Llopis <jairo.llopis@tecnativa.com> |
||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> |
||||
|
<templates> |
||||
|
<t t-extend="SearchView.FilterMenu"> |
||||
|
<t t-jquery=".o_filters_menu" t-operation="append"> |
||||
|
<li class="divider"/> |
||||
|
<li> |
||||
|
<a class="o_add_advanced_search">Add Advanced Filter</a> |
||||
|
</li> |
||||
|
</t> |
||||
|
</t> |
||||
|
</templates> |
@ -1,13 +0,0 @@ |
|||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||
<!-- Copyright 2017 Jairo Llopis <jairo.llopis@tecnativa.com> |
|
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> |
|
||||
<templates> |
|
||||
<t t-name="web_advanced_search_x2x.proposition"> |
|
||||
<t t-if="widget.x2x_widget_name()"> |
|
||||
<div class="x2x_container"/> |
|
||||
</t> |
|
||||
<t t-if="!widget.x2x_widget_name()"> |
|
||||
<input type="text"/> |
|
||||
</t> |
|
||||
</t> |
|
||||
</templates> |
|
@ -1,12 +1,13 @@ |
|||||
<?xml version="1.0" encoding="UTF-8"?> |
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!-- Copyright 2017 Jairo Llopis <jairo.llopis@tecnativa.com> |
|
||||
|
<!-- Copyright 2017-2018 Jairo Llopis <jairo.llopis@tecnativa.com> |
||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> |
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). --> |
||||
<odoo> |
<odoo> |
||||
|
|
||||
<template id="assets_backend" name="web_advanced_search_x2x assets" inherit_id="web.assets_backend"> |
|
||||
|
<template id="assets_backend" inherit_id="web.assets_backend"> |
||||
<xpath expr="." position="inside"> |
<xpath expr="." position="inside"> |
||||
<link rel="stylesheet" href="/web_advanced_search_x2x/static/src/css/web_advanced_search_x2x.less"/> |
|
||||
<script type="text/javascript" src="/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js"/> |
|
||||
|
<link rel="stylesheet" href="/web_advanced_search/static/src/css/web_advanced_search.less"/> |
||||
|
<script type="text/javascript" src="/web_advanced_search/static/src/js/human_domain.js"/> |
||||
|
<script type="text/javascript" src="/web_advanced_search/static/src/js/web_advanced_search.js"/> |
||||
</xpath> |
</xpath> |
||||
</template> |
</template> |
||||
|
|
||||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue