diff --git a/web_advanced_filters/__init__.py b/web_advanced_filters/__init__.py new file mode 100644 index 00000000..500e771d --- /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 00000000..b91430b5 --- /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/web_advanced_filters.js', + ], + "css": [ + 'static/src/css/web_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 00000000..0219058d --- /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 00000000..5691d68c --- /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: +# * web_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: web_advanced_filters +#: view:ir.filters:0 +msgid "Save filter" +msgstr "" + +#. module: web_advanced_filters +#: selection:ir.filters.combine.with.existing,action:0 +msgid "Union" +msgstr "" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Add the result of following filters" +msgstr "" + +#. module: web_advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Save" +msgstr "" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Freeze filter" +msgstr "" + +#. 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 "" + +#. module: web_advanced_filters +#: code:addons/web_advanced_filters/model/ir_filters.py:131 +#, python-format +msgid "Testing %s" +msgstr "" + +#. module: web_advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "or" +msgstr "" + +#. module: web_advanced_filters +#: field:ir.filters,complement_filter_ids:0 +msgid "Remove result of filters" +msgstr "" + +#. 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 "" + +#. module: web_advanced_filters +#: view:ir.filters.combine.with.existing:0 +msgid "Combine with existing filter" +msgstr "" + +#. 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: web_advanced_filters +#. openerp-web +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:32 +#, python-format +msgid "Advanced filters" +msgstr "" + +#. module: web_advanced_filters +#: field:ir.filters,active:0 +msgid "Active" +msgstr "" + +#. 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 "" + +#. module: web_advanced_filters +#: field:ir.filters,is_frozen:0 +msgid "Frozen" +msgstr "" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,filter_id:0 +msgid "Filter" +msgstr "" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Test filter" +msgstr "" + +#. 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 "" + +#. module: web_advanced_filters +#: field:ir.filters,domain_this:0 +msgid "This filter's own domain" +msgstr "" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Remove the result of following filters" +msgstr "" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,context:0 +msgid "Context" +msgstr "" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,action:0 +msgid "Action" +msgstr "" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,model:0 +msgid "Model" +msgstr "" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,domain:0 +msgid "Domain" +msgstr "" + +#. module: web_advanced_filters +#: field:ir.filters,union_filter_ids:0 +msgid "Add result of filters" +msgstr "" + +#. 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: web_advanced_filters +#: view:ir.filters:0 +msgid "Are you sure? You can't undo this operation!" +msgstr "" + +#. module: web_advanced_filters +#: model:ir.model,name:web_advanced_filters.model_ir_filters +msgid "Filters" +msgstr "" + +#. module: web_advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Cancel" +msgstr "" + +#. 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 "" + +#. 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 new file mode 100644 index 00000000..45026482 --- /dev/null +++ b/web_advanced_filters/i18n/nl.po @@ -0,0 +1,194 @@ +# This file contains the translation of the following modules: +# * web_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: web_advanced_filters +#: view:ir.filters:0 +msgid "Save filter" +msgstr "Opslaan filter" + +#. module: web_advanced_filters +#: selection:ir.filters.combine.with.existing,action:0 +msgid "Union" +msgstr "Union" + +#. 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: web_advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Save" +msgstr "Opslaan" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Freeze filter" +msgstr "Bevries filter" + +#. 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 filter" +msgstr "Verwijder uit bestaand filter" + +#. module: web_advanced_filters +#: code:addons/web_advanced_filters/model/ir_filters.py:131 +#, python-format +msgid "Testing %s" +msgstr "Testen %s" + +#. module: web_advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "or" +msgstr "or" + +#. module: web_advanced_filters +#: field:ir.filters,complement_filter_ids:0 +msgid "Remove result of filters" +msgstr "Verwijder resultaat van filters" + +#. 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 filter" +msgstr "Voeg toe aan bestaand filter" + +#. module: web_advanced_filters +#: view:ir.filters.combine.with.existing:0 +msgid "Combine with existing filter" +msgstr "Combineer met bestaande filter" + +#. 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: web_advanced_filters +#. openerp-web +#: code:addons/web_advanced_filters/static/src/js/web_advanced_filters.js:32 +#, python-format +msgid "Advanced filters" +msgstr "Geavanceerde filters" + +#. module: web_advanced_filters +#: field:ir.filters,active:0 +msgid "Active" +msgstr "Actief" + +#. 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 "Aangevinkte records (verzameling)" + +#. module: web_advanced_filters +#: field:ir.filters,is_frozen:0 +msgid "Frozen" +msgstr "Bevroren" + +#. 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 "Test filter" + +#. module: web_advanced_filters +#. openerp-web +#: 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: web_advanced_filters +#: field:ir.filters,domain_this:0 +msgid "This filter's own domain" +msgstr "Deze filters eigen domein" + +#. module: web_advanced_filters +#: view:ir.filters:0 +msgid "Remove the result of following filters" +msgstr "Verwijder resultaat van onderstaande filteringen uit filter" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,context:0 +msgid "Context" +msgstr "Context" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,action:0 +msgid "Action" +msgstr "Actie" + +#. module: web_advanced_filters +#: field:ir.filters.combine.with.existing,model:0 +msgid "Model" +msgstr "Model" + +#. module: web_advanced_filters +#. openerp-web +#: 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: web_advanced_filters +#: field:ir.filters.combine.with.existing,domain:0 +msgid "Domain" +msgstr "Domein" + +#. module: web_advanced_filters +#: field:ir.filters,union_filter_ids:0 +msgid "Add result of filters" +msgstr "Voeg resultaat toe aan filters" + +#. 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: 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: web_advanced_filters +#: model:ir.model,name:web_advanced_filters.model_ir_filters +msgid "Filters" +msgstr "Filters" + +#. module: web_advanced_filters +#: view:ir.filters:0 +#: view:ir.filters.combine.with.existing:0 +msgid "Cancel" +msgstr "Annuleren" + +#. module: web_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 00000000..bbae0587 --- /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 00000000..fb09d45b --- /dev/null +++ b/web_advanced_filters/model/ir_filters.py @@ -0,0 +1,175 @@ +# -*- 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): + 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') + 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''' + try: + domain = safe_eval(domain) + except: + domain = [expression.FALSE_LEAF] + return expression.normalize_domain( + 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/web_advanced_filters.css b/web_advanced_filters/static/src/css/web_advanced_filters.css new file mode 100644 index 00000000..06a3c8ec --- /dev/null +++ b/web_advanced_filters/static/src/css/web_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 00000000..29372604 Binary files /dev/null and b/web_advanced_filters/static/src/img/icon.png differ diff --git a/web_advanced_filters/static/src/js/web_advanced_filters.js b/web_advanced_filters/static/src/js/web_advanced_filters.js new file mode 100644 index 00000000..ae1a8ea1 --- /dev/null +++ b/web_advanced_filters/static/src/js/web_advanced_filters.js @@ -0,0 +1,270 @@ +//-*- 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 . +// +//############################################################################ + +openerp.web_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 new filter'), + callback: function () + { + self.advanced_filters_save_criteria.apply( + self, arguments); + }, + }, + { + 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) + { + var self = this; + 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) + { + 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 + { + $e.toggle(ids.length > 0); + } + }); + } + }, + 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; + 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: '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_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 00000000..057db6b1 --- /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 00000000..608ed687 --- /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 00000000..7cb48206 --- /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 safe_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 = safe_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(safe_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(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)}) + 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 00000000..bc7028d3 --- /dev/null +++ b/web_advanced_filters/wizard/ir_filters_combine_with_existing.xml @@ -0,0 +1,21 @@ + + + + + ir.filters.combine.with.existing + +
+ + + + + +
+
+
+
+