From 896ccf9075eb0b641054773b517d08ef8d01c636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 9 Mar 2025 11:23:43 +0100 Subject: [PATCH] Do not include record with NULL field in nested negative clause on None Since 87456218dcc9, record with NULL field is include in nested negative clause but this should not applied if the operand is or contains None. Closes #13911 (grafted from 993d890dd786424c2cd00009045ad95a09075e72) --HG-- branch : 6.0 --- trytond/doc/topics/domain.rst | 1 + trytond/trytond/model/fields/many2one.py | 5 ++++- trytond/trytond/model/fields/reference.py | 5 ++++- trytond/trytond/tests/test_field_many2one.py | 15 +++++++++++++++ trytond/trytond/tests/test_field_reference.py | 19 +++++++++++++++++++ 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/trytond/doc/topics/domain.rst b/trytond/doc/topics/domain.rst index f7c9be42cf..f51b09a94e 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 1c31118e0c..42708901d4 100644 --- a/trytond/trytond/model/fields/many2one.py +++ b/trytond/trytond/model/fields/many2one.py @@ -236,7 +236,10 @@ class Many2One(Field): target_domain = [target_domain, rule_domain] _, 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 33d47d7350..4fdd2707ba 100644 --- a/trytond/trytond/model/fields/reference.py +++ b/trytond/trytond/model/fields/reference.py @@ -189,7 +189,10 @@ class Reference(SelectionMixin, Field): Position(',', column) + Literal(1)), Model.id.sql_type().base).in_(query) & column.ilike(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 6b7ca8d118..ce71e7e3c6 100644 --- a/trytond/trytond/tests/test_field_many2one.py +++ b/trytond/trytond/tests/test_field_many2one.py @@ -485,6 +485,21 @@ class FieldMany2OneTestCase(unittest.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_context_attribute(self): "Test context on many2one attribute" diff --git a/trytond/trytond/tests/test_field_reference.py b/trytond/trytond/tests/test_field_reference.py index ae36dc8c31..96d9bc0432 100644 --- a/trytond/trytond/tests/test_field_reference.py +++ b/trytond/trytond/tests/test_field_reference.py @@ -373,6 +373,25 @@ class FieldReferenceTestCase(unittest.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"