diff --git a/trytond/model/fields/many2many.py b/trytond/model/fields/many2many.py index 67a011e0fe..57ad8cb86c 100644 --- a/trytond/model/fields/many2many.py +++ b/trytond/model/fields/many2many.py @@ -453,7 +453,10 @@ class Many2Many(Field): relation_domain, tables=relation_tables) query_table = convert_from(None, relation_tables) query = query_table.select(origin, where=expression) - return table.id.in_(query) + expression = table.id.in_(query) + if operator.startswith('!') or operator.startswith('not '): + expression |= ~table.id.in_(relation.select(origin)) + return expression def definition(self, model, language): encoder = PYSONEncoder() diff --git a/trytond/model/fields/one2many.py b/trytond/model/fields/one2many.py index b613251b91..530e469828 100644 --- a/trytond/model/fields/one2many.py +++ b/trytond/model/fields/one2many.py @@ -354,6 +354,8 @@ class One2Many(Field): if operator == 'not where': expression = ~expression + elif operator.startswith('!') or operator.startswith('not '): + expression |= ~table.id.in_(target.select(origin)) return expression def definition(self, model, language): diff --git a/trytond/tests/test_field_many2many.py b/trytond/tests/test_field_many2many.py index 8a42d44043..84f8ebec5f 100644 --- a/trytond/tests/test_field_many2many.py +++ b/trytond/tests/test_field_many2many.py @@ -39,6 +39,21 @@ class CommonTestCaseMixin: self.assertListEqual(many2manys, [many2many]) + @with_transaction() + def test_search_equals_no_link(self): + "Test search many2many equals without link" + Many2Many = self.Many2Many() + many2many, no_link = Many2Many.create([{ + 'targets': [('create', [{'name': "Target"}])], + }, { + }]) + + many2manys = Many2Many.search([ + ('targets', '=', "Target"), + ]) + + self.assertListEqual(many2manys, [many2many]) + @with_transaction() def test_search_non_equals(self): "Test search many2many non equals" @@ -85,6 +100,21 @@ class CommonTestCaseMixin: self.assertListEqual(many2manys, [many2many1]) + @with_transaction() + def test_search_non_equals_no_link(self): + "Test search many2many non equals without link" + Many2Many = self.Many2Many() + many2many, no_link = Many2Many.create([{ + 'targets': [('create', [{'name': "Target"}])], + }, { + }]) + + many2manys = Many2Many.search([ + ('targets', '!=', "Target"), + ]) + + self.assertListEqual(many2manys, [no_link]) + @with_transaction() def test_search_in(self): "Test search many2many in" diff --git a/trytond/tests/test_field_one2many.py b/trytond/tests/test_field_one2many.py index 7abe51490a..9566a89662 100644 --- a/trytond/tests/test_field_one2many.py +++ b/trytond/tests/test_field_one2many.py @@ -101,6 +101,21 @@ class CommonTestCaseMixin: self.assertListEqual(one2manys, [one2many]) + @with_transaction() + def test_search_equals_no_link(self): + "Test search one2many equals without link" + One2Many = self.One2Many() + one2many, no_link = One2Many.create([{ + 'targets': [('create', [{'name': "Target"}])], + }, { + }]) + + one2manys = One2Many.search([ + ('targets', '=', "Target"), + ]) + + self.assertListEqual(one2manys, [one2many]) + @with_transaction() def test_search_non_equals(self): "Test search one2many non equals" @@ -147,6 +162,21 @@ class CommonTestCaseMixin: self.assertListEqual(one2manys, [one2many1]) + @with_transaction() + def test_search_non_equals_no_link(self): + "Test search one2many non equals without link" + One2Many = self.One2Many() + one2many, no_link = One2Many.create([{ + 'targets': [('create', [{'name': "Target"}])], + }, { + }]) + + one2manys = One2Many.search([ + ('targets', '!=', "Target"), + ]) + + self.assertListEqual(one2manys, [no_link]) + @with_transaction() def test_search_in(self): "Test search one2many in"