diff --git a/trytond/trytond/model/fields/one2many.py b/trytond/trytond/model/fields/one2many.py index 80ffc94453..29323c0038 100644 --- a/trytond/trytond/model/fields/one2many.py +++ b/trytond/trytond/model/fields/one2many.py @@ -347,9 +347,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) @@ -359,12 +360,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 642beac9cf..68637ec1d5 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 @@ -203,6 +203,51 @@ class Many2ManyContextTarget(ModelSQL): return context.get('test') +class Many2ManyActive(ModelSQL): + "Many2Many Active" + __name__ = 'test.many2many_active' + targets = fields.Many2Many( + 'test.many2many_active.relation', 'origin', 'target', 'Targets') + + +class Many2ManyTargetActive(DeactivableMixin, ModelSQL): + "Many2Many Target Active" + __name__ = 'test.many2many_active.target' + name = fields.Char('Name') + + +class Many2ManyRelationActive(ModelSQL): + "Many2Many Relation Active" + __name__ = 'test.many2many_active.relation' + origin = fields.Many2One('test.many2many_active', 'Origin') + target = fields.Many2One('test.many2many_active.target', 'Target') + + +class Many2ManyReferenceActive(ModelSQL): + "Many2Many Reference Active" + __name__ = 'test.many2many_reference.active' + targets = fields.Many2Many( + 'test.many2many_reference.active.relation', 'origin', 'target', + "Targets") + + +class Many2ManyReferenceActiveTarget(DeactivableMixin, ModelSQL): + "Many2Many Reference Active Target" + __name__ = 'test.many2many_reference.active.target' + name = fields.Char('Name') + + +class Many2ManyReferenceActiveRelation(ModelSQL): + "Many2Many Reference Active Relation" + __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, @@ -231,4 +276,10 @@ def register(module): Many2ManyContext, Many2ManyContextTarget, Many2ManyContextRelation, + 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 be97f94eff..6835bf6b4c 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 @@ -147,6 +147,37 @@ class One2ManyContextTarget(ModelSQL): return context.get('test') +class One2ManyActive(ModelSQL): + "One2Many Active" + __name__ = 'test.one2many.active' + targets = fields.One2Many( + 'test.one2many.active_target', 'origin', "Targets") + + +class One2ManyActiveTarget(DeactivableMixin, ModelSQL): + "One2Many Active Target" + __name__ = 'test.one2many.active_target' + name = fields.Char('Name') + origin = fields.Many2One('test.one2many.active', "Origin") + + +class One2ManyActiveReference(ModelSQL): + "One2Many Active Reference" + __name__ = 'test.one2many_reference.active' + targets = fields.One2Many( + 'test.one2many_reference.active_target', 'origin', "Targets") + + +class One2ManyActiveReferenceTarget(DeactivableMixin, ModelSQL): + "One2Many Active Reference Target" + __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, @@ -167,4 +198,8 @@ def register(module): One2ManyFilterDomainTarget, One2ManyContext, One2ManyContextTarget, + 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 666497551c..7a27b858c6 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" @@ -387,6 +429,9 @@ class FieldMany2ManyTestCase(unittest.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" @@ -769,3 +814,6 @@ class FieldMany2ManyReferenceTestCase(unittest.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 65b1990a6f..a108098a1d 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" @@ -464,6 +504,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" @@ -724,6 +767,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(unittest.TestCase, SearchTestCaseMixin): "Test Field One2Many when using EXISTS" @@ -747,6 +793,9 @@ class FieldOne2ManyExistsTestCase(unittest.TestCase, SearchTestCaseMixin): def One2ManyTarget(self): return Pool().get('test.one2many.target') + def One2ManyActive(self): + return Pool().get('test.one2many.active') + class FieldOne2ManyReferenceExistsTestCase( unittest.TestCase, SearchTestCaseMixin): @@ -771,5 +820,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)