[FIX] expression: allow exclusion of records with translated fields
authorChristophe Simonis <chs@openerp.com>
Tue, 7 Jan 2014 11:22:27 +0000 (12:22 +0100)
committerChristophe Simonis <chs@openerp.com>
Tue, 7 Jan 2014 11:22:27 +0000 (12:22 +0100)
Manual forwardport of revid 3672 chs@openerp.com-20140106195143-u3gxmfm9ifg9k3u0 from 6.0 branch

lp bug: https://launchpad.net/bugs/1071710 fixed

bzr revid: chs@openerp.com-20140107112227-sc1ssd1dw404vcz2

openerp/addons/base/test/test_osv_expression.yml
openerp/osv/expression.py

index d9a1b21..3de0c92 100644 (file)
     !python {model: res.partner }: |
         res_ids = self.search(cr, uid, [('address.country_id','=','Belgium'),('active','=',False)])
         assert len(res_ids) != 0, "Record not Found with country Belgium and active False."
+-
+    Check that we can exclude translated fields (bug lp:1071710)
+-
+    !python {model: res.country}: |
+        # first install french language
+        BLI = self.pool.get('base.language.install')
+        BLI.lang_install(cr, uid, [BLI.create(cr, uid, {'lang': 'fr_FR'})])
+
+        be_id = self.search(cr, uid, [('name', '=', 'Belgium')])[0]
+
+        ctx = {'lang': 'fr_FR'}
+        not_be = self.search(cr, uid, [('name', '!=', 'Belgique')], context=ctx)
 
+        assert be_id not in not_be, "Search match failed"
index db70088..5d92695 100644 (file)
@@ -141,8 +141,8 @@ DOMAIN_OPERATORS = (NOT_OPERATOR, OR_OPERATOR, AND_OPERATOR)
 # for consistency. This list doesn't contain '<>' as it is simpified to '!='
 # by the normalize_operator() function (so later part of the code deals with
 # only one representation).
-# An internal (i.e. not available to the user) 'inselect' operator is also
-# used. In this case its right operand has the form (subselect, params).
+# Internals (i.e. not available to the user) 'inselect' and 'not inselect'
+# operators are also used. In this case its right operand has the form (subselect, params).
 TERM_OPERATORS = ('=', '!=', '<=', '<', '>', '>=', '=?', '=like', '=ilike',
                   'like', 'not like', 'ilike', 'not ilike', 'in', 'not in',
                   'child_of')
@@ -230,10 +230,11 @@ def is_operator(element):
 def is_leaf(element, internal=False):
     """ Test whether an object is a valid domain term.
 
-    :param internal: allow or not the 'inselect' internal operator in the term.
+    :param internal: allow or not the 'inselect' and 'not inselect' internals
+    operators in the term.
     This normally should be always left to False.
     """
-    INTERNAL_OPS = TERM_OPERATORS + ('inselect',)
+    INTERNAL_OPS = TERM_OPERATORS + ('inselect', 'not inselect')
     return (isinstance(element, tuple) or isinstance(element, list)) \
        and len(element) == 3 \
        and (((not internal) and element[1] in TERM_OPERATORS + ('<>',)) \
@@ -622,6 +623,12 @@ class expression(object):
                     if need_wildcard:
                         right = '%%%s%%' % right
 
+                    inselect_operator = 'inselect'
+                    if operator in NEGATIVE_TERM_OPERATORS:
+                        # negate operator (fix lp:1071710)
+                        operator = operator[4:] if operator[:3] == 'not' else '='
+                        inselect_operator = 'not inselect'
+
                     subselect = '( SELECT res_id'          \
                              '    FROM ir_translation'  \
                              '   WHERE name = %s'       \
@@ -629,7 +636,7 @@ class expression(object):
                              '     AND type = %s'
                     instr = ' %s'
                     #Covering in,not in operators with operands (%s,%s) ,etc.
-                    if sql_operator in ['in','not in']:
+                    if sql_operator == 'in':
                         instr = ','.join(['%s'] * len(right))
                         subselect += '     AND value ' + sql_operator +  ' ' +" (" + instr + ")"   \
                              ') UNION ('                \
@@ -650,13 +657,13 @@ class expression(object):
                               right,
                              ]
 
-                    self.__exp[i] = ('id', 'inselect', (subselect, params))
+                    self.__exp[i] = ('id', inselect_operator, (subselect, params))
 
     def __leaf_to_sql(self, leaf, table):
         left, operator, right = leaf
 
         # final sanity checks - should never fail
-        assert operator in (TERM_OPERATORS + ('inselect',)), \
+        assert operator in (TERM_OPERATORS + ('inselect', 'not inselect')), \
             "Invalid operator %r in domain term %r" % (operator, leaf)
         assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \
             or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf)
@@ -673,6 +680,10 @@ class expression(object):
             query = '(%s."%s" in (%s))' % (table._table, left, right[0])
             params = right[1]
 
+        elif operator == 'not inselect':
+            query = '(%s."%s" not in (%s))' % (table._table, left, right[0])
+            params = right[1]
+
         elif operator in ['in', 'not in']:
             # Two cases: right is a boolean or a list. The boolean case is an
             # abuse and handled for backward compatibility.