diff --git a/web_advanced_search/README.rst b/web_advanced_search/README.rst index 2a8c3c46..be42ca3b 100644 --- a/web_advanced_search/README.rst +++ b/web_advanced_search/README.rst @@ -17,6 +17,7 @@ To use this module, you need to: * 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 +* click *Apply* To search for properties of linked records (ie invoices for customers with a credit limit higher than X): @@ -24,11 +25,8 @@ To search for properties of linked records (ie invoices for customers with a cre * 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. +* select the records you want, or select the top corner box to select all matching records with that criteria +* click *Select* 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. @@ -36,6 +34,13 @@ Note that you can stack searching for properties: Simply add another advanced se :alt: Try me on Runbot :target: https://runbot.odoo-community.org/runbot/154/9.0 +Known issues / Roadmap +====================== + +* When you use *is [not] equal to* search system and click on an option with + the mouse, the search dropdown gets hidden and you have to reopen it and + reapply the filter. As a workaround, use the keyboard to select the option + you want. Credits ======= @@ -45,6 +50,7 @@ Contributors * Holger Brunn * Vicent Cubells +* Jairo Llopis Maintainer ---------- diff --git a/web_advanced_search/__openerp__.py b/web_advanced_search/__openerp__.py index bbd644a2..a02f3b93 100644 --- a/web_advanced_search/__openerp__.py +++ b/web_advanced_search/__openerp__.py @@ -7,6 +7,7 @@ "name": "Search x2x fields", "version": "9.0.1.0.0", "author": "Therp BV, " + "Tecnativa, " "Odoo Community Association (OCA)", "license": "AGPL-3", "category": "Usability", diff --git a/web_advanced_search/static/src/css/web_advanced_search_x2x.css b/web_advanced_search/static/src/css/web_advanced_search_x2x.css deleted file mode 100644 index 67c3e194..00000000 --- a/web_advanced_search/static/src/css/web_advanced_search_x2x.css +++ /dev/null @@ -1,36 +0,0 @@ -.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; -} diff --git a/web_advanced_search/static/src/js/web_advanced_search_x2x.js b/web_advanced_search/static/src/js/web_advanced_search_x2x.js index ba2ae5ee..5aa66ed2 100644 --- a/web_advanced_search/static/src/js/web_advanced_search_x2x.js +++ b/web_advanced_search/static/src/js/web_advanced_search_x2x.js @@ -1,41 +1,23 @@ -//-*- coding: utf-8 -*- -//############################################################################ -// -// OpenERP, Open Source Management Solution -// This module copyright (C) 2015 Therp BV . -// -// 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 . -// -//############################################################################ +/* Copyright 2015 Therp BV + * Copyright 2017 Jairo Llopis + * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ odoo.define('web_advanced_search_x2x.search_filters', function (require) { "use strict"; - var filters = require('web.search_filters'); + require('web.form_relational'); + require('web.form_widgets'); + var search_filters = require('web.search_filters'); var form_common = require('web.form_common'); var SearchView = require('web.SearchView'); var data = require('web.data'); - var session = require('web.session'); var core = require('web.core'); - var searchfilters = filters.ExtendedSearchProposition.Char.extend( - form_common.FieldManagerMixin, - { - template: 'web_advanced_search_x2x.extended_search.proposition.many2one', - searchfield: null, + var X2XAdvancedSearchPropositionMixin = { + template: "web_advanced_search_x2x.proposition", init: function() { + // Make equal and not equal appear 1st and 2nd this.operators = _.sortBy( this.operators, function(op) @@ -50,99 +32,120 @@ odoo.define('web_advanced_search_x2x.search_filters', function (require) { return 0; } }); + // Append domain operator this.operators.push({ - 'value': 'domain', 'text': data._lt('is in selection'), + 'value': 'domain', 'text': core._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() - { + /** + * Add the right relational field to the template. + */ + renderElement: function () { + try { + this._x2x_field.destroy(); + } catch (error) {} + this.relational = this.x2x_widget_name(); + this._super.apply(this, arguments); + if (this.relational) { + this.x2x_field().appendTo(this.$el); + } + delete this.relational; + }, + /** + * 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 widget( + this, + this.x2x_field_create_options() + ); + this._x2x_field.on( + "change:value", + this, + this.proxy("x2x_value_changed") + ); + return this._x2x_field; + }, + x2x_field_create_options: function () { return { attrs: { name: this.field.name, - options: '{"no_create": true}', + options: JSON.stringify({ + no_create: true, + model: this.field.relation, + }), }, - } + }; }, - create_searchfield: function() - { - if(this.searchfield) - { - this.searchfield.destroy(); + x2x_value_changed: function () { + switch (this.x2x_widget_name()) { + case "char_domain": + // Apply domain when selected + this.getParent().getParent().commit_search(); + break; } - this.searchfield = new form_common.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(); - } + x2x_widget: function () { + var name = this.x2x_widget_name(); + return name && core.form_widget_registry.get(name); }, - get_operator: function() - { - if(this.isDestroyed()) - { - return false; + x2x_widget_name: function () { + switch (this.get_operator()) { + case "=": + case "!=": + return "many2one"; + case "domain": + return "char_domain"; } - return this.getParent().$('.searchview_extended_prop_op').val(); }, - show_searchfield: function() - { - var operator = this.get_operator() - return operator == '=' || operator == '!='; + get_domain: function () { + // Special way to get domain if user chose "domain" filter + if (this.get_operator() == "domain") { + var value = this._x2x_field.get_value(); + var domain = new data.CompoundDomain(), + name = this.field.name; + $.map(value, function (el) { + domain.add([[ + _.str.sprintf("%s.%s", name, el[0]), + el[1], + el[2], + ]]); + }); + return domain; + } else { + return this._super.apply(this, arguments); + } }, - show_domain_selection: function() - { - return this.get_operator() == 'domain'; + get_operator: function () { + return !this.isDestroyed() && + this.getParent().$('.searchview_extended_prop_op').val(); }, - get_value: function() - { - if(this.show_searchfield() && this.searchfield) - { - return this.searchfield.get_value(); + get_value: function () { + try { + return this._x2x_field.get_value(); + } catch (error) { + return this._super.apply(this, arguments); } - 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) - { + format_label: function (format, field, operator) { + if (this.x2x_widget()) { + var value = String(this._x2x_field.get_value()); + if (this._x2x_field.display_value) { + value = this._x2x_field.display_value[value]; + } return _.str.sprintf( format, { @@ -151,57 +154,46 @@ odoo.define('web_advanced_search_x2x.search_filters', function (require) { value: value, } ); + } else { + return this._super.apply(this, arguments); } - 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 filters.Invalid( - this.field.string, this.domain_representation, - data._lt('invalid search domain')); - } - return _.extend(new data.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; - }), - ], - }) + }; + + var ExtendedSearchProposition = search_filters.ExtendedSearchProposition, + Char = ExtendedSearchProposition.Char, + affected_types = ["one2many", "many2one", "many2many"], + X2XAdvancedSearchProposition = Char.extend( + form_common.FieldManagerMixin, + X2XAdvancedSearchPropositionMixin + ); + + ExtendedSearchProposition.include({ + /** + * Force re-rendering the value widget if needed. + */ + operator_changed: function (event) { + if (this.value instanceof X2XAdvancedSearchProposition) { + this.value_rerender(); } return this._super.apply(this, arguments); }, - popup_domain_selection: function() - { - var self = this, - popup = new form_common.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, - new data.CompoundContext( - session.user_context, this.field.context)); + /** + * Re-render proposition's value widget. + * + * @return {jQuery.Deferred} + */ + value_rerender: function () { + return this.value.appendTo( + this.$(".searchview_extended_prop_value").show().empty() + ); }, }); + // Register this search proposition for relational fields + $.each(affected_types, function (index, value) { + core.search_filters_registry.add(value, X2XAdvancedSearchProposition); + }); SearchView.include({ build_search_data: function() @@ -245,5 +237,10 @@ odoo.define('web_advanced_search_x2x.search_filters', function (require) { }); return result; }, - }) + }); + + return { + X2XAdvancedSearchPropositionMixin: X2XAdvancedSearchPropositionMixin, + X2XAdvancedSearchProposition: X2XAdvancedSearchProposition, + }; }); diff --git a/web_advanced_search/static/src/xml/web_advanced_search_x2x.xml b/web_advanced_search/static/src/xml/web_advanced_search_x2x.xml index bac699ac..74e4a86e 100644 --- a/web_advanced_search/static/src/xml/web_advanced_search_x2x.xml +++ b/web_advanced_search/static/src/xml/web_advanced_search_x2x.xml @@ -1,12 +1,14 @@ + - - - + + + +
- - + + - diff --git a/web_advanced_search/views/templates.xml b/web_advanced_search/views/templates.xml index fd0ce2a4..5c978273 100644 --- a/web_advanced_search/views/templates.xml +++ b/web_advanced_search/views/templates.xml @@ -1,10 +1,11 @@ +