Browse Source
[REN] to web_advanced_search_x2x
[REN] to web_advanced_search_x2x
and replace company icon [FIX] js syntax [FIX] move our patched equals operator to top of list [ADD] support selecting domains on x2x fields [FIX] eval search view's domain [IMP] UI fixes [IMP] reflect recent additions in README.rst [FIX] typos [ADD] translations [FIX] correct js reference [RFR] rebuild our domains in a different way this solves timing problemspull/999/head
Holger Brunn
10 years ago
committed by
Pedro M. Baeza
10 changed files with 598 additions and 0 deletions
-
53web_advanced_search/README.rst
-
20web_advanced_search/__init__.py
-
46web_advanced_search/__openerp__.py
-
38web_advanced_search/i18n/nl.po
-
53web_advanced_search/i18n/web_advanced_search_x2x.pot
-
BINweb_advanced_search/static/description/icon.png
-
36web_advanced_search/static/src/css/web_advanced_search_x2x.css
-
329web_advanced_search/static/src/js/web_advanced_search_x2x.js
-
12web_advanced_search/static/src/xml/web_advanced_search_x2x.xml
-
11web_advanced_search/views/templates.xml
@ -0,0 +1,53 @@ |
|||
Search for x2x records in advanced search |
|||
========================================= |
|||
|
|||
Standard behavior in advanced search for one2many, many2many and many2one fields is to do a `name_search`. This often is not satisfactionary as you might want to search for other properties. There might also be cases where you don't exactly know what you're searching for, then a list of possible options is necessary too. This module enables you to have a full search view to select the record in question, and either select specific records or select them using a search query of its own. |
|||
|
|||
Usage |
|||
===== |
|||
|
|||
To use this module, you need to: |
|||
|
|||
* open the advanced search options in a search view |
|||
* select a one2many, many2many or many2one field |
|||
* select operator `is equal to` or `is not equal to` |
|||
* the textfield changes to a many2one selection field where you can search for the record in question |
|||
|
|||
To search for properties of linked records (ie invoices for customers with a credit limit higher than X): |
|||
|
|||
* open the advanced search options in a search view |
|||
* select a one2many, many2many or many2one field |
|||
* select operator `is in selection` |
|||
* in the search view that pops up, select the criteria |
|||
* click `Use criteria` |
|||
* if you're only interested in certain records, mark them en click `Select` |
|||
* if you want to change your selection afterwards, click the search symbol right of the selection term |
|||
|
|||
In both cases, don't forget to click `Apply` to actually execute the search. |
|||
|
|||
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. |
|||
|
|||
For further information, please visit: |
|||
|
|||
* https://www.odoo.com/forum/help-1 |
|||
|
|||
Credits |
|||
======= |
|||
|
|||
Contributors |
|||
------------ |
|||
|
|||
* Holger Brunn <hbrunn@therp.nl> |
|||
|
|||
Maintainer |
|||
---------- |
|||
|
|||
.. image:: http://odoo-community.org/logo.png |
|||
:alt: Odoo Community Association |
|||
:target: http://odoo-community.org |
|||
|
|||
This module is maintained by the OCA. |
|||
|
|||
OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. |
|||
|
|||
To contribute to this module, please visit http://odoo-community.org. |
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# OpenERP, Open Source Management Solution |
|||
# This module copyright (C) 2015 Therp BV <http://therp.nl>. |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
@ -0,0 +1,46 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################## |
|||
# |
|||
# OpenERP, Open Source Management Solution |
|||
# This module copyright (C) 2015 Therp BV <http://therp.nl>. |
|||
# |
|||
# This program is free software: you can redistribute it and/or modify |
|||
# it under the terms of the GNU Affero General Public License as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################## |
|||
{ |
|||
"name": "Search x2x fields", |
|||
"version": "8.0.1.0.0", |
|||
"author": "Therp BV, " |
|||
"Odoo Community Association (OCA)", |
|||
"license": "AGPL-3", |
|||
"category": "Usability", |
|||
"summary": "Use a search widget in advanced search for x2x fields", |
|||
"depends": [ |
|||
'web', |
|||
], |
|||
"data": [ |
|||
'views/templates.xml', |
|||
], |
|||
"qweb": [ |
|||
'static/src/xml/web_advanced_search_x2x.xml', |
|||
], |
|||
"test": [ |
|||
], |
|||
"auto_install": False, |
|||
"installable": True, |
|||
"application": False, |
|||
"external_dependencies": { |
|||
'python': [], |
|||
}, |
|||
} |
@ -0,0 +1,38 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * web_advanced_search_x2x |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2015-05-27 07:19+0000\n" |
|||
"PO-Revision-Date: 2015-05-27 07:19+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: web_advanced_search_x2x |
|||
#. openerp-web |
|||
#: code:addons/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js:221 |
|||
#, python-format |
|||
msgid "Use criteria" |
|||
msgstr "Gebruik kenmerken" |
|||
|
|||
#. module: web_advanced_search_x2x |
|||
#. openerp-web |
|||
#: code:addons/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js:159 |
|||
#, python-format |
|||
msgid "invalid search domain" |
|||
msgstr "ongeldige zoekopdracht" |
|||
|
|||
#. module: web_advanced_search_x2x |
|||
#. openerp-web |
|||
#: code:addons/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js:47 |
|||
#, python-format |
|||
msgid "is in selection" |
|||
msgstr "is in selectie" |
|||
|
@ -0,0 +1,53 @@ |
|||
# Translation of Odoo Server. |
|||
# This file contains the translation of the following modules: |
|||
# * web_advanced_search_x2x |
|||
# |
|||
msgid "" |
|||
msgstr "" |
|||
"Project-Id-Version: Odoo Server 8.0\n" |
|||
"Report-Msgid-Bugs-To: \n" |
|||
"POT-Creation-Date: 2015-05-27 07:19+0000\n" |
|||
"PO-Revision-Date: 2015-05-27 07:19+0000\n" |
|||
"Last-Translator: <>\n" |
|||
"Language-Team: \n" |
|||
"MIME-Version: 1.0\n" |
|||
"Content-Type: text/plain; charset=UTF-8\n" |
|||
"Content-Transfer-Encoding: \n" |
|||
"Plural-Forms: \n" |
|||
|
|||
#. module: web_advanced_search_x2x |
|||
#. openerp-web |
|||
#: code:addons/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js:295 |
|||
#: code:addons/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js:322 |
|||
#, python-format |
|||
msgid "Advanced" |
|||
msgstr "" |
|||
|
|||
#. module: web_advanced_search_x2x |
|||
#. openerp-web |
|||
#: code:addons/web_advanced_search_x2x/static/src/xml/web_advanced_search_x2x.xml:8 |
|||
#, python-format |
|||
msgid "Search" |
|||
msgstr "" |
|||
|
|||
#. module: web_advanced_search_x2x |
|||
#. openerp-web |
|||
#: code:addons/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js:221 |
|||
#, python-format |
|||
msgid "Use criteria" |
|||
msgstr "" |
|||
|
|||
#. module: web_advanced_search_x2x |
|||
#. openerp-web |
|||
#: code:addons/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js:159 |
|||
#, python-format |
|||
msgid "invalid search domain" |
|||
msgstr "" |
|||
|
|||
#. module: web_advanced_search_x2x |
|||
#. openerp-web |
|||
#: code:addons/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js:47 |
|||
#, python-format |
|||
msgid "is in selection" |
|||
msgstr "" |
|||
|
After Width: 100 | Height: 100 | Size: 2.0 KiB |
@ -0,0 +1,36 @@ |
|||
.openerp .searchview_extended_prop_value .oe_form_field_with_button |
|||
{ |
|||
position: relative; |
|||
} |
|||
.openerp .oe_searchview_drawer .web_advanced_search_x2x_domain |
|||
{ |
|||
max-width: 20em; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
display: inline-block; |
|||
} |
|||
/* copy search view's button style */ |
|||
.openerp .oe_searchview_drawer .web_advanced_search_x2x_search:before |
|||
{ |
|||
font: 21px "mnmliconsRegular"; |
|||
content: "r"; |
|||
color: #a3a3a3; |
|||
margin-left: 5px; |
|||
} |
|||
.openerp .oe_searchview_drawer .web_advanced_search_x2x_search |
|||
{ |
|||
font-size: 1px; |
|||
letter-spacing: -1px; |
|||
color: transparent; |
|||
text-shadow: none; |
|||
font-weight: normal; |
|||
-moz-box-shadow: none; |
|||
-webkit-box-shadow: none; |
|||
box-shadow: none; |
|||
-moz-border-radius: 0; |
|||
-webkit-border-radius: 0; |
|||
border-radius: 0; |
|||
padding: 0; |
|||
border: none; |
|||
background: transparent; |
|||
} |
@ -0,0 +1,329 @@ |
|||
//-*- coding: utf-8 -*-
|
|||
//############################################################################
|
|||
//
|
|||
// OpenERP, Open Source Management Solution
|
|||
// This module copyright (C) 2015 Therp BV <http://therp.nl>.
|
|||
//
|
|||
// This program is free software: you can redistribute it and/or modify
|
|||
// it under the terms of the GNU Affero General Public License as
|
|||
// published by the Free Software Foundation, either version 3 of the
|
|||
// License, or (at your option) any later version.
|
|||
//
|
|||
// This program is distributed in the hope that it will be useful,
|
|||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
// GNU Affero General Public License for more details.
|
|||
//
|
|||
// You should have received a copy of the GNU Affero General Public License
|
|||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
//
|
|||
//############################################################################
|
|||
|
|||
openerp.web_advanced_search_x2x = function(instance) |
|||
{ |
|||
instance.web_advanced_search_x2x.ExtendedSearchPropositionMany2One = |
|||
instance.web.search.ExtendedSearchProposition.Char.extend( |
|||
instance.web.form.FieldManagerMixin, |
|||
{ |
|||
template: 'web_advanced_search_x2x.extended_search.proposition.many2one', |
|||
searchfield: null, |
|||
init: function() |
|||
{ |
|||
this.operators = _.sortBy( |
|||
this.operators, |
|||
function(op) |
|||
{ |
|||
switch(op.value) |
|||
{ |
|||
case '=': |
|||
return -2; |
|||
case '!=': |
|||
return -1; |
|||
default: |
|||
return 0; |
|||
} |
|||
}); |
|||
this.operators.push({ |
|||
'value': 'domain', 'text': instance.web._lt('is in selection'), |
|||
}); |
|||
return this._super.apply(this, arguments); |
|||
}, |
|||
start: function() |
|||
{ |
|||
this.getParent().$('.searchview_extended_prop_op') |
|||
.on('change', this.proxy('operator_changed')); |
|||
return this._super.apply(this, arguments).then( |
|||
this.proxy(this.operator_changed)); |
|||
}, |
|||
get_field_desc: function() |
|||
{ |
|||
return this.field; |
|||
}, |
|||
create_searchfield_node: function() |
|||
{ |
|||
return { |
|||
attrs: { |
|||
name: this.field.name, |
|||
options: '{"no_create": true}', |
|||
}, |
|||
} |
|||
}, |
|||
create_searchfield: function() |
|||
{ |
|||
if(this.searchfield) |
|||
{ |
|||
this.searchfield.destroy(); |
|||
} |
|||
this.searchfield = new instance.web.form.FieldMany2One( |
|||
this, this.create_searchfield_node()); |
|||
return this.searchfield; |
|||
}, |
|||
operator_changed: function(e) |
|||
{ |
|||
if(this.searchfield) |
|||
{ |
|||
this.searchfield.destroy(); |
|||
} |
|||
this.renderElement(); |
|||
if(this.show_searchfield()) |
|||
{ |
|||
this.create_searchfield().appendTo(this.$el.empty()); |
|||
} |
|||
if(this.show_domain_selection()) |
|||
{ |
|||
this.$el.filter('input').remove(); |
|||
this.$el.filter('button.web_advanced_search_x2x_search').click( |
|||
this.proxy(this.popup_domain_selection)); |
|||
this.popup_domain_selection(); |
|||
} |
|||
}, |
|||
get_operator: function() |
|||
{ |
|||
if(this.isDestroyed()) |
|||
{ |
|||
return false; |
|||
} |
|||
return this.getParent().$('.searchview_extended_prop_op').val(); |
|||
}, |
|||
show_searchfield: function() |
|||
{ |
|||
var operator = this.get_operator() |
|||
return operator == '=' || operator == '!='; |
|||
}, |
|||
show_domain_selection: function() |
|||
{ |
|||
return this.get_operator() == 'domain'; |
|||
}, |
|||
get_value: function() |
|||
{ |
|||
if(this.show_searchfield() && this.searchfield) |
|||
{ |
|||
return this.searchfield.get_value(); |
|||
} |
|||
return this._super.apply(this, arguments); |
|||
}, |
|||
format_label: function(format, field, operator) |
|||
{ |
|||
var value = null; |
|||
if(this.show_searchfield() && this.searchfield) |
|||
{ |
|||
value = this.searchfield.display_value[ |
|||
String(this.searchfield.get_value())]; |
|||
} |
|||
if(this.show_domain_selection() && this.domain_representation) |
|||
{ |
|||
value = this.domain_representation; |
|||
} |
|||
if(value) |
|||
{ |
|||
return _.str.sprintf( |
|||
format, |
|||
{ |
|||
field: field.string, |
|||
operator: operator.label || operator.text, |
|||
value: value, |
|||
} |
|||
); |
|||
} |
|||
return this._super.apply(this, arguments); |
|||
}, |
|||
get_domain: function() |
|||
{ |
|||
if(this.show_domain_selection()) |
|||
{ |
|||
var self = this; |
|||
if(!this.domain || this.domain.length == 0) |
|||
{ |
|||
throw new instance.web.search.Invalid( |
|||
this.field.string, this.domain_representation, |
|||
instance.web._lt('invalid search domain')); |
|||
} |
|||
return _.extend(new instance.web.CompoundDomain(), { |
|||
__domains: [ |
|||
_.map(this.domain, function(leaf) |
|||
{ |
|||
if(_.isArray(leaf) && leaf.length == 3) |
|||
{ |
|||
return [ |
|||
self.field.name + '.' + leaf[0], |
|||
leaf[1], |
|||
leaf[2] |
|||
] |
|||
} |
|||
return leaf; |
|||
}), |
|||
], |
|||
}) |
|||
} |
|||
return this._super.apply(this, arguments); |
|||
}, |
|||
popup_domain_selection: function() |
|||
{ |
|||
var self = this, |
|||
popup = new instance.web_advanced_search_x2x.SelectCreatePopup(this); |
|||
popup.on('domain_selected', this, function(domain, domain_representation) |
|||
{ |
|||
self.$el.filter('.web_advanced_search_x2x_domain').text( |
|||
domain_representation); |
|||
self.domain = domain; |
|||
self.domain_representation = domain_representation; |
|||
}); |
|||
popup.select_element( |
|||
this.field.relation, {}, this.field.domain, |
|||
this.field.context); |
|||
}, |
|||
}); |
|||
|
|||
instance.web.search.custom_filters.add( |
|||
'one2many', |
|||
'instance.web_advanced_search_x2x.ExtendedSearchPropositionMany2One'); |
|||
instance.web.search.custom_filters.add( |
|||
'many2many', |
|||
'instance.web_advanced_search_x2x.ExtendedSearchPropositionMany2One'); |
|||
instance.web.search.custom_filters.add( |
|||
'many2one', |
|||
'instance.web_advanced_search_x2x.ExtendedSearchPropositionMany2One'); |
|||
|
|||
instance.web_advanced_search_x2x.SelectCreatePopup = instance.web.form.SelectCreatePopup.extend({ |
|||
setup_search_view: function() |
|||
{ |
|||
var self = this; |
|||
this._super.apply(this, arguments); |
|||
this.searchview.on("search_view_loaded", this, function() |
|||
{ |
|||
self.view_list.on("list_view_loaded", self, function() |
|||
{ |
|||
self.$buttonpane.find(".oe_selectcreatepopup-search-create").remove(); |
|||
self.$buttonpane.prepend( |
|||
jQuery('<button/>') |
|||
.addClass('oe_highlight') |
|||
.addClass('oe_selectcreatepopup-search-select-domain') |
|||
.text(instance.web._lt('Use criteria')) |
|||
.click(self.proxy(self.select_domain)) |
|||
); |
|||
self.$buttonpane.find('.oe_selectcreatepopup-search-select-domain') |
|||
.prop('disabled', self.searchview.build_search_data().domains.length == 0); |
|||
self.$buttonpane.find(".oe_selectcreatepopup-search-select") |
|||
.unbind('click') |
|||
.click(function() |
|||
{ |
|||
self.select_elements(self.selected_ids) |
|||
.then(function() |
|||
{ |
|||
self.destroy(); |
|||
}); |
|||
}); |
|||
self.view_list.select_record = function(index) |
|||
{ |
|||
self.select_elements([self.view_list.dataset.ids[index]]) |
|||
.then(function() |
|||
{ |
|||
self.destroy(); |
|||
}); |
|||
}; |
|||
}); |
|||
}); |
|||
}, |
|||
select_domain: function() |
|||
{ |
|||
var self = this, |
|||
search = this.searchview.build_search_data(); |
|||
instance.web.pyeval.eval_domains_and_contexts({ |
|||
domains: search.domains, |
|||
contexts: search.contexts, |
|||
groupbys: search.groupbys || [] |
|||
}).then(function(search) |
|||
{ |
|||
var representation = self.searchview.query.reduce(function(memo, term) |
|||
{ |
|||
return _.str.sprintf( |
|||
'%s%s(%s: %s)', memo, (memo ? ' ' : ''), |
|||
term.attributes.category, |
|||
_.reduce(term.get('values'), function(memo, value) |
|||
{ |
|||
return memo + (memo ? ', ' : '') + value.label; |
|||
}, '')); |
|||
}, ''); |
|||
self.trigger('domain_selected', search.domain, representation); |
|||
self.destroy(); |
|||
}) |
|||
}, |
|||
select_elements: function(ids) |
|||
{ |
|||
var self = this; |
|||
return this.dataset.name_get(ids).then(function(name_gets) |
|||
{ |
|||
var names = _.reduce(name_gets, function(memo, name_get) |
|||
{ |
|||
return memo + (memo ? ', ' : '') + name_get[1]; |
|||
}, ''); |
|||
self.trigger('domain_selected', [['id', 'in', ids]], names); |
|||
}); |
|||
}, |
|||
}); |
|||
|
|||
instance.web.SearchView.include({ |
|||
build_search_data: function() |
|||
{ |
|||
//Advanced.commit_search can only cope with propositions
|
|||
//(=domain leaves),
|
|||
//so we need to rebuild the domain if one of our CompoundDomains
|
|||
//is involved
|
|||
var result = this._super.apply(this, arguments); |
|||
_.each(result.domains, function(domain, index) |
|||
{ |
|||
if(!_.isArray(domain)) |
|||
{ |
|||
return; |
|||
} |
|||
var compound_domains = [], leaves = []; |
|||
_.each(domain, function(leaf) |
|||
{ |
|||
if(leaf instanceof instance.web.CompoundDomain) |
|||
{ |
|||
compound_domains.push(leaf); |
|||
} |
|||
if(_.isArray(leaf)) |
|||
{ |
|||
leaves.push(leaf); |
|||
} |
|||
}); |
|||
if(compound_domains.length) |
|||
{ |
|||
var combined = new instance.web.CompoundDomain(); |
|||
_.each(compound_domains, function(domain) |
|||
{ |
|||
combined.add(domain.eval()); |
|||
}) |
|||
_.each(leaves, function(leaf) |
|||
{ |
|||
combined.add([leaf]) |
|||
}); |
|||
result.domains[index] = combined; |
|||
} |
|||
}); |
|||
return result; |
|||
}, |
|||
}) |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<templates> |
|||
<t t-name="web_advanced_search_x2x.extended_search.proposition.many2one"> |
|||
<t t-if="!widget.show_searchfield()"> |
|||
<t t-call="SearchView.extended_search.proposition.char" /> |
|||
</t> |
|||
<t t-if="widget.show_domain_selection()"> |
|||
<span class="web_advanced_search_x2x_domain" /><button class="web_advanced_search_x2x_search" type="button">Search</button> |
|||
</t> |
|||
<span /> |
|||
</t> |
|||
</templates> |
@ -0,0 +1,11 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<openerp> |
|||
<data> |
|||
<template id="assets_backend" name="web_advanced_search_x2x assets" inherit_id="web.assets_backend"> |
|||
<xpath expr="." position="inside"> |
|||
<script type="text/javascript" src="/web_advanced_search_x2x/static/src/js/web_advanced_search_x2x.js"></script> |
|||
<link rel="stylesheet" href="/web_advanced_search_x2x/static/src/css/web_advanced_search_x2x.css"/> |
|||
</xpath> |
|||
</template> |
|||
</data> |
|||
</openerp> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue