diff --git a/trytond/trytond/model/fields/one2many.py b/trytond/trytond/model/fields/one2many.py index 705af2eb65..1690eee2af 100644 --- a/trytond/trytond/model/fields/one2many.py +++ b/trytond/trytond/model/fields/one2many.py @@ -355,9 +355,10 @@ class One2Many(Field): where &= history_where if origin_where: where &= origin_where - if self.filter: - query = Target.search( - self.filter, order=[], query=True) + if self.filter or hasattr(Target, 'active'): + # Use an empty filter to apply the active test + filter_ = self.filter if self.filter else [] + query = Target.search(filter_, order=[], query=True) where &= target.id.in_(query) query = target.select(origin, where=where) expression = ~table.id.in_(query) @@ -367,12 +368,13 @@ class One2Many(Field): where &= history_where if origin_where: where &= origin_where - if self.filter: + if self.filter or hasattr(Target, 'active'): target_tables = { None: (target, None), } + filter_ = self.filter if self.filter else [] target_tables, clause = Target.search_domain( - self.filter, tables=target_tables) + filter_, tables=target_tables) where &= clause query_table = convert_from(None, target_tables) query = query_table.select(Literal(1), where=where) diff --git a/trytond/trytond/tests/field_many2many.py b/trytond/trytond/tests/field_many2many.py index a375236220..21f72a5740 100644 --- a/trytond/trytond/tests/field_many2many.py +++ b/trytond/trytond/tests/field_many2many.py @@ -1,7 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.model import ModelSQL, fields +from trytond.model import DeactivableMixin, ModelSQL, fields from trytond.pool import Pool from trytond.transaction import Transaction @@ -197,6 +197,45 @@ class Many2ManyOrderTarget(ModelSQL): __name__ = 'test.many2many_order.target' +class Many2ManyActive(ModelSQL): + __name__ = 'test.many2many_active' + targets = fields.Many2Many( + 'test.many2many_active.relation', 'origin', 'target', 'Targets') + + +class Many2ManyTargetActive(DeactivableMixin, ModelSQL): + __name__ = 'test.many2many_active.target' + name = fields.Char('Name') + + +class Many2ManyRelationActive(ModelSQL): + __name__ = 'test.many2many_active.relation' + origin = fields.Many2One('test.many2many_active', 'Origin') + target = fields.Many2One('test.many2many_active.target', 'Target') + + +class Many2ManyReferenceActive(ModelSQL): + __name__ = 'test.many2many_reference.active' + targets = fields.Many2Many( + 'test.many2many_reference.active.relation', 'origin', 'target', + "Targets") + + +class Many2ManyReferenceActiveTarget(DeactivableMixin, ModelSQL): + __name__ = 'test.many2many_reference.active.target' + name = fields.Char('Name') + + +class Many2ManyReferenceActiveRelation(ModelSQL): + __name__ = 'test.many2many_reference.active.relation' + origin = fields.Reference('Origin', [ + (None, ''), + ('test.many2many_reference.active', 'Many2Many Reference'), + ]) + target = fields.Many2One('test.many2many_reference.active.target', + 'Reference Target') + + def register(module): Pool.register( Many2Many, @@ -228,4 +267,10 @@ def register(module): Many2ManyOrder, Many2ManyOrderRelation, Many2ManyOrderTarget, + Many2ManyActive, + Many2ManyTargetActive, + Many2ManyRelationActive, + Many2ManyReferenceActive, + Many2ManyReferenceActiveRelation, + Many2ManyReferenceActiveTarget, module=module, type_='model') diff --git a/trytond/trytond/tests/field_one2many.py b/trytond/trytond/tests/field_one2many.py index 7edb1981e9..e2dce371c4 100644 --- a/trytond/trytond/tests/field_one2many.py +++ b/trytond/trytond/tests/field_one2many.py @@ -1,7 +1,7 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. -from trytond.model import ModelSQL, fields +from trytond.model import DeactivableMixin, ModelSQL, fields from trytond.pool import Pool from trytond.pyson import Eval from trytond.transaction import Transaction @@ -148,6 +148,33 @@ class One2ManyOrderTarget(ModelSQL): cls._order = [('id', 'DESC')] +class One2ManyActive(ModelSQL): + __name__ = 'test.one2many.active' + targets = fields.One2Many( + 'test.one2many.active_target', 'origin', "Targets") + + +class One2ManyActiveTarget(DeactivableMixin, ModelSQL): + __name__ = 'test.one2many.active_target' + name = fields.Char('Name') + origin = fields.Many2One('test.one2many.active', "Origin") + + +class One2ManyActiveReference(ModelSQL): + __name__ = 'test.one2many_reference.active' + targets = fields.One2Many( + 'test.one2many_reference.active_target', 'origin', "Targets") + + +class One2ManyActiveReferenceTarget(DeactivableMixin, ModelSQL): + __name__ = 'test.one2many_reference.active_target' + name = fields.Char('Name') + origin = fields.Reference('Origin', [ + (None, ''), + ('test.one2many_reference.active', 'One2Many Reference'), + ]) + + def register(module): Pool.register( One2Many, @@ -170,4 +197,8 @@ def register(module): One2ManyContextTarget, One2ManyOrder, One2ManyOrderTarget, + One2ManyActive, + One2ManyActiveTarget, + One2ManyActiveReference, + One2ManyActiveReferenceTarget, module=module, type_='model') diff --git a/trytond/trytond/tests/test_field_many2many.py b/trytond/trytond/tests/test_field_many2many.py index 1bc7cfe323..5a1ae19d20 100644 --- a/trytond/trytond/tests/test_field_many2many.py +++ b/trytond/trytond/tests/test_field_many2many.py @@ -100,6 +100,48 @@ class CommonTestCaseMixin: self.assertListEqual(many2manys, [many2many1]) + @with_transaction() + def test_search_equals_none_inactive(self): + "Test search many2many equals None when the target is inactive" + Many2Many = self.Many2ManyActive() + + many2many1, many2many2, many2many3 = Many2Many.create([{ + 'targets': [('create', [{'name': "Target"}])], + }, { + 'targets': [ + ('create', [{'name': "Target", 'active': False}]) + ], + }, { + 'targets': None, + }]) + + many2manys = Many2Many.search([ + ('targets', '=', None), + ]) + + self.assertListEqual(many2manys, [many2many3]) + + @with_transaction() + def test_search_non_equals_none_inactive(self): + "Test search many2many equals non None when the target is inactive" + Many2Many = self.Many2ManyActive() + + many2many1, many2many2, many2many3 = Many2Many.create([{ + 'targets': [('create', [{'name': "Target"}])], + }, { + 'targets': [ + ('create', [{'name': "Target", 'active': False}]) + ], + }, { + 'targets': None, + }]) + + many2manys = Many2Many.search([ + ('targets', '!=', None), + ]) + + self.assertListEqual(many2manys, [many2many1, many2many2]) + @with_transaction() def test_search_non_equals_no_link(self): "Test search many2many non equals without link" @@ -388,6 +430,9 @@ class FieldMany2ManyTestCase(TestCase, CommonTestCaseMixin): def Many2ManyTarget(self): return Pool().get('test.many2many.target') + def Many2ManyActive(self): + return Pool().get('test.many2many_active') + @with_transaction() def test_create_required_with_value(self): "Test create many2many required with value" @@ -794,3 +839,6 @@ class FieldMany2ManyReferenceTestCase(TestCase, CommonTestCaseMixin): def Many2ManyTarget(self): return Pool().get('test.many2many_reference.target') + + def Many2ManyActive(self): + return Pool().get('test.many2many_reference.active') diff --git a/trytond/trytond/tests/test_field_one2many.py b/trytond/trytond/tests/test_field_one2many.py index b36f8f5e7f..1db1a1cb24 100644 --- a/trytond/trytond/tests/test_field_one2many.py +++ b/trytond/trytond/tests/test_field_one2many.py @@ -70,6 +70,46 @@ class SearchTestCaseMixin: self.assertListEqual(one2manys, [one2many2]) + @with_transaction() + def test_search_equals_none_inactive(self): + "Test search one2many equals None when the target is inactive" + One2Many = self.One2ManyActive() + one2many1, one2many2, one2many3 = One2Many.create([{ + 'targets': [('create', [{'name': "Target1"}])], + }, { + 'targets': [ + ('create', [{'name': "Target2", 'active': False}]), + ], + }, { + 'targets': None, + }]) + + one2manys = One2Many.search([ + ('targets', '=', None), + ]) + + self.assertEqual(one2manys, [one2many2, one2many3]) + + @with_transaction() + def test_search_not_equals_none_inactive(self): + "Test search one2many not equals None when the target is inactive" + One2Many = self.One2ManyActive() + one2many1, one2many2, one2many3 = One2Many.create([{ + 'targets': [('create', [{'name': "Target1"}])], + }, { + 'targets': [ + ('create', [{'name': "Target2", 'active': False}]), + ], + }, { + 'targets': None, + }]) + + one2manys = One2Many.search([ + ('targets', '!=', None), + ]) + + self.assertEqual(one2manys, [one2many1]) + @with_transaction() def test_search_non_equals_none(self): "Test search one2many non equals None" @@ -465,6 +505,9 @@ class FieldOne2ManyTestCase( def One2ManyTarget(self): return Pool().get('test.one2many.target') + def One2ManyActive(self): + return Pool().get('test.one2many.active') + @with_transaction() def test_create_required_with_value(self): "Test create one2many required with value" @@ -750,6 +793,9 @@ class FieldOne2ManyReferenceTestCase( def One2ManyTarget(self): return Pool().get('test.one2many_reference.target') + def One2ManyActive(self): + return Pool().get('test.one2many_reference.active') + class FieldOne2ManyExistsTestCase(TestCase, SearchTestCaseMixin): "Test Field One2Many when using EXISTS" @@ -772,6 +818,9 @@ class FieldOne2ManyExistsTestCase(TestCase, SearchTestCaseMixin): def One2ManyTarget(self): return Pool().get('test.one2many.target') + def One2ManyActive(self): + return Pool().get('test.one2many.active') + class FieldOne2ManyReferenceExistsTestCase( TestCase, SearchTestCaseMixin): @@ -795,5 +844,8 @@ class FieldOne2ManyReferenceExistsTestCase( def One2ManyTarget(self): return Pool().get('test.one2many_reference.target') + def One2ManyActive(self): + return Pool().get('test.one2many_reference.active') + def assert_strategy(self, query): self.assertIn('EXISTS', query)