From 37125e826bc16962e29c67e7ccef8ce75e53197f Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 28 Jul 2014 10:10:09 +0200 Subject: [PATCH] [ADD] advanced_filters --- advanced_filters/__init__.py | 22 ++ advanced_filters/__openerp__.py | 78 +++++++ advanced_filters/model/__init__.py | 21 ++ advanced_filters/model/ir_filters.py | 149 +++++++++++++ .../static/src/css/advanced_filters.css | 14 ++ advanced_filters/static/src/img/icon.png | Bin 0 -> 16070 bytes .../static/src/js/advanced_filters.js | 209 ++++++++++++++++++ advanced_filters/view/ir_filters.xml | 44 ++++ advanced_filters/wizard/__init__.py | 21 ++ .../ir_filters_combine_with_existing.py | 87 ++++++++ .../ir_filters_combine_with_existing.xml | 21 ++ 11 files changed, 666 insertions(+) create mode 100644 advanced_filters/__init__.py create mode 100644 advanced_filters/__openerp__.py create mode 100644 advanced_filters/model/__init__.py create mode 100644 advanced_filters/model/ir_filters.py create mode 100644 advanced_filters/static/src/css/advanced_filters.css create mode 100644 advanced_filters/static/src/img/icon.png create mode 100644 advanced_filters/static/src/js/advanced_filters.js create mode 100644 advanced_filters/view/ir_filters.xml create mode 100644 advanced_filters/wizard/__init__.py create mode 100644 advanced_filters/wizard/ir_filters_combine_with_existing.py create mode 100644 advanced_filters/wizard/ir_filters_combine_with_existing.xml diff --git a/advanced_filters/__init__.py b/advanced_filters/__init__.py new file mode 100644 index 00000000..500e771d --- /dev/null +++ b/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/advanced_filters/__openerp__.py b/advanced_filters/__openerp__.py new file mode 100644 index 00000000..3cd69411 --- /dev/null +++ b/advanced_filters/__openerp__.py @@ -0,0 +1,78 @@ +# -*- 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": [ + "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/advanced_filters/model/__init__.py b/advanced_filters/model/__init__.py new file mode 100644 index 00000000..bbae0587 --- /dev/null +++ b/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/advanced_filters/model/ir_filters.py b/advanced_filters/model/ir_filters.py new file mode 100644 index 00000000..a709a52a --- /dev/null +++ b/advanced_filters/model/ir_filters.py @@ -0,0 +1,149 @@ +# -*- 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 const_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 = const_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( + const_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, const_eval(this.domain), context=const_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 [('ids', '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=None): + 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) diff --git a/advanced_filters/static/src/css/advanced_filters.css b/advanced_filters/static/src/css/advanced_filters.css new file mode 100644 index 00000000..06a3c8ec --- /dev/null +++ b/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/advanced_filters/static/src/img/icon.png b/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); + 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="0"],a[data-index="1"],' + + 'a[data-index="2"],a[data-index="3"]') + .parent().toggle(ids.length); + } + else + { + $e.toggle(ids.length); + } + }); + } + return result; + }, + load_list: function(data) + { + var result = this._super.apply(this, arguments), + self = this; + if(!this.sidebar || this.sidebar.items.advanced_filters.length) + { + return result; + } + this.sidebar.add_items( + 'advanced_filters', + [ + { + label: _t('Marked records'), + classname: 'oe_advanced_filters_header', + }, + { + label: _t('To new selection'), + callback: function () + { + self.advanced_filters_save_selection.apply( + self, arguments); + }, + }, + { + label: _t('To existing selection'), + callback: function (item) + { + self.advanced_filters_combine_with_existing.apply( + self, ['union', 'ids', item]); + }, + }, + { + label: _t('Remove from existing selection'), + callback: function (item) + { + self.advanced_filters_combine_with_existing.apply( + self, ['complement', 'ids', item]); + }, + }, + { + label: _t('Whole result set'), + classname: 'oe_advanced_filters_header', + }, + { + label: _t('To existing selection'), + callback: function (item) + { + self.advanced_filters_combine_with_existing.apply( + self, ['union', 'domain', item]); + }, + }, + { + label: _t('Remove from existing selection'), + callback: function (item) + { + self.advanced_filters_combine_with_existing.apply( + self, ['complement', 'domain', item]); + }, + }, + ] + ); + this.do_select([], []); + return result; + }, + 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/advanced_filters/view/ir_filters.xml b/advanced_filters/view/ir_filters.xml new file mode 100644 index 00000000..057db6b1 --- /dev/null +++ b/advanced_filters/view/ir_filters.xml @@ -0,0 +1,44 @@ + + + + + ir.filters + + + +
+
+
+ + + + + + + + + + +
+
+ + ir.filters + 999 + +
+ + + + +
+
+
+
+
+
+
diff --git a/advanced_filters/wizard/__init__.py b/advanced_filters/wizard/__init__.py new file mode 100644 index 00000000..608ed687 --- /dev/null +++ b/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/advanced_filters/wizard/ir_filters_combine_with_existing.py b/advanced_filters/wizard/ir_filters_combine_with_existing.py new file mode 100644 index 00000000..0641caf8 --- /dev/null +++ b/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/advanced_filters/wizard/ir_filters_combine_with_existing.xml b/advanced_filters/wizard/ir_filters_combine_with_existing.xml new file mode 100644 index 00000000..bc7028d3 --- /dev/null +++ b/advanced_filters/wizard/ir_filters_combine_with_existing.xml @@ -0,0 +1,21 @@ + + + + + ir.filters.combine.with.existing + +
+ + + + + +
+
+
+
+