diff --git a/trytond/doc/topics/domain.rst b/trytond/doc/topics/domain.rst index e65a166f18..e61456ff47 100644 --- a/trytond/doc/topics/domain.rst +++ b/trytond/doc/topics/domain.rst @@ -40,6 +40,7 @@ by this pattern:: .. note:: Negative operators match records for which any of the field before the last dot is empty. + Except if the operand is or contains ``None``. .. warning:: For :class:`trytond.model.fields.Reference`, an extra ending clause is diff --git a/trytond/trytond/model/fields/many2one.py b/trytond/trytond/model/fields/many2one.py index d7714a12b4..3c6ec233e6 100644 --- a/trytond/trytond/model/fields/many2one.py +++ b/trytond/trytond/model/fields/many2one.py @@ -280,7 +280,10 @@ class Many2One(Field): target_table, _ = target_tables[None] _, expression = Target.search_domain( target_domain, tables=target_tables) - if operator.startswith('not') or operator == '!=': + if ((operator.startswith('not') and not ( + operator == 'not in' + and None in value)) + or (operator == '!=' and value is not None)): expression |= column == Null return expression diff --git a/trytond/trytond/model/fields/reference.py b/trytond/trytond/model/fields/reference.py index 6b30e9e884..232e3a0b88 100644 --- a/trytond/trytond/model/fields/reference.py +++ b/trytond/trytond/model/fields/reference.py @@ -216,7 +216,10 @@ class Reference(SelectionMixin, Field): expression = ( self.sql_id(column, Model).in_(query) & column.like(target + ',%')) - if operator.startswith('not') or operator == '!=': + if ((operator.startswith('not') and not ( + operator == 'not in' + and None in value)) + or (operator == '!=' and value is not None)): expression |= column == Null return expression diff --git a/trytond/trytond/tests/test_field_many2one.py b/trytond/trytond/tests/test_field_many2one.py index 7993d5e282..b992904cea 100644 --- a/trytond/trytond/tests/test_field_many2one.py +++ b/trytond/trytond/tests/test_field_many2one.py @@ -220,6 +220,21 @@ class FieldMany2OneTestCase(TestCase): ]) self.assertListEqual(result, [record]) + result = Many2One.search([ + ('many2one.value', '!=', None), + ]) + self.assertListEqual(result, []) + + result = Many2One.search([ + ('many2one.value', 'not in', [1]), + ]) + self.assertListEqual(result, [record]) + + result = Many2One.search([ + ('many2one.value', 'not in', [1, None]), + ]) + self.assertListEqual(result, []) + @with_transaction() def test_set_instance(self): "Test set instance" diff --git a/trytond/trytond/tests/test_field_reference.py b/trytond/trytond/tests/test_field_reference.py index 4559ab15a9..76a48860e5 100644 --- a/trytond/trytond/tests/test_field_reference.py +++ b/trytond/trytond/tests/test_field_reference.py @@ -375,6 +375,25 @@ class FieldReferenceTestCase(TestCase): self.assertListEqual(result, [reference]) + result = Reference.search([ + ('reference.name', '!=', None, Target.__name__), + ]) + + self.assertListEqual(result, []) + + result = Reference.search([ + ('reference.name', 'not in', ["Target"], Target.__name__), + ]) + + self.assertListEqual(result, [reference]) + + result = Reference.search([ + ('reference.name', 'not in', ["Target", None], + Target.__name__), + ]) + + self.assertListEqual(result, []) + @with_transaction() def test_write_string(self): "Test write reference string"