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.

259 lines
11 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, MAGIC_COLUMNS
  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. for u in self.read(cr, uid, this['union_filter_ids'],
  59. ['domain', 'evaluate_always', 'model_id'],
  60. context=context):
  61. if u['evaluate_always']:
  62. matching_ids = self.pool[u['model_id']].search(
  63. cr, uid, eval_n(u['domain']),
  64. context=context)
  65. domain = expression.OR([
  66. domain,
  67. [('id', 'in', matching_ids)],
  68. ])
  69. else:
  70. domain = expression.OR([domain, eval_n(u['domain'])])
  71. for c in self.read(cr, uid, this['complement_filter_ids'],
  72. ['domain', 'evaluate_before_negate',
  73. 'model_id'],
  74. context=context):
  75. if c['evaluate_before_negate']:
  76. matching_ids = self.pool[c['model_id']].search(
  77. cr, uid, eval_n(c['domain']),
  78. context=context)
  79. domain = expression.AND([
  80. domain,
  81. [('id', 'not in', matching_ids)],
  82. ])
  83. else:
  84. domain = expression.AND([
  85. domain,
  86. ['!'] + eval_n(c['domain'])])
  87. result[this['id']] = str(expression.normalize_domain(domain))
  88. return result
  89. def _domain_set(self, cr, uid, ids, field_name, field_value, args,
  90. context=None):
  91. self.write(cr, uid, ids, {'domain_this': field_value})
  92. def _evaluate_get(self, cr, uid, ids, field_name, args, context=None):
  93. """check if this filter contains references to x2many fields. If so,
  94. then negation goes wrong in nearly all cases, so we evaluate the
  95. filter and remove its resulting ids"""
  96. result = {}
  97. for this in self.read(cr, uid, ids, ['model_id', 'domain'],
  98. context=context):
  99. result[this['id']] = {
  100. 'evaluate_before_negate': False,
  101. 'evaluate_always': False,
  102. }
  103. domain = expression.normalize_domain(
  104. safe_eval(this['domain'] or 'False') or
  105. [expression.FALSE_LEAF])
  106. for arg in domain:
  107. if not expression.is_leaf(arg) or not isinstance(
  108. arg[0], basestring):
  109. continue
  110. current_model = self.pool.get(this['model_id'])
  111. if not current_model:
  112. continue
  113. has_x2many = False
  114. has_auto_join = False
  115. for field_name in arg[0].split('.'):
  116. if field_name in MAGIC_COLUMNS:
  117. continue
  118. field = current_model._all_columns[field_name].column
  119. has_x2many |= field._type in self._evaluate_before_negate
  120. has_x2many |= isinstance(field, fields.function)
  121. has_auto_join |= field._auto_join
  122. has_auto_join |= isinstance(field, fields.function)
  123. if hasattr(field, '_obj'):
  124. current_model = self.pool.get(field._obj)
  125. if not current_model or has_x2many and has_auto_join:
  126. break
  127. result[this['id']]['evaluate_before_negate'] |= has_x2many
  128. result[this['id']]['evaluate_always'] |= has_auto_join
  129. if result[this['id']]['evaluate_before_negate'] and\
  130. result[this['id']]['evaluate_always']:
  131. break
  132. return result
  133. _columns = {
  134. 'is_frozen': fields.function(
  135. _is_frozen_get, type='boolean', string='Frozen'),
  136. 'union_filter_ids': fields.many2many(
  137. 'ir.filters', 'ir_filters_union_rel', 'left_filter_id',
  138. 'right_filter_id', 'Add result of filters',
  139. domain=['|', ('active', '=', False), ('active', '=', True)]),
  140. 'complement_filter_ids': fields.many2many(
  141. 'ir.filters', 'ir_filters_complement_rel', 'left_filter_id',
  142. 'right_filter_id', 'Remove result of filters',
  143. domain=['|', ('active', '=', False), ('active', '=', True)]),
  144. 'active': fields.boolean('Active'),
  145. 'domain': fields.function(
  146. _domain_get, type='text', string='Domain',
  147. fnct_inv=_domain_set),
  148. 'domain_this': fields.text(
  149. 'This filter\'s own domain', oldname='domain'),
  150. 'evaluate_before_negate': fields.function(
  151. _evaluate_get, type='boolean', multi='evaluate',
  152. string='Evaluate this filter before negating',
  153. help='This is necessary if this filter contains positive operators'
  154. 'on x2many fields'),
  155. 'evaluate_always': fields.function(
  156. _evaluate_get, type='boolean', multi='evaluate',
  157. string='Always evaluate this filter before using it',
  158. help='This is necessary if this filter contains x2many fields with'
  159. '_auto_join activated'),
  160. 'save_as_public': fields.function(
  161. lambda self, cr, uid, ids, *args, **kwargs:
  162. dict((i, False) for i in ids),
  163. fnct_inv=lambda *args, **kwargs: None,
  164. type='boolean',
  165. string='Share with all users'),
  166. }
  167. _defaults = {
  168. 'active': True,
  169. 'save_as_public': False,
  170. }
  171. def _evaluate(self, cr, uid, ids, context=None):
  172. assert len(ids) == 1
  173. this = self.browse(cr, uid, ids[0], context=context)
  174. return self.pool[this.model_id].search(
  175. cr, uid, safe_eval(this.domain), context=safe_eval(this.context))
  176. def button_save(self, cr, uid, ids, context=None):
  177. return {'type': 'ir.actions.act_window.close'}
  178. def button_freeze(self, cr, uid, ids, context=None):
  179. '''evaluate the filter and write a fixed [('id', 'in', [])] domain'''
  180. for this in self.browse(cr, uid, ids, context=context):
  181. ids = this._evaluate()
  182. removed_filter_ids = [f.id for f in itertools.chain(
  183. this.union_filter_ids, this.complement_filter_ids)]
  184. this.write({
  185. 'domain': str([('id', 'in', ids)]),
  186. 'union_filter_ids': [(6, 0, [])],
  187. 'complement_filter_ids': [(6, 0, [])],
  188. })
  189. # if we removed inactive filters which are orphaned now, delete
  190. # them
  191. cr.execute('''delete from ir_filters
  192. where
  193. not active and id in %s
  194. and not exists (select right_filter_id
  195. from ir_filters_union_rel where left_filter_id=id)
  196. and not exists (select right_filter_id
  197. from ir_filters_complement_rel where
  198. left_filter_id=id)
  199. ''',
  200. (tuple(removed_filter_ids),))
  201. def button_test(self, cr, uid, ids, context=None):
  202. for this in self.browse(cr, uid, ids, context=context):
  203. return {
  204. 'type': 'ir.actions.act_window',
  205. 'name': _('Testing %s') % this.name,
  206. 'res_model': this.model_id,
  207. 'domain': this.domain,
  208. 'view_type': 'form',
  209. 'view_mode': 'tree,form',
  210. 'context': {
  211. 'default_filter_id': this.id,
  212. },
  213. }
  214. def _auto_init(self, cr, context=None):
  215. cr.execute(
  216. 'SELECT count(attname) FROM pg_attribute '
  217. 'WHERE attrelid = '
  218. '( SELECT oid FROM pg_class WHERE relname = %s) '
  219. 'AND attname = %s', (self._table, 'domain_this'))
  220. if not cr.fetchone()[0]:
  221. cr.execute(
  222. 'ALTER table %s RENAME domain TO domain_this' % self._table)
  223. return super(IrFilters, self)._auto_init(cr, context=context)
  224. def _migrate_name_change(self, cr, uid, context=None):
  225. cr.execute(
  226. "select id from ir_module_module where name='advanced_filters' "
  227. "and author='Therp BV'")
  228. old_name_installed = cr.fetchall()
  229. if not old_name_installed:
  230. return
  231. cr.execute(
  232. "delete from ir_model_data where module='web_advanced_filters'")
  233. cr.execute(
  234. "update ir_model_data set module='web_advanced_filters' "
  235. "where module='advanced_filters'")
  236. cr.execute(
  237. "update ir_module_module set state='to remove' where "
  238. "name='advanced_filters' and state not in "
  239. "('uninstalled', 'to remove')")
  240. def create(self, cr, uid, values, context=None):
  241. values.setdefault(
  242. 'user_id', False if values.get('save_as_public') else uid)
  243. return super(IrFilters, self).create(cr, uid, values, context=context)