From fb84ffdc681b6accca6a98c362e763e96ca7a5b2 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 24 Nov 2014 14:26:40 +0100 Subject: [PATCH 01/24] [REN] rename to web_advanced_filters --- web_advanced_filters/__init__.py | 22 ++ web_advanced_filters/__openerp__.py | 79 +++++++ web_advanced_filters/data/migration.xml | 6 + web_advanced_filters/i18n/advanced_filter.pot | 195 ++++++++++++++++ web_advanced_filters/i18n/nl.po | 194 ++++++++++++++++ web_advanced_filters/model/__init__.py | 21 ++ web_advanced_filters/model/ir_filters.py | 167 ++++++++++++++ .../static/src/css/advanced_filters.css | 14 ++ web_advanced_filters/static/src/img/icon.png | Bin 0 -> 16070 bytes .../static/src/js/advanced_filters.js | 214 ++++++++++++++++++ web_advanced_filters/view/ir_filters.xml | 44 ++++ web_advanced_filters/wizard/__init__.py | 21 ++ .../ir_filters_combine_with_existing.py | 87 +++++++ .../ir_filters_combine_with_existing.xml | 21 ++ 14 files changed, 1085 insertions(+) create mode 100644 web_advanced_filters/__init__.py create mode 100644 web_advanced_filters/__openerp__.py create mode 100644 web_advanced_filters/data/migration.xml create mode 100644 web_advanced_filters/i18n/advanced_filter.pot create mode 100644 web_advanced_filters/i18n/nl.po create mode 100644 web_advanced_filters/model/__init__.py create mode 100644 web_advanced_filters/model/ir_filters.py create mode 100644 web_advanced_filters/static/src/css/advanced_filters.css create mode 100644 web_advanced_filters/static/src/img/icon.png create mode 100644 web_advanced_filters/static/src/js/advanced_filters.js create mode 100644 web_advanced_filters/view/ir_filters.xml create mode 100644 web_advanced_filters/wizard/__init__.py create mode 100644 web_advanced_filters/wizard/ir_filters_combine_with_existing.py create mode 100644 web_advanced_filters/wizard/ir_filters_combine_with_existing.xml diff --git a/web_advanced_filters/__init__.py b/web_advanced_filters/__init__.py new file mode 100644 index 000000000..500e771d9 --- /dev/null +++ b/web_advanced_filters/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 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 . +# +############################################################################## +from . import model +from . import wizard diff --git a/web_advanced_filters/__openerp__.py b/web_advanced_filters/__openerp__.py new file mode 100644 index 000000000..4cd39dfb2 --- /dev/null +++ b/web_advanced_filters/__openerp__.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 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 . +# +############################################################################## +{ + "name": "Advanced filters", + "version": "1.0", + "author": "Therp BV", + "license": "AGPL-3", + "complexity": "normal", + "description": """ +Introduction +------------ + +This addon allows users to apply set operations on filters: Remove or add +certain ids from/to a selection, but also to remove or add another filter's +outcome from/to a filter. This can be stacked, so the filter domain can be +arbitrarily complicated. + +The math is hidden from the user as far as possible, in the hope it's still +user friendly. + +Usage +----- + +After this addon is installed, every list view shows a new menu 'Advanced +filters'. Here the set operations can be applied as necessary. + +Caution +------- + +Deinstalling this module will leave you with filters with empty domains. Use +this query before uninstalling to avoid that: + +``alter table ir_filters rename domain_this to domain`` + """, + "category": "Tools", + "depends": [ + 'base', + 'web', + ], + "data": [ + "data/migration.xml", + "wizard/ir_filters_combine_with_existing.xml", + "view/ir_filters.xml", + ], + "js": [ + 'static/src/js/advanced_filters.js', + ], + "css": [ + 'static/src/css/advanced_filters.css', + ], + "qweb": [ + ], + "test": [ + ], + "auto_install": False, + "installable": True, + "application": False, + "external_dependencies": { + 'python': [], + }, +} diff --git a/web_advanced_filters/data/migration.xml b/web_advanced_filters/data/migration.xml new file mode 100644 index 000000000..0219058df --- /dev/null +++ b/web_advanced_filters/data/migration.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/web_advanced_filters/i18n/advanced_filter.pot b/web_advanced_filters/i18n/advanced_filter.pot new file mode 100644 index 000000000..9619a63c5 --- /dev/null +++ b/web_advanced_filters/i18n/advanced_filter.pot @@ -0,0 +1,195 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * advanced_filters +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-29 10:55+0000\n" +"PO-Revision-Date: 2014-07-29 10:55+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: advanced_filters +#: view:ir.filters:0 +msgid "Save filter" +msgstr "" + +#. module: advanced_filters +#: selection:ir.filters.combine.with.existing,action:0 +msgid "Union" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Add the result of following filters" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Save" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Freeze filter" +msgstr "" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:94 +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:114 +#, python-format +msgid "Remove from existing selection" +msgstr "" + +#. module: advanced_filters +#: code:addons/advanced_filters/model/ir_filters.py:131 +#, python-format +msgid "Testing %s" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "or" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters,complement_filter_ids:0 +msgid "Remove result of filters" +msgstr "" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:86 +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:106 +#, python-format +msgid "To existing selection" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters.combine.with.existing:0 +msgid "Combine with existing filter" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Have this filter contain extly the records it currently contains, with no changes in the future. Be careful, you can't undo this operation!" +msgstr "" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:32 +#, python-format +msgid "Advanced filters" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters,active:0 +msgid "Active" +msgstr "" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:74 +#, python-format +msgid "Marked records" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters,is_frozen:0 +msgid "Frozen" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,filter_id:0 +msgid "Filter" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Test filter" +msgstr "" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:78 +#, python-format +msgid "To new selection" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters,domain_this:0 +msgid "This filter's own domain" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Remove the result of following filters" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,context:0 +msgid "Context" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,action:0 +msgid "Action" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,model:0 +msgid "Model" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,domain:0 +msgid "Domain" +msgstr "" + +#. module: advanced_filters +#: field:ir.filters,union_filter_ids:0 +msgid "Add result of filters" +msgstr "" + +#. module: advanced_filters +#: model:ir.model,name:advanced_filters.model_ir_filters_combine_with_existing +msgid "Combine a selection with an existing filter" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Are you sure? You can't undo this operation!" +msgstr "" + +#. module: advanced_filters +#: model:ir.model,name:advanced_filters.model_ir_filters +msgid "Filters" +msgstr "" + +#. module: advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Cancel" +msgstr "" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:102 +#, python-format +msgid "Whole selection" +msgstr "" + +#. module: advanced_filters +#: selection:ir.filters.combine.with.existing,action:0 +msgid "Complement" +msgstr "" + diff --git a/web_advanced_filters/i18n/nl.po b/web_advanced_filters/i18n/nl.po new file mode 100644 index 000000000..8f7c43f07 --- /dev/null +++ b/web_advanced_filters/i18n/nl.po @@ -0,0 +1,194 @@ +# This file contains the translation of the following modules: +# * advanced_filters +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-29 09:54+0000\n" +"PO-Revision-Date: 2014-07-29 09:54+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: advanced_filters +#: view:ir.filters:0 +msgid "Save filter" +msgstr "Opslaan filter" + +#. module: advanced_filters +#: selection:ir.filters.combine.with.existing,action:0 +msgid "Union" +msgstr "Union" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Add the result of following filters" +msgstr "Voeg resultaat van onderstaande filteringen toe aan filter" + +#. module: advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Save" +msgstr "Opslaan" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Freeze filter" +msgstr "Bevries filter" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:94 +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:114 +#, python-format +msgid "Remove from existing filter" +msgstr "Verwijder uit bestaand filter" + +#. module: advanced_filters +#: code:addons/advanced_filters/model/ir_filters.py:131 +#, python-format +msgid "Testing %s" +msgstr "Testen %s" + +#. module: advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "or" +msgstr "or" + +#. module: advanced_filters +#: field:ir.filters,complement_filter_ids:0 +msgid "Remove result of filters" +msgstr "Verwijder resultaat van filters" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:86 +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:106 +#, python-format +msgid "To existing filter" +msgstr "Voeg toe aan bestaand filter" + +#. module: advanced_filters +#: view:ir.filters.combine.with.existing:0 +msgid "Combine with existing filter" +msgstr "Combineer met bestaande filter" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Have this filter contain exactly the records it currently contains, with no changes in the future. Be careful, you can't undo this operation!" +msgstr "Laat deze filter precies deze records bevatten, zonder veranderingen in de toekomst. Wees voorzichtig, je kan dit niet meer veranderen!" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:32 +#, python-format +msgid "Advanced filters" +msgstr "Geavanceerde filters" + +#. module: advanced_filters +#: field:ir.filters,active:0 +msgid "Active" +msgstr "Actief" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:74 +#, python-format +msgid "Marked records" +msgstr "Aangevinkte records (verzameling)" + +#. module: advanced_filters +#: field:ir.filters,is_frozen:0 +msgid "Frozen" +msgstr "Bevroren" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,filter_id:0 +msgid "Filter" +msgstr "Filter" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Test filter" +msgstr "Test filter" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:78 +#, python-format +msgid "To new filter" +msgstr "Opslaan als nieuw filter" + +#. module: advanced_filters +#: field:ir.filters,domain_this:0 +msgid "This filter's own domain" +msgstr "Deze filters eigen domein" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Remove the result of following filters" +msgstr "Verwijder resultaat van onderstaande filteringen uit filter" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,context:0 +msgid "Context" +msgstr "Context" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,action:0 +msgid "Action" +msgstr "Actie" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,model:0 +msgid "Model" +msgstr "Model" + +#. module: advanced_filters +#. openerp-web +#: code:addons/advanced_filters/static/src/js/advanced_filters.js:102 +#, python-format +msgid "Whole selection (criteria)" +msgstr "Alle records (als criteria)" + +#. module: advanced_filters +#: field:ir.filters.combine.with.existing,domain:0 +msgid "Domain" +msgstr "Domein" + +#. module: advanced_filters +#: field:ir.filters,union_filter_ids:0 +msgid "Add result of filters" +msgstr "Voeg resultaat toe aan filters" + +#. module: advanced_filters +#: model:ir.model,name:advanced_filters.model_ir_filters_combine_with_existing +msgid "Combine a selection with an existing filter" +msgstr "Combineer een selectie met een bestaande filter" + +#. module: advanced_filters +#: view:ir.filters:0 +msgid "Are you sure? You can't undo this operation!" +msgstr "Weet u het zeker? U kan deze operatie niet meer ongedaan maken" + +#. module: advanced_filters +#: model:ir.model,name:advanced_filters.model_ir_filters +msgid "Filters" +msgstr "Filters" + +#. module: advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Cancel" +msgstr "Annuleren" + +#. module: advanced_filters +#: selection:ir.filters.combine.with.existing,action:0 +msgid "Complement" +msgstr "Aanvullen" + diff --git a/web_advanced_filters/model/__init__.py b/web_advanced_filters/model/__init__.py new file mode 100644 index 000000000..bbae05878 --- /dev/null +++ b/web_advanced_filters/model/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 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 . +# +############################################################################## +from . import ir_filters diff --git a/web_advanced_filters/model/ir_filters.py b/web_advanced_filters/model/ir_filters.py new file mode 100644 index 000000000..07b437a4d --- /dev/null +++ b/web_advanced_filters/model/ir_filters.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 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 . +# +############################################################################## +import itertools +from openerp.osv.orm import Model +from openerp.osv import fields, expression +from openerp.tools.safe_eval import safe_eval +from openerp.tools.translate import _ + + +class IrFilters(Model): + _inherit = 'ir.filters' + + def _is_frozen_get(self, cr, uid, ids, field_name, args, context=None): + '''determine if this is fixed list of ids''' + result = {} + for this in self.browse(cr, uid, ids, context=context): + domain = safe_eval(this.domain) + result[this.id] = (len(domain) == 1 and + expression.is_leaf(domain[0]) and + domain[0][0] == 'id') + return result + + def _domain_get(self, cr, uid, ids, field_name, args, context=None): + '''combine our domain with all domains to union/complement, + this works recursively''' + def eval_n(domain): + '''parse a domain and normalize it''' + return expression.normalize_domain( + safe_eval(domain) or [expression.FALSE_LEAF]) + result = {} + for this in self.read( + cr, uid, ids, + ['domain_this', 'union_filter_ids', 'complement_filter_ids'], + context=context): + domain = eval_n(this['domain_this']) + domain = expression.OR( + [domain] + + [eval_n(u['domain']) for u in self.read( + cr, uid, this['union_filter_ids'], ['domain'], + context=context)]) + for c in self.read(cr, uid, this['complement_filter_ids'], + ['domain'], context=context): + domain = expression.AND([ + domain, + ['!'] + eval_n(c['domain'])]) + result[this['id']] = str(domain) + return result + + def _domain_set(self, cr, uid, ids, field_name, field_value, args, + context=None): + self.write(cr, uid, ids, {'domain_this': field_value}) + + _columns = { + 'is_frozen': fields.function( + _is_frozen_get, type='boolean', string='Frozen'), + 'union_filter_ids': fields.many2many( + 'ir.filters', 'ir_filters_union_rel', 'left_filter_id', + 'right_filter_id', 'Add result of filters', + domain=['|', ('active', '=', False), ('active', '=', True)]), + 'complement_filter_ids': fields.many2many( + 'ir.filters', 'ir_filters_complement_rel', 'left_filter_id', + 'right_filter_id', 'Remove result of filters', + domain=['|', ('active', '=', False), ('active', '=', True)]), + 'active': fields.boolean('Active'), + 'domain': fields.function( + _domain_get, type='text', string='Domain', + fnct_inv=_domain_set), + 'domain_this': fields.text( + 'This filter\'s own domain', oldname='domain'), + } + + _defaults = { + 'active': True, + } + + def _evaluate(self, cr, uid, ids, context=None): + assert len(ids) == 1 + this = self.browse(cr, uid, ids[0], context=context) + return self.pool[this.model_id].search( + cr, uid, safe_eval(this.domain), context=safe_eval(this.context)) + + def button_save(self, cr, uid, ids, context=None): + return {'type': 'ir.actions.act_window.close'} + + def button_freeze(self, cr, uid, ids, context=None): + '''evaluate the filter and write a fixed [('id', 'in', [])] domain''' + for this in self.browse(cr, uid, ids, context=context): + ids = this._evaluate() + removed_filter_ids = [f.id for f in itertools.chain( + this.union_filter_ids, this.complement_filter_ids)] + this.write({ + 'domain': str([('id', 'in', ids)]), + 'union_filter_ids': [(6, 0, [])], + 'complement_filter_ids': [(6, 0, [])], + }) + # if we removed inactive filters which are orphaned now, delete + # them + cr.execute('''delete from ir_filters + where + not active and id in %s + and not exists (select right_filter_id + from ir_filters_union_rel where left_filter_id=id) + and not exists (select right_filter_id + from ir_filters_complement_rel where + left_filter_id=id) + ''', + (tuple(removed_filter_ids),)) + + def button_test(self, cr, uid, ids, context=None): + for this in self.browse(cr, uid, ids, context=context): + return { + 'type': 'ir.actions.act_window', + 'name': _('Testing %s') % this.name, + 'res_model': this.model_id, + 'domain': this.domain, + 'view_type': 'form', + 'view_mode': 'tree', + 'context': { + 'default_filter_id': this.id, + }, + } + + def _auto_init(self, cr, context=None): + cr.execute( + 'SELECT count(attname) FROM pg_attribute ' + 'WHERE attrelid = ' + '( SELECT oid FROM pg_class WHERE relname = %s) ' + 'AND attname = %s', (self._table, 'domain_this')) + if not cr.fetchone()[0]: + cr.execute( + 'ALTER table %s RENAME domain TO domain_this' % self._table) + return super(IrFilters, self)._auto_init(cr, context=context) + + def _migrate_name_change(self, cr, uid, context=None): + cr.execute( + "select id from ir_module_module where name='advanced_filters' " + "and author='Therp BV'") + old_name_installed = cr.fetchall() + if not old_name_installed: + return + cr.execute( + "delete from ir_model_data where module='web_advanced_filters'") + cr.execute( + "update ir_model_data set module='web_advanced_filters' " + "where module='advanced_filters'") + cr.execute( + "update ir_module_module set state='to remove' where " + "name='advanced_filters' and state not in " + "('uninstalled', 'to remove')") diff --git a/web_advanced_filters/static/src/css/advanced_filters.css b/web_advanced_filters/static/src/css/advanced_filters.css new file mode 100644 index 000000000..06a3c8ecf --- /dev/null +++ b/web_advanced_filters/static/src/css/advanced_filters.css @@ -0,0 +1,14 @@ +li.oe_advanced_filters_header +{ + font-weight: bold; +} +.openerp .oe_dropdown_menu > li.oe_advanced_filters_header:hover +{ + background-color: inherit; + background-image: inherit; + box-shadow: none; +} +.openerp .oe_dropdown_menu > li.oe_advanced_filters_header a:hover +{ + cursor: default !important; +} diff --git a/web_advanced_filters/static/src/img/icon.png b/web_advanced_filters/static/src/img/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2937260475c2bf744b31c1d593e3485abbd997d0 GIT binary patch literal 16070 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%0D?ZV<_Z0$Ta^5DP@LR`tJHgEGg_osZLP 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. +// +// 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 . +// +//############################################################################ + +openerp.advanced_filters = function(instance) +{ + var _t = instance.web._t; + + instance.web.Sidebar.include({ + init: function() + { + var result = this._super.apply(this, arguments); + this.sections.push({ + 'name': 'advanced_filters', + 'label': _t('Advanced filters'), + }); + this.items.advanced_filters = []; + return result; + }, + }); + instance.web.ListView.include({ + do_select: function (ids, records) + { + var result = this._super(this, arguments); + this.advanced_filters_show(ids); + return result; + }, + load_list: function(data) + { + var result = this._super.apply(this, arguments), + self = this; + if(!this.sidebar || this.sidebar.items.advanced_filters.length) + { + this.advanced_filters_show([]); + return result; + } + this.sidebar.add_items( + 'advanced_filters', + [ + { + label: _t('Whole selection (criteria)'), + classname: 'oe_advanced_filters_header', + }, + { + label: _t('To existing filter'), + callback: function (item) + { + self.advanced_filters_combine_with_existing.apply( + self, ['union', 'domain', item]); + }, + }, + { + label: _t('Remove from existing filter'), + callback: function (item) + { + self.advanced_filters_combine_with_existing.apply( + self, ['complement', 'domain', item]); + }, + }, + { + label: _t('Marked records'), + classname: 'oe_advanced_filters_header', + }, + { + label: _t('To new filter'), + callback: function () + { + self.advanced_filters_save_selection.apply( + self, arguments); + }, + }, + { + label: _t('To existing filter'), + callback: function (item) + { + self.advanced_filters_combine_with_existing.apply( + self, ['union', 'ids', item]); + }, + }, + { + label: _t('Remove from existing filter'), + callback: function (item) + { + self.advanced_filters_combine_with_existing.apply( + self, ['complement', 'ids', item]); + }, + }, + ] + ); + this.do_select([], []); + return result; + }, + advanced_filters_show: function(ids) + { + if(this.sidebar) + { + this.sidebar.$el.show(); + this.sidebar.$el.children().children().each(function(i, e) + { + $e = jQuery(e) + if($e.find('li.oe_advanced_filters_header').length) + { + $e.find('a[data-index="3"],a[data-index="4"],' + + 'a[data-index="5"],a[data-index="6"]') + .parent().toggle(ids.length > 0); + } + else + { + $e.toggle(ids.length > 0); + } + }); + } + }, + advanced_filters_save_selection: function(item) + { + var self = this; + this.do_action({ + name: item.label, + type: 'ir.actions.act_window', + res_model: 'ir.filters', + views: [[false, 'form']], + target: 'new', + context: { + default_model_id: this.dataset._model.name, + default_domain: JSON.stringify( + [ + ['id', 'in', this.groups.get_selection().ids], + ] + ), + default_context: JSON.stringify({}), + form_view_ref: 'advanced_filters.form_ir_filters_save_new', + }, + }, + { + on_close: function() + { + self.ViewManager.setup_search_view( + self.ViewManager.searchview.view_id, + self.ViewManager.searchview.defaults); + }, + }); + }, + advanced_filters_combine_with_existing: function(action, type, item) + { + var search = this.ViewManager.searchview.build_search_data(), + self = this; + instance.web.pyeval.eval_domains_and_contexts({ + domains: search.domains, + contexts: search.contexts, + group_by_seq: search.groupbys || [] + }).done(function(search) + { + var domain = [], ctx = {}; + switch(type) + { + case 'domain': + domain = search.domain; + ctx = search.context; + _(_.keys(instance.session.user_context)).each( + function (key) {delete ctx[key]}); + break; + case 'ids': + domain = [ + ['id', 'in', self.groups.get_selection().ids], + ] + ctx = {}; + break; + } + self.do_action({ + name: item.label, + type: 'ir.actions.act_window', + res_model: 'ir.filters.combine.with.existing', + views: [[false, 'form']], + target: 'new', + context: _.extend({ + default_model: self.dataset._model.name, + default_domain: JSON.stringify(domain), + default_action: action, + default_context: JSON.stringify(ctx), + }, + self.dataset.context.default_filter_id ? { + default_filter_id: + self.dataset.context.default_filter_id, + } : {}), + }, + { + on_close: function() + { + self.ViewManager.setup_search_view( + self.ViewManager.searchview.view_id, + self.ViewManager.searchview.defaults); + }, + }); + }); + }, + }); +} diff --git a/web_advanced_filters/view/ir_filters.xml b/web_advanced_filters/view/ir_filters.xml new file mode 100644 index 000000000..057db6b1f --- /dev/null +++ b/web_advanced_filters/view/ir_filters.xml @@ -0,0 +1,44 @@ + + + + + ir.filters + + + +
+
+
+ + + + + + + + + + +
+
+ + ir.filters + 999 + +
+ + + + +
+
+
+
+
+
+
diff --git a/web_advanced_filters/wizard/__init__.py b/web_advanced_filters/wizard/__init__.py new file mode 100644 index 000000000..608ed687f --- /dev/null +++ b/web_advanced_filters/wizard/__init__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 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 . +# +############################################################################## +from . import ir_filters_combine_with_existing diff --git a/web_advanced_filters/wizard/ir_filters_combine_with_existing.py b/web_advanced_filters/wizard/ir_filters_combine_with_existing.py new file mode 100644 index 000000000..0641caf88 --- /dev/null +++ b/web_advanced_filters/wizard/ir_filters_combine_with_existing.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2014 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 . +# +############################################################################## +import time +from openerp.osv.orm import TransientModel +from openerp.osv import fields, expression +from openerp.tools.safe_eval import const_eval + + +class IrFiltersCombineWithExisting(TransientModel): + _name = 'ir.filters.combine.with.existing' + _description = 'Combine a selection with an existing filter' + + _columns = { + 'action': fields.selection( + [('union', 'Union'), ('complement', 'Complement')], + 'Action', required=True), + 'domain': fields.char('Domain', required=True), + 'context': fields.char('Context', required=True), + 'model': fields.char('Model', required=True), + 'filter_id': fields.many2one('ir.filters', 'Filter', required=True), + } + + def button_save(self, cr, uid, ids, context=None): + assert len(ids) == 1 + this = self.browse(cr, uid, ids[0], context=context) + domain = const_eval(this.domain) + is_frozen = (len(domain) == 1 and + expression.is_leaf(domain[0]) and + domain[0][0] == 'id') + + if this.action == 'union': + if is_frozen and this.filter_id.is_frozen: + domain[0][2] = list(set(domain[0][2]).union( + set(const_eval(this.filter_id.domain)[0][2]))) + this.filter_id.write({'domain': str(domain)}) + else: + this.filter_id.write( + { + 'union_filter_ids': [(0, 0, { + 'name': '%s_%s_%d' % ( + this.filter_id.name, 'add', time.time()), + 'active': False, + 'domain': str(domain), + 'context': this.context, + 'model_id': this.model, + 'user_id': uid, + })], + }) + elif this.action == 'complement': + if is_frozen and this.filter_id.is_frozen: + complement_set = set(const_eval(this.filter_id.domain)[0][2]) + domain[0][2] = list( + complement_set.difference(set(domain[0][2]))) + this.filter_id.write({'domain': str(domain)}) + else: + this.filter_id.write( + { + 'complement_filter_ids': [(0, 0, { + 'name': '%s_%s_%d' % ( + this.filter_id.name, 'remove', time.time()), + 'active': False, + 'domain': str(domain), + 'context': this.context, + 'model_id': this.model, + 'user_id': uid, + })], + }) + + return {'type': 'ir.actions.act_window.close'} diff --git a/web_advanced_filters/wizard/ir_filters_combine_with_existing.xml b/web_advanced_filters/wizard/ir_filters_combine_with_existing.xml new file mode 100644 index 000000000..bc7028d36 --- /dev/null +++ b/web_advanced_filters/wizard/ir_filters_combine_with_existing.xml @@ -0,0 +1,21 @@ + + + + + ir.filters.combine.with.existing + +
+ + + + +
+
+
+
+
+
+
From b11ba271cb652f69716b5ecc9fc56acb658123d6 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 24 Nov 2014 15:19:52 +0100 Subject: [PATCH 02/24] [FIX] do the renaming properly --- web_advanced_filters/__openerp__.py | 4 +- web_advanced_filters/i18n/advanced_filter.pot | 88 +++++++++---------- web_advanced_filters/i18n/nl.po | 88 +++++++++---------- ...d_filters.css => web_advanced_filters.css} | 0 ...ced_filters.js => web_advanced_filters.js} | 4 +- 5 files changed, 92 insertions(+), 92 deletions(-) rename web_advanced_filters/static/src/css/{advanced_filters.css => web_advanced_filters.css} (100%) rename web_advanced_filters/static/src/js/{advanced_filters.js => web_advanced_filters.js} (98%) diff --git a/web_advanced_filters/__openerp__.py b/web_advanced_filters/__openerp__.py index 4cd39dfb2..b91430b59 100644 --- a/web_advanced_filters/__openerp__.py +++ b/web_advanced_filters/__openerp__.py @@ -61,10 +61,10 @@ this query before uninstalling to avoid that: "view/ir_filters.xml", ], "js": [ - 'static/src/js/advanced_filters.js', + 'static/src/js/web_advanced_filters.js', ], "css": [ - 'static/src/css/advanced_filters.css', + 'static/src/css/web_advanced_filters.css', ], "qweb": [ ], diff --git a/web_advanced_filters/i18n/advanced_filter.pot b/web_advanced_filters/i18n/advanced_filter.pot index 9619a63c5..5691d68c6 100644 --- a/web_advanced_filters/i18n/advanced_filter.pot +++ b/web_advanced_filters/i18n/advanced_filter.pot @@ -1,6 +1,6 @@ # Translation of OpenERP Server. # This file contains the translation of the following modules: -# * advanced_filters +# * web_advanced_filters # msgid "" msgstr "" @@ -15,180 +15,180 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Save filter" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: selection:ir.filters.combine.with.existing,action:0 msgid "Union" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Add the result of following filters" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 #: view:ir.filters.combine.with.existing:0 msgid "Save" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Freeze filter" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:94 -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:114 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:94 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:114 #, python-format msgid "Remove from existing selection" msgstr "" -#. module: advanced_filters -#: code:addons/advanced_filters/model/ir_filters.py:131 +#. module: web_advanced_filters +#: code:addons/web_advanced_filters/model/ir_filters.py:131 #, python-format msgid "Testing %s" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 #: view:ir.filters.combine.with.existing:0 msgid "or" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,complement_filter_ids:0 msgid "Remove result of filters" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:86 -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:106 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:86 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:106 #, python-format msgid "To existing selection" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters.combine.with.existing:0 msgid "Combine with existing filter" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Have this filter contain extly the records it currently contains, with no changes in the future. Be careful, you can't undo this operation!" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:32 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:32 #, python-format msgid "Advanced filters" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,active:0 msgid "Active" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:74 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:74 #, python-format msgid "Marked records" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,is_frozen:0 msgid "Frozen" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,filter_id:0 msgid "Filter" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Test filter" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:78 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:78 #, python-format msgid "To new selection" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,domain_this:0 msgid "This filter's own domain" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Remove the result of following filters" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,context:0 msgid "Context" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,action:0 msgid "Action" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,model:0 msgid "Model" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,domain:0 msgid "Domain" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,union_filter_ids:0 msgid "Add result of filters" msgstr "" -#. module: advanced_filters -#: model:ir.model,name:advanced_filters.model_ir_filters_combine_with_existing +#. module: web_advanced_filters +#: model:ir.model,name:web_advanced_filters.model_ir_filters_combine_with_existing msgid "Combine a selection with an existing filter" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Are you sure? You can't undo this operation!" msgstr "" -#. module: advanced_filters -#: model:ir.model,name:advanced_filters.model_ir_filters +#. module: web_advanced_filters +#: model:ir.model,name:web_advanced_filters.model_ir_filters msgid "Filters" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 #: view:ir.filters.combine.with.existing:0 msgid "Cancel" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:102 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:102 #, python-format msgid "Whole selection" msgstr "" -#. module: advanced_filters +#. module: web_advanced_filters #: selection:ir.filters.combine.with.existing,action:0 msgid "Complement" msgstr "" diff --git a/web_advanced_filters/i18n/nl.po b/web_advanced_filters/i18n/nl.po index 8f7c43f07..450264824 100644 --- a/web_advanced_filters/i18n/nl.po +++ b/web_advanced_filters/i18n/nl.po @@ -1,5 +1,5 @@ # This file contains the translation of the following modules: -# * advanced_filters +# * web_advanced_filters # msgid "" msgstr "" @@ -14,180 +14,180 @@ msgstr "" "Content-Transfer-Encoding: \n" "Plural-Forms: \n" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Save filter" msgstr "Opslaan filter" -#. module: advanced_filters +#. module: web_advanced_filters #: selection:ir.filters.combine.with.existing,action:0 msgid "Union" msgstr "Union" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Add the result of following filters" msgstr "Voeg resultaat van onderstaande filteringen toe aan filter" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 #: view:ir.filters.combine.with.existing:0 msgid "Save" msgstr "Opslaan" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Freeze filter" msgstr "Bevries filter" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:94 -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:114 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:94 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:114 #, python-format msgid "Remove from existing filter" msgstr "Verwijder uit bestaand filter" -#. module: advanced_filters -#: code:addons/advanced_filters/model/ir_filters.py:131 +#. module: web_advanced_filters +#: code:addons/web_advanced_filters/model/ir_filters.py:131 #, python-format msgid "Testing %s" msgstr "Testen %s" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 #: view:ir.filters.combine.with.existing:0 msgid "or" msgstr "or" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,complement_filter_ids:0 msgid "Remove result of filters" msgstr "Verwijder resultaat van filters" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:86 -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:106 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:86 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:106 #, python-format msgid "To existing filter" msgstr "Voeg toe aan bestaand filter" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters.combine.with.existing:0 msgid "Combine with existing filter" msgstr "Combineer met bestaande filter" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Have this filter contain exactly the records it currently contains, with no changes in the future. Be careful, you can't undo this operation!" msgstr "Laat deze filter precies deze records bevatten, zonder veranderingen in de toekomst. Wees voorzichtig, je kan dit niet meer veranderen!" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:32 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:32 #, python-format msgid "Advanced filters" msgstr "Geavanceerde filters" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,active:0 msgid "Active" msgstr "Actief" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:74 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:74 #, python-format msgid "Marked records" msgstr "Aangevinkte records (verzameling)" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,is_frozen:0 msgid "Frozen" msgstr "Bevroren" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,filter_id:0 msgid "Filter" msgstr "Filter" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Test filter" msgstr "Test filter" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:78 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:78 #, python-format msgid "To new filter" msgstr "Opslaan als nieuw filter" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,domain_this:0 msgid "This filter's own domain" msgstr "Deze filters eigen domein" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Remove the result of following filters" msgstr "Verwijder resultaat van onderstaande filteringen uit filter" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,context:0 msgid "Context" msgstr "Context" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,action:0 msgid "Action" msgstr "Actie" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,model:0 msgid "Model" msgstr "Model" -#. module: advanced_filters +#. module: web_advanced_filters #. openerp-web -#: code:addons/advanced_filters/static/src/js/advanced_filters.js:102 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:102 #, python-format msgid "Whole selection (criteria)" msgstr "Alle records (als criteria)" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters.combine.with.existing,domain:0 msgid "Domain" msgstr "Domein" -#. module: advanced_filters +#. module: web_advanced_filters #: field:ir.filters,union_filter_ids:0 msgid "Add result of filters" msgstr "Voeg resultaat toe aan filters" -#. module: advanced_filters -#: model:ir.model,name:advanced_filters.model_ir_filters_combine_with_existing +#. module: web_advanced_filters +#: model:ir.model,name:web_advanced_filters.model_ir_filters_combine_with_existing msgid "Combine a selection with an existing filter" msgstr "Combineer een selectie met een bestaande filter" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 msgid "Are you sure? You can't undo this operation!" msgstr "Weet u het zeker? U kan deze operatie niet meer ongedaan maken" -#. module: advanced_filters -#: model:ir.model,name:advanced_filters.model_ir_filters +#. module: web_advanced_filters +#: model:ir.model,name:web_advanced_filters.model_ir_filters msgid "Filters" msgstr "Filters" -#. module: advanced_filters +#. module: web_advanced_filters #: view:ir.filters:0 #: view:ir.filters.combine.with.existing:0 msgid "Cancel" msgstr "Annuleren" -#. module: advanced_filters +#. module: web_advanced_filters #: selection:ir.filters.combine.with.existing,action:0 msgid "Complement" msgstr "Aanvullen" diff --git a/web_advanced_filters/static/src/css/advanced_filters.css b/web_advanced_filters/static/src/css/web_advanced_filters.css similarity index 100% rename from web_advanced_filters/static/src/css/advanced_filters.css rename to web_advanced_filters/static/src/css/web_advanced_filters.css diff --git a/web_advanced_filters/static/src/js/advanced_filters.js b/web_advanced_filters/static/src/js/web_advanced_filters.js similarity index 98% rename from web_advanced_filters/static/src/js/advanced_filters.js rename to web_advanced_filters/static/src/js/web_advanced_filters.js index cfea223be..9a1948893 100644 --- a/web_advanced_filters/static/src/js/advanced_filters.js +++ b/web_advanced_filters/static/src/js/web_advanced_filters.js @@ -19,7 +19,7 @@ // //############################################################################ -openerp.advanced_filters = function(instance) +openerp.web_advanced_filters = function(instance) { var _t = instance.web._t; @@ -145,7 +145,7 @@ openerp.advanced_filters = function(instance) ] ), default_context: JSON.stringify({}), - form_view_ref: 'advanced_filters.form_ir_filters_save_new', + form_view_ref: 'web_advanced_filters.form_ir_filters_save_new', }, }, { From ac02061658c2626a824ba11bd4de0d4fadccb0e8 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 24 Nov 2014 15:34:57 +0100 Subject: [PATCH 03/24] [ADD] duplicate saving new filters to menu (usability) --- .../static/src/js/web_advanced_filters.js | 48 ++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/web_advanced_filters/static/src/js/web_advanced_filters.js b/web_advanced_filters/static/src/js/web_advanced_filters.js index 9a1948893..1c3dd505e 100644 --- a/web_advanced_filters/static/src/js/web_advanced_filters.js +++ b/web_advanced_filters/static/src/js/web_advanced_filters.js @@ -58,6 +58,14 @@ openerp.web_advanced_filters = function(instance) label: _t('Whole selection (criteria)'), classname: 'oe_advanced_filters_header', }, + { + label: _t('To new filter'), + callback: function () + { + self.advanced_filters_save_criteria.apply( + self, arguments); + }, + }, { label: _t('To existing filter'), callback: function (item) @@ -117,8 +125,8 @@ openerp.web_advanced_filters = function(instance) $e = jQuery(e) if($e.find('li.oe_advanced_filters_header').length) { - $e.find('a[data-index="3"],a[data-index="4"],' + - 'a[data-index="5"],a[data-index="6"]') + $e.find('a[data-index="4"],a[data-index="5"],' + + 'a[data-index="6"],a[data-index="7"]') .parent().toggle(ids.length > 0); } else @@ -128,6 +136,42 @@ openerp.web_advanced_filters = function(instance) }); } }, + advanced_filters_save_criteria: function(item) + { + var search = this.ViewManager.searchview.build_search_data(), + self = this; + instance.web.pyeval.eval_domains_and_contexts({ + domains: search.domains, + contexts: search.contexts, + group_by_seq: search.groupbys || [] + }).done(function(search) + { + var ctx = search.context; + _(_.keys(instance.session.user_context)).each( + function (key) {delete ctx[key]}); + self.do_action({ + name: item.label, + type: 'ir.actions.act_window', + res_model: 'ir.filters', + views: [[false, 'form']], + target: 'new', + context: { + default_model_id: self.dataset._model.name, + default_domain: JSON.stringify(search.domain), + default_context: JSON.stringify(ctx), + form_view_ref: 'web_advanced_filters.form_ir_filters_save_new', + }, + }, + { + on_close: function() + { + self.ViewManager.setup_search_view( + self.ViewManager.searchview.view_id, + self.ViewManager.searchview.defaults); + }, + }); + }); + }, advanced_filters_save_selection: function(item) { var self = this; From ced4e21fcf780b84cb18838ca3a2d68257ab4111 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 24 Nov 2014 15:53:02 +0100 Subject: [PATCH 04/24] [FIX] use safe_eval to account for names like False --- .../wizard/ir_filters_combine_with_existing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web_advanced_filters/wizard/ir_filters_combine_with_existing.py b/web_advanced_filters/wizard/ir_filters_combine_with_existing.py index 0641caf88..7cb48206c 100644 --- a/web_advanced_filters/wizard/ir_filters_combine_with_existing.py +++ b/web_advanced_filters/wizard/ir_filters_combine_with_existing.py @@ -21,7 +21,7 @@ import time from openerp.osv.orm import TransientModel from openerp.osv import fields, expression -from openerp.tools.safe_eval import const_eval +from openerp.tools.safe_eval import safe_eval class IrFiltersCombineWithExisting(TransientModel): @@ -41,7 +41,7 @@ class IrFiltersCombineWithExisting(TransientModel): def button_save(self, cr, uid, ids, context=None): assert len(ids) == 1 this = self.browse(cr, uid, ids[0], context=context) - domain = const_eval(this.domain) + domain = safe_eval(this.domain) is_frozen = (len(domain) == 1 and expression.is_leaf(domain[0]) and domain[0][0] == 'id') @@ -49,7 +49,7 @@ class IrFiltersCombineWithExisting(TransientModel): if this.action == 'union': if is_frozen and this.filter_id.is_frozen: domain[0][2] = list(set(domain[0][2]).union( - set(const_eval(this.filter_id.domain)[0][2]))) + set(safe_eval(this.filter_id.domain)[0][2]))) this.filter_id.write({'domain': str(domain)}) else: this.filter_id.write( @@ -66,7 +66,7 @@ class IrFiltersCombineWithExisting(TransientModel): }) elif this.action == 'complement': if is_frozen and this.filter_id.is_frozen: - complement_set = set(const_eval(this.filter_id.domain)[0][2]) + complement_set = set(safe_eval(this.filter_id.domain)[0][2]) domain[0][2] = list( complement_set.difference(set(domain[0][2]))) this.filter_id.write({'domain': str(domain)}) From 80b82f0bc739e3e6df956b236f4f93f9e754dd02 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 24 Nov 2014 16:04:27 +0100 Subject: [PATCH 05/24] [FIX] cope with faulty expressions in filters --- web_advanced_filters/model/ir_filters.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/web_advanced_filters/model/ir_filters.py b/web_advanced_filters/model/ir_filters.py index 07b437a4d..fb09d45b0 100644 --- a/web_advanced_filters/model/ir_filters.py +++ b/web_advanced_filters/model/ir_filters.py @@ -32,7 +32,10 @@ class IrFilters(Model): '''determine if this is fixed list of ids''' result = {} for this in self.browse(cr, uid, ids, context=context): - domain = safe_eval(this.domain) + try: + domain = safe_eval(this.domain) + except: + domain = [expression.FALSE_LEAF] result[this.id] = (len(domain) == 1 and expression.is_leaf(domain[0]) and domain[0][0] == 'id') @@ -43,8 +46,13 @@ class IrFilters(Model): this works recursively''' def eval_n(domain): '''parse a domain and normalize it''' + try: + domain = safe_eval(domain) + except: + domain = [expression.FALSE_LEAF] return expression.normalize_domain( - safe_eval(domain) or [expression.FALSE_LEAF]) + domain or [expression.FALSE_LEAF]) + result = {} for this in self.read( cr, uid, ids, From 2abca4145779715789fd6075070651fe1128e3d8 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 24 Nov 2014 16:05:01 +0100 Subject: [PATCH 06/24] [ADD] hide advanced filter menu if there is no filter and nothing checked --- .../static/src/js/web_advanced_filters.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/web_advanced_filters/static/src/js/web_advanced_filters.js b/web_advanced_filters/static/src/js/web_advanced_filters.js index 1c3dd505e..ae1a8ea10 100644 --- a/web_advanced_filters/static/src/js/web_advanced_filters.js +++ b/web_advanced_filters/static/src/js/web_advanced_filters.js @@ -117,6 +117,7 @@ openerp.web_advanced_filters = function(instance) }, advanced_filters_show: function(ids) { + var self = this; if(this.sidebar) { this.sidebar.$el.show(); @@ -125,9 +126,20 @@ openerp.web_advanced_filters = function(instance) $e = jQuery(e) if($e.find('li.oe_advanced_filters_header').length) { + var search = self.ViewManager.searchview + .build_search_data(); + $e.find('a[data-index="0"],a[data-index="1"],' + + 'a[data-index="2"],a[data-index="3"]') + .parent().toggle( + search.contexts.length > 0 || + search.domains.length > 0); $e.find('a[data-index="4"],a[data-index="5"],' + 'a[data-index="6"],a[data-index="7"]') .parent().toggle(ids.length > 0); + $e.toggle( + search.contexts.length > 0 || + search.domains.length > 0 || + ids.length > 0); } else { From b5efc1e5dd5e7d4741418003a4303562e6087cfe Mon Sep 17 00:00:00 2001 From: Rudolf Schnapka Date: Sun, 4 Jan 2015 14:51:33 +0100 Subject: [PATCH 07/24] initial german translations (and added pot-file) --- web_advanced_filters/i18n/de.po | 204 ++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 web_advanced_filters/i18n/de.po diff --git a/web_advanced_filters/i18n/de.po b/web_advanced_filters/i18n/de.po new file mode 100644 index 000000000..52c860b05 --- /dev/null +++ b/web_advanced_filters/i18n/de.po @@ -0,0 +1,204 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * web_advanced_filters +# +# Rudolf Schnapka , 2015. +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-07-29 10:55+0000\n" +"PO-Revision-Date: 2015-01-04 14:05+0100\n" +"Last-Translator: Rudolf Schnapka \n" +"Language-Team: German \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" +"Language: de\n" +"X-Generator: Lokalize 1.5\n" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Save filter" +msgstr "Filter sichern" + +#. module: web_advanced_filters +#: selection:ir.filters.combine.with.existing,action:0 +msgid "Union" +msgstr "Verbund" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Add the result of following filters" +msgstr "Die Ergebnisse der folgenden Filter hinzufügen" + +#. module: web_advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Save" +msgstr "Speichern" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Freeze filter" +msgstr "Filter einfrieren" + +#. module: web_advanced_filters +#. openerp-web +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:94 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:114 +#, python-format +msgid "Remove from existing selection" +msgstr "Aus aktueller Auswahl entfernen" + +#. module: web_advanced_filters +#: code:addons/web_advanced_filters/model/ir_filters.py:131 +#, python-format +msgid "Testing %s" +msgstr "Teste %s" + +#. module: web_advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "or" +msgstr "oder" + +#. module: web_advanced_filters +#: field:ir.filters,complement_filter_ids:0 +msgid "Remove result of filters" +msgstr "Ergebnis der Filter entfernen" + +#. module: web_advanced_filters +#. openerp-web +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:86 +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:106 +#, python-format +msgid "To existing selection" +msgstr "Zu bestehender Auswahl" + +#. module: web_advanced_filters +#: view:ir.filters.combine.with.existing:0 +msgid "Combine with existing filter" +msgstr "Mit bestehendem Filter kombinieren" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "" +"Have this filter contain extly the records it currently contains, with no " +"changes in the future. Be careful, you can't undo this operation!" +msgstr "" +"Diesem Filter genau die aktuellen Datensätze, künftig unveränderlich, " +"zuordnen. Vorsicht, dieser Vorgang kann nicht rückgängig gemacht werden!" + +#. module: web_advanced_filters +#. openerp-web +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:32 +#, python-format +msgid "Advanced filters" +msgstr "Fortgeschrittene Filter" + +#. module: web_advanced_filters +#: field:ir.filters,active:0 +msgid "Active" +msgstr "Aktiv" + +#. module: web_advanced_filters +#. openerp-web +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:74 +#, python-format +msgid "Marked records" +msgstr "Markierte Datensätze" + +#. module: web_advanced_filters +#: field:ir.filters,is_frozen:0 +msgid "Frozen" +msgstr "Eingefroren" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,filter_id:0 +msgid "Filter" +msgstr "Filter" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Test filter" +msgstr "Filtertest" + +#. module: web_advanced_filters +#. openerp-web +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:78 +#, python-format +msgid "To new selection" +msgstr "Zu neuer Auswahl" + +#. module: web_advanced_filters +#: field:ir.filters,domain_this:0 +msgid "This filter's own domain" +msgstr "Dieses Filters eigene Domäne (Abgrenzung)" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Remove the result of following filters" +msgstr "Das Ergebnis des folgenden Filters entfernen" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,context:0 +msgid "Context" +msgstr "Kontext" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,action:0 +msgid "Action" +msgstr "Aktion" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,model:0 +msgid "Model" +msgstr "Modell" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,domain:0 +msgid "Domain" +msgstr "Domäne" + +#. module: web_advanced_filters +#: field:ir.filters,union_filter_ids:0 +msgid "Add result of filters" +msgstr "Ergebnis von Filtern hinzufügen" + +#. module: web_advanced_filters +#: model:ir.model,name:web_advanced_filters.model_ir_filters_combine_with_existing +msgid "Combine a selection with an existing filter" +msgstr "Eine Auswahl mit bestehendem Filter kombinieren" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Are you sure? You can't undo this operation!" +msgstr "" +"Sind Sie sicher? Sie können diesen Vorgang nicht mehr rückgängig machen!" + +#. module: web_advanced_filters +#: model:ir.model,name:web_advanced_filters.model_ir_filters +msgid "Filters" +msgstr "Filter" + +#. module: web_advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Cancel" +msgstr "Abbrechen" + +#. module: web_advanced_filters +#. openerp-web +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:102 +#, python-format +msgid "Whole selection" +msgstr "Gesamte Auswahl" + +#. module: web_advanced_filters +#: selection:ir.filters.combine.with.existing,action:0 +msgid "Complement" +msgstr "Ergänzung" + + From aa72aeb400e42c23e0db773730d05bae29b0a9c1 Mon Sep 17 00:00:00 2001 From: Rudolf Schnapka Date: Sun, 4 Jan 2015 14:54:31 +0100 Subject: [PATCH 08/24] removed excess LF --- web_advanced_filters/i18n/de.po | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web_advanced_filters/i18n/de.po b/web_advanced_filters/i18n/de.po index 52c860b05..0d753437d 100644 --- a/web_advanced_filters/i18n/de.po +++ b/web_advanced_filters/i18n/de.po @@ -175,8 +175,7 @@ msgstr "Eine Auswahl mit bestehendem Filter kombinieren" #. module: web_advanced_filters #: view:ir.filters:0 msgid "Are you sure? You can't undo this operation!" -msgstr "" -"Sind Sie sicher? Sie können diesen Vorgang nicht mehr rückgängig machen!" +msgstr "Sind Sie sicher? Sie können diesen Vorgang nicht mehr rückgängig machen!" #. module: web_advanced_filters #: model:ir.model,name:web_advanced_filters.model_ir_filters From 2a62f9ba596d164984ca1de05eb3e2afa5a9b29d Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 10 Feb 2015 14:59:54 +0100 Subject: [PATCH 09/24] [FIX] evaluate filters if necessary --- web_advanced_filters/model/ir_filters.py | 61 ++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/web_advanced_filters/model/ir_filters.py b/web_advanced_filters/model/ir_filters.py index fb09d45b0..c076a4a98 100644 --- a/web_advanced_filters/model/ir_filters.py +++ b/web_advanced_filters/model/ir_filters.py @@ -27,6 +27,7 @@ from openerp.tools.translate import _ class IrFilters(Model): _inherit = 'ir.filters' + _evaluate_before_negate = ['one2many', 'many2many'] def _is_frozen_get(self, cr, uid, ids, field_name, args, context=None): '''determine if this is fixed list of ids''' @@ -65,17 +66,62 @@ class IrFilters(Model): cr, uid, this['union_filter_ids'], ['domain'], context=context)]) for c in self.read(cr, uid, this['complement_filter_ids'], - ['domain'], context=context): - domain = expression.AND([ - domain, - ['!'] + eval_n(c['domain'])]) - result[this['id']] = str(domain) + ['domain', 'evaluate_before_negate', + 'model_id'], + context=context): + if c['evaluate_before_negate']: + domain = expression.AND([ + domain, + [ + [ + 'id', 'not in', + self.pool[c['model_id']].search( + cr, uid, eval_n(c['domain']), + context=context) + ] + ]]) + else: + domain = expression.AND([ + domain, + ['!'] + eval_n(c['domain'])]) + result[this['id']] = str(expression.normalize_domain(domain)) return result def _domain_set(self, cr, uid, ids, field_name, field_value, args, context=None): self.write(cr, uid, ids, {'domain_this': field_value}) + def _evaluate_before_negate_get(self, cr, uid, ids, field_name, args, + context=None): + """check if this filter contains references to x2many fields. If so, + then negation goes wrong in nearly all cases, so we evaluate the + filter and remove its resulting ids""" + result = {} + for this in self.read(cr, uid, ids, ['model_id', 'domain'], + context=context): + result[this['id']] = False + complement_domain = expression.normalize_domain( + safe_eval(this['domain'] or 'False') or [expression.FALSE_LEAF]) + for arg in complement_domain: + if not expression.is_leaf(arg): + continue + current_model = self.pool.get(this['model_id']) + if not current_model: + continue + has_x2many = False + for field_name in arg[0].split('.'): + field = current_model._all_columns[field_name].column + has_x2many |= field._type in self._evaluate_before_negate + has_x2many |= isinstance(field, fields.function) + if hasattr(field, '_obj'): + current_model = self.pool.get(field._obj) + if not current_model or has_x2many: + break + if has_x2many: + result[this['id']] = True + break + return result + _columns = { 'is_frozen': fields.function( _is_frozen_get, type='boolean', string='Frozen'), @@ -93,6 +139,11 @@ class IrFilters(Model): fnct_inv=_domain_set), 'domain_this': fields.text( 'This filter\'s own domain', oldname='domain'), + 'evaluate_before_negate': fields.function( + _evaluate_before_negate_get, type='boolean', + string='Evaluate this filter before negating', + help='This is necessary if this filter contains positive operators' + 'on x2many fields') } _defaults = { From 006667bca4fce5f94d3b2d91b9be47f1edefd3e0 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 10 Feb 2015 15:01:07 +0100 Subject: [PATCH 10/24] [IMP] allow users to inspect records when testing filters --- web_advanced_filters/model/ir_filters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_advanced_filters/model/ir_filters.py b/web_advanced_filters/model/ir_filters.py index c076a4a98..d09db3dfd 100644 --- a/web_advanced_filters/model/ir_filters.py +++ b/web_advanced_filters/model/ir_filters.py @@ -191,7 +191,7 @@ class IrFilters(Model): 'res_model': this.model_id, 'domain': this.domain, 'view_type': 'form', - 'view_mode': 'tree', + 'view_mode': 'tree,form', 'context': { 'default_filter_id': this.id, }, From 5bae13681695c4b9b2fca84bd878539f6cd3fd9f Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 10 Feb 2015 15:19:21 +0100 Subject: [PATCH 11/24] [IMP] let users edit domain_this if there's other filters, make domain readonly then --- web_advanced_filters/view/ir_filters.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/web_advanced_filters/view/ir_filters.xml b/web_advanced_filters/view/ir_filters.xml index 057db6b1f..32c562e61 100644 --- a/web_advanced_filters/view/ir_filters.xml +++ b/web_advanced_filters/view/ir_filters.xml @@ -21,6 +21,12 @@ + + {'readonly': ['|', ('union_filter_ids', '!=', [[6, False, []]]), ('complement_filter_ids', '!=', [[6, False, []]])]} + + + + From bae5367b4326b93736f1a2f5fb49a52d73e119cb Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Tue, 10 Feb 2015 20:30:19 +0100 Subject: [PATCH 12/24] [FIX] coding style --- web_advanced_filters/model/ir_filters.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web_advanced_filters/model/ir_filters.py b/web_advanced_filters/model/ir_filters.py index d09db3dfd..6d1aedca5 100644 --- a/web_advanced_filters/model/ir_filters.py +++ b/web_advanced_filters/model/ir_filters.py @@ -101,7 +101,8 @@ class IrFilters(Model): context=context): result[this['id']] = False complement_domain = expression.normalize_domain( - safe_eval(this['domain'] or 'False') or [expression.FALSE_LEAF]) + safe_eval(this['domain'] or 'False') + or [expression.FALSE_LEAF]) for arg in complement_domain: if not expression.is_leaf(arg): continue From cfbf7eb88bb898aee44c7dd9f26e25c303f413ca Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 2 Mar 2015 10:28:00 +0100 Subject: [PATCH 13/24] [IMP] more readable code --- web_advanced_filters/model/ir_filters.py | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/web_advanced_filters/model/ir_filters.py b/web_advanced_filters/model/ir_filters.py index 6d1aedca5..72c7a9586 100644 --- a/web_advanced_filters/model/ir_filters.py +++ b/web_advanced_filters/model/ir_filters.py @@ -70,16 +70,13 @@ class IrFilters(Model): 'model_id'], context=context): if c['evaluate_before_negate']: + matching_ids = self.pool[c['model_id']].search( + cr, uid, eval_n(c['domain']), + context=context) domain = expression.AND([ domain, - [ - [ - 'id', 'not in', - self.pool[c['model_id']].search( - cr, uid, eval_n(c['domain']), - context=context) - ] - ]]) + [('id', 'not in', matching_ids)], + ]) else: domain = expression.AND([ domain, @@ -101,8 +98,8 @@ class IrFilters(Model): context=context): result[this['id']] = False complement_domain = expression.normalize_domain( - safe_eval(this['domain'] or 'False') - or [expression.FALSE_LEAF]) + safe_eval(this['domain'] or 'False') or + [expression.FALSE_LEAF]) for arg in complement_domain: if not expression.is_leaf(arg): continue From 7c9992dabcd145e91bb9d7d3b25ad1b5ed641875 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Mon, 2 Mar 2015 17:29:16 +0100 Subject: [PATCH 14/24] Add OCA as author of OCA addons In order to get visibility on https://www.odoo.com/apps the OCA board has decided to add the OCA as author of all the addons maintained as part of the association. --- web_advanced_filters/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_advanced_filters/__openerp__.py b/web_advanced_filters/__openerp__.py index b91430b59..df3bb4adb 100644 --- a/web_advanced_filters/__openerp__.py +++ b/web_advanced_filters/__openerp__.py @@ -21,7 +21,7 @@ { "name": "Advanced filters", "version": "1.0", - "author": "Therp BV", + "author": "Therp BV,Odoo Community Association (OCA)", "license": "AGPL-3", "complexity": "normal", "description": """ From 01c4ed572ead349b775200a7b2b8f0be73e4146f Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 18 Mar 2015 10:10:09 +0100 Subject: [PATCH 15/24] [FIX] evaluate domains containing _auto_join fields before taking unions --- web_advanced_filters/model/ir_filters.py | 56 +++++++++++++++++------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/web_advanced_filters/model/ir_filters.py b/web_advanced_filters/model/ir_filters.py index 72c7a9586..0a58e1b4e 100644 --- a/web_advanced_filters/model/ir_filters.py +++ b/web_advanced_filters/model/ir_filters.py @@ -19,7 +19,7 @@ # ############################################################################## import itertools -from openerp.osv.orm import Model +from openerp.osv.orm import Model, MAGIC_COLUMNS from openerp.osv import fields, expression from openerp.tools.safe_eval import safe_eval from openerp.tools.translate import _ @@ -60,11 +60,19 @@ class IrFilters(Model): ['domain_this', 'union_filter_ids', 'complement_filter_ids'], context=context): domain = eval_n(this['domain_this']) - domain = expression.OR( - [domain] + - [eval_n(u['domain']) for u in self.read( - cr, uid, this['union_filter_ids'], ['domain'], - context=context)]) + for u in self.read(cr, uid, this['union_filter_ids'], + ['domain', 'evaluate_always', 'model_id'], + context=context): + if u['evaluate_always']: + matching_ids = self.pool[u['model_id']].search( + cr, uid, eval_n(u['domain']), + context=context) + domain = expression.OR([ + domain, + [('id', 'in', matching_ids)], + ]) + else: + domain = expression.OR([domain, eval_n(u['domain'])]) for c in self.read(cr, uid, this['complement_filter_ids'], ['domain', 'evaluate_before_negate', 'model_id'], @@ -88,7 +96,7 @@ class IrFilters(Model): context=None): self.write(cr, uid, ids, {'domain_this': field_value}) - def _evaluate_before_negate_get(self, cr, uid, ids, field_name, args, + def _evaluate_get(self, cr, uid, ids, field_name, args, context=None): """check if this filter contains references to x2many fields. If so, then negation goes wrong in nearly all cases, so we evaluate the @@ -96,27 +104,38 @@ class IrFilters(Model): result = {} for this in self.read(cr, uid, ids, ['model_id', 'domain'], context=context): - result[this['id']] = False - complement_domain = expression.normalize_domain( + result[this['id']] = { + 'evaluate_before_negate': False, + 'evaluate_always': False, + } + domain = expression.normalize_domain( safe_eval(this['domain'] or 'False') or [expression.FALSE_LEAF]) - for arg in complement_domain: - if not expression.is_leaf(arg): + for arg in domain: + if not expression.is_leaf(arg) or not isinstance( + arg[0], basestring): continue current_model = self.pool.get(this['model_id']) if not current_model: continue has_x2many = False + has_auto_join = False for field_name in arg[0].split('.'): + if field_name in MAGIC_COLUMNS: + continue field = current_model._all_columns[field_name].column has_x2many |= field._type in self._evaluate_before_negate has_x2many |= isinstance(field, fields.function) + has_auto_join |= field._auto_join + has_auto_join |= isinstance(field, fields.function) if hasattr(field, '_obj'): current_model = self.pool.get(field._obj) - if not current_model or has_x2many: + if not current_model or has_x2many and has_auto_join: break - if has_x2many: - result[this['id']] = True + result[this['id']]['evaluate_before_negate'] |= has_x2many + result[this['id']]['evaluate_always'] |= has_auto_join + if result[this['id']]['evaluate_before_negate'] and\ + result[this['id']]['evaluate_always']: break return result @@ -138,10 +157,15 @@ class IrFilters(Model): 'domain_this': fields.text( 'This filter\'s own domain', oldname='domain'), 'evaluate_before_negate': fields.function( - _evaluate_before_negate_get, type='boolean', + _evaluate_get, type='boolean', multi='evaluate', string='Evaluate this filter before negating', help='This is necessary if this filter contains positive operators' - 'on x2many fields') + 'on x2many fields'), + 'evaluate_always': fields.function( + _evaluate_get, type='boolean', multi='evaluate', + string='Always evaluate this filter before using it', + help='This is necessary if this filter contains x2many fields with' + '_auto_join activated') } _defaults = { From e949159215368fdb8095f7f45b8a2ec0b83f3aa3 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 18 Mar 2015 10:22:38 +0100 Subject: [PATCH 16/24] [FIX] coding style --- web_advanced_filters/model/ir_filters.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/web_advanced_filters/model/ir_filters.py b/web_advanced_filters/model/ir_filters.py index 0a58e1b4e..488c5a020 100644 --- a/web_advanced_filters/model/ir_filters.py +++ b/web_advanced_filters/model/ir_filters.py @@ -61,8 +61,8 @@ class IrFilters(Model): context=context): domain = eval_n(this['domain_this']) for u in self.read(cr, uid, this['union_filter_ids'], - ['domain', 'evaluate_always', 'model_id'], - context=context): + ['domain', 'evaluate_always', 'model_id'], + context=context): if u['evaluate_always']: matching_ids = self.pool[u['model_id']].search( cr, uid, eval_n(u['domain']), @@ -96,8 +96,7 @@ class IrFilters(Model): context=None): self.write(cr, uid, ids, {'domain_this': field_value}) - def _evaluate_get(self, cr, uid, ids, field_name, args, - context=None): + def _evaluate_get(self, cr, uid, ids, field_name, args, context=None): """check if this filter contains references to x2many fields. If so, then negation goes wrong in nearly all cases, so we evaluate the filter and remove its resulting ids""" From 7aad31543b5febc378f6a28cf69b690e6abc5a25 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Fri, 17 Apr 2015 08:48:47 +0200 Subject: [PATCH 17/24] [FIX] don't eval() json, but json.loads it --- .../wizard/ir_filters_combine_with_existing.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web_advanced_filters/wizard/ir_filters_combine_with_existing.py b/web_advanced_filters/wizard/ir_filters_combine_with_existing.py index 7cb48206c..b6d6668c0 100644 --- a/web_advanced_filters/wizard/ir_filters_combine_with_existing.py +++ b/web_advanced_filters/wizard/ir_filters_combine_with_existing.py @@ -19,6 +19,7 @@ # ############################################################################## import time +import json from openerp.osv.orm import TransientModel from openerp.osv import fields, expression from openerp.tools.safe_eval import safe_eval @@ -41,7 +42,7 @@ class IrFiltersCombineWithExisting(TransientModel): def button_save(self, cr, uid, ids, context=None): assert len(ids) == 1 this = self.browse(cr, uid, ids[0], context=context) - domain = safe_eval(this.domain) + domain = json.loads(this.domain) is_frozen = (len(domain) == 1 and expression.is_leaf(domain[0]) and domain[0][0] == 'id') From c0904e3aaea61cb22171a5c33e7095bac9868824 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 20 Apr 2015 09:31:39 +0200 Subject: [PATCH 18/24] [IMP] let user decide if a filter is private when saving it for filter combinations, inherit user_id from the filter we combine it with --- web_advanced_filters/static/src/js/web_advanced_filters.js | 2 ++ web_advanced_filters/view/ir_filters.xml | 1 + .../wizard/ir_filters_combine_with_existing.py | 4 ++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/web_advanced_filters/static/src/js/web_advanced_filters.js b/web_advanced_filters/static/src/js/web_advanced_filters.js index ae1a8ea10..e34ec0233 100644 --- a/web_advanced_filters/static/src/js/web_advanced_filters.js +++ b/web_advanced_filters/static/src/js/web_advanced_filters.js @@ -171,6 +171,7 @@ openerp.web_advanced_filters = function(instance) default_model_id: self.dataset._model.name, default_domain: JSON.stringify(search.domain), default_context: JSON.stringify(ctx), + default_user_id: JSON.stringify(false), form_view_ref: 'web_advanced_filters.form_ir_filters_save_new', }, }, @@ -201,6 +202,7 @@ openerp.web_advanced_filters = function(instance) ] ), default_context: JSON.stringify({}), + default_user_id: JSON.stringify(false), form_view_ref: 'web_advanced_filters.form_ir_filters_save_new', }, }, diff --git a/web_advanced_filters/view/ir_filters.xml b/web_advanced_filters/view/ir_filters.xml index 32c562e61..10c1f3275 100644 --- a/web_advanced_filters/view/ir_filters.xml +++ b/web_advanced_filters/view/ir_filters.xml @@ -37,6 +37,7 @@ +