You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
9.6 KiB

9 years ago
10 years ago
  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # OpenERP, Open Source Management Solution
  5. # This module copyright (C) 2014 Therp BV (<http://therp.nl>).
  6. #
  7. # This program is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU Affero General Public License as
  9. # published by the Free Software Foundation, either version 3 of the
  10. # License, or (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU Affero General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU Affero General Public License
  18. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. #
  20. ##############################################################################
  21. import itertools
  22. from openerp.osv.orm import Model
  23. from openerp.osv import fields, expression
  24. from openerp.tools.safe_eval import safe_eval
  25. from openerp.tools.translate import _
  26. class IrFilters(Model):
  27. _inherit = 'ir.filters'
  28. _evaluate_before_negate = ['one2many', 'many2many']
  29. def _is_frozen_get(self, cr, uid, ids, field_name, args, context=None):
  30. '''determine if this is fixed list of ids'''
  31. result = {}
  32. for this in self.browse(cr, uid, ids, context=context):
  33. try:
  34. domain = safe_eval(this.domain)
  35. except:
  36. domain = [expression.FALSE_LEAF]
  37. result[this.id] = (len(domain) == 1 and
  38. expression.is_leaf(domain[0]) and
  39. domain[0][0] == 'id')
  40. return result
  41. def _domain_get(self, cr, uid, ids, field_name, args, context=None):
  42. '''combine our domain with all domains to union/complement,
  43. this works recursively'''
  44. def eval_n(domain):
  45. '''parse a domain and normalize it'''
  46. try:
  47. domain = safe_eval(domain)
  48. except:
  49. domain = [expression.FALSE_LEAF]
  50. return expression.normalize_domain(
  51. domain or [expression.FALSE_LEAF])
  52. result = {}
  53. for this in self.read(
  54. cr, uid, ids,
  55. ['domain_this', 'union_filter_ids', 'complement_filter_ids'],
  56. context=context):
  57. domain = eval_n(this['domain_this'])
  58. domain = expression.OR(
  59. [domain] +
  60. [eval_n(u['domain']) for u in self.read(
  61. cr, uid, this['union_filter_ids'], ['domain'],
  62. context=context)])
  63. for c in self.read(cr, uid, this['complement_filter_ids'],
  64. ['domain', 'evaluate_before_negate',
  65. 'model_id'],
  66. context=context):
  67. if c['evaluate_before_negate']:
  68. matching_ids = self.pool[c['model_id']].search(
  69. cr, uid, eval_n(c['domain']),
  70. context=context)
  71. domain = expression.AND([
  72. domain,
  73. [('id', 'not in', matching_ids)],
  74. ])
  75. else:
  76. domain = expression.AND([
  77. domain,
  78. ['!'] + eval_n(c['domain'])])
  79. result[this['id']] = str(expression.normalize_domain(domain))
  80. return result
  81. def _domain_set(self, cr, uid, ids, field_name, field_value, args,
  82. context=None):
  83. self.write(cr, uid, ids, {'domain_this': field_value})
  84. def _evaluate_before_negate_get(self, cr, uid, ids, field_name, args,
  85. context=None):
  86. """check if this filter contains references to x2many fields. If so,
  87. then negation goes wrong in nearly all cases, so we evaluate the
  88. filter and remove its resulting ids"""
  89. result = {}
  90. for this in self.read(cr, uid, ids, ['model_id', 'domain'],
  91. context=context):
  92. result[this['id']] = False
  93. complement_domain = expression.normalize_domain(
  94. safe_eval(this['domain'] or 'False') or
  95. [expression.FALSE_LEAF])
  96. for arg in complement_domain:
  97. if not expression.is_leaf(arg):
  98. continue
  99. current_model = self.pool.get(this['model_id'])
  100. if not current_model:
  101. continue
  102. has_x2many = False
  103. for field_name in arg[0].split('.'):
  104. field = current_model._all_columns[field_name].column
  105. has_x2many |= field._type in self._evaluate_before_negate
  106. has_x2many |= isinstance(field, fields.function)
  107. if hasattr(field, '_obj'):
  108. current_model = self.pool.get(field._obj)
  109. if not current_model or has_x2many:
  110. break
  111. if has_x2many:
  112. result[this['id']] = True
  113. break
  114. return result
  115. _columns = {
  116. 'is_frozen': fields.function(
  117. _is_frozen_get, type='boolean', string='Frozen'),
  118. 'union_filter_ids': fields.many2many(
  119. 'ir.filters', 'ir_filters_union_rel', 'left_filter_id',
  120. 'right_filter_id', 'Add result of filters',
  121. domain=['|', ('active', '=', False), ('active', '=', True)]),
  122. 'complement_filter_ids': fields.many2many(
  123. 'ir.filters', 'ir_filters_complement_rel', 'left_filter_id',
  124. 'right_filter_id', 'Remove result of filters',
  125. domain=['|', ('active', '=', False), ('active', '=', True)]),
  126. 'active': fields.boolean('Active'),
  127. 'domain': fields.function(
  128. _domain_get, type='text', string='Domain',
  129. fnct_inv=_domain_set),
  130. 'domain_this': fields.text(
  131. 'This filter\'s own domain', oldname='domain'),
  132. 'evaluate_before_negate': fields.function(
  133. _evaluate_before_negate_get, type='boolean',
  134. string='Evaluate this filter before negating',
  135. help='This is necessary if this filter contains positive operators'
  136. 'on x2many fields')
  137. }
  138. _defaults = {
  139. 'active': True,
  140. }
  141. def _evaluate(self, cr, uid, ids, context=None):
  142. assert len(ids) == 1
  143. this = self.browse(cr, uid, ids[0], context=context)
  144. return self.pool[this.model_id].search(
  145. cr, uid, safe_eval(this.domain), context=safe_eval(this.context))
  146. def button_save(self, cr, uid, ids, context=None):
  147. return {'type': 'ir.actions.act_window.close'}
  148. def button_freeze(self, cr, uid, ids, context=None):
  149. '''evaluate the filter and write a fixed [('id', 'in', [])] domain'''
  150. for this in self.browse(cr, uid, ids, context=context):
  151. ids = this._evaluate()
  152. removed_filter_ids = [f.id for f in itertools.chain(
  153. this.union_filter_ids, this.complement_filter_ids)]
  154. this.write({
  155. 'domain': str([('id', 'in', ids)]),
  156. 'union_filter_ids': [(6, 0, [])],
  157. 'complement_filter_ids': [(6, 0, [])],
  158. })
  159. # if we removed inactive filters which are orphaned now, delete
  160. # them
  161. cr.execute('''delete from ir_filters
  162. where
  163. not active and id in %s
  164. and not exists (select right_filter_id
  165. from ir_filters_union_rel where left_filter_id=id)
  166. and not exists (select right_filter_id
  167. from ir_filters_complement_rel where
  168. left_filter_id=id)
  169. ''',
  170. (tuple(removed_filter_ids),))
  171. def button_test(self, cr, uid, ids, context=None):
  172. for this in self.browse(cr, uid, ids, context=context):
  173. return {
  174. 'type': 'ir.actions.act_window',
  175. 'name': _('Testing %s') % this.name,
  176. 'res_model': this.model_id,
  177. 'domain': this.domain,
  178. 'view_type': 'form',
  179. 'view_mode': 'tree,form',
  180. 'context': {
  181. 'default_filter_id': this.id,
  182. },
  183. }
  184. def _auto_init(self, cr, context=None):
  185. cr.execute(
  186. 'SELECT count(attname) FROM pg_attribute '
  187. 'WHERE attrelid = '
  188. '( SELECT oid FROM pg_class WHERE relname = %s) '
  189. 'AND attname = %s', (self._table, 'domain_this'))
  190. if not cr.fetchone()[0]:
  191. cr.execute(
  192. 'ALTER table %s RENAME domain TO domain_this' % self._table)
  193. return super(IrFilters, self)._auto_init(cr, context=context)
  194. def _migrate_name_change(self, cr, uid, context=None):
  195. cr.execute(
  196. "select id from ir_module_module where name='advanced_filters' "
  197. "and author='Therp BV'")
  198. old_name_installed = cr.fetchall()
  199. if not old_name_installed:
  200. return
  201. cr.execute(
  202. "delete from ir_model_data where module='web_advanced_filters'")
  203. cr.execute(
  204. "update ir_model_data set module='web_advanced_filters' "
  205. "where module='advanced_filters'")
  206. cr.execute(
  207. "update ir_module_module set state='to remove' where "
  208. "name='advanced_filters' and state not in "
  209. "('uninstalled', 'to remove')")