From eccfdd5512375099d578ccd686fdd71a8cdcec93 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 28 Oct 2019 10:38:50 +0000 Subject: [PATCH] [FIX] models: filtered_domain on Falsy date/datetime fields The purpose of this patch is to avoid `filtered_domain` to crash with domains on date/datetime fields when the fields are `False` on some records: records.filtered_domain([('date', '<', '2019-10-28')]) closes odoo/odoo#39449 X-original-commit: dae379adebedb3ed9bd38a2d6925ba6aefef1e7f Signed-off-by: Raphael Collet (rco) --- .../test_new_api/tests/test_new_fields.py | 18 ++++++++++ odoo/models.py | 36 +++++++++++-------- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index f7a555f6e2a..7622ec7e5a1 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -617,6 +617,15 @@ class TestFields(common.TransactionCase): self.assertTrue(record.filtered_domain([('date', '<', '2012-05-02')])) self.assertTrue(record.filtered_domain([('date', '<', date(2012, 5, 2))])) self.assertTrue(record.filtered_domain([('date', '<', datetime(2012, 5, 2, 12, 0, 0))])) + self.assertTrue(record.filtered_domain([('date', '!=', False)])) + self.assertFalse(record.filtered_domain([('date', '=', False)])) + + record.date = None + self.assertFalse(record.filtered_domain([('date', '<', '2012-05-02')])) + self.assertFalse(record.filtered_domain([('date', '<', date(2012, 5, 2))])) + self.assertFalse(record.filtered_domain([('date', '<', datetime(2012, 5, 2, 12, 0, 0))])) + self.assertFalse(record.filtered_domain([('date', '!=', False)])) + self.assertTrue(record.filtered_domain([('date', '=', False)])) def test_21_datetime(self): """ test datetime fields """ @@ -647,6 +656,15 @@ class TestFields(common.TransactionCase): self.assertTrue(record.filtered_domain([('moment', '<', '2012-05-02')])) self.assertTrue(record.filtered_domain([('moment', '<', date(2012, 5, 2))])) self.assertTrue(record.filtered_domain([('moment', '<', datetime(2012, 5, 1, 12, 0, 0))])) + self.assertTrue(record.filtered_domain([('moment', '!=', False)])) + self.assertFalse(record.filtered_domain([('moment', '=', False)])) + + record.moment = None + self.assertFalse(record.filtered_domain([('moment', '<', '2012-05-02')])) + self.assertFalse(record.filtered_domain([('moment', '<', date(2012, 5, 2))])) + self.assertFalse(record.filtered_domain([('moment', '<', datetime(2012, 5, 2, 12, 0, 0))])) + self.assertFalse(record.filtered_domain([('moment', '!=', False)])) + self.assertTrue(record.filtered_domain([('moment', '=', False)])) def test_21_date_datetime_helpers(self): """ test date/datetime fields helpers """ diff --git a/odoo/models.py b/odoo/models.py index e0a8ac86473..58bca634283 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -5132,7 +5132,15 @@ Record ids: %(records)s (key, comparator, value) = d if key.endswith('.id'): key = key[:-3] - if key == 'id': key='' + if key == 'id': + key = '' + # determine the field with the final type for values + field = None + if key: + model = self.browse() + for fname in key.split('.'): + field = model._fields[fname] + model = model[fname] if comparator in ('like', 'ilike', '=like', '=ilike', 'not ilike', 'not like'): value_esc = value.replace('_', '?').replace('%', '*').replace('[', '?') records = self.browse() @@ -5149,16 +5157,14 @@ Record ids: %(records)s data = data.mapped('display_name') else: data = data and data.ids or [False] - else: - true_data = next((d for d in data if d), None) - if isinstance(true_data, datetime.date): - # convert all date and datetime values to datetime - normalize = Datetime.to_datetime - if isinstance(value, (list, tuple)): - value = [normalize(v) for v in value] - else: - value = normalize(value) - data = [normalize(d) for d in data] + elif field and field.type in ('date', 'datetime'): + # convert all date and datetime values to datetime + normalize = Datetime.to_datetime + if isinstance(value, (list, tuple)): + value = [normalize(v) for v in value] + else: + value = normalize(value) + data = [normalize(d) for d in data] if comparator in ('in', 'not in'): if not (isinstance(value, list) or isinstance(value, tuple)): value = [value] @@ -5168,13 +5174,13 @@ Record ids: %(records)s elif comparator == 'in': ok = any(map(lambda x: x in data, value)) elif comparator == '<': - ok = any(map(lambda x: x < value, data)) + ok = any(map(lambda x: x is not None and x < value, data)) elif comparator == '>': - ok = any(map(lambda x: x > value, data)) + ok = any(map(lambda x: x is not None and x > value, data)) elif comparator == '<=': - ok = any(map(lambda x: x <= value, data)) + ok = any(map(lambda x: x is not None and x <= value, data)) elif comparator == '>=': - ok = any(map(lambda x: x >= value, data)) + ok = any(map(lambda x: x is not None and x >= value, data)) elif comparator in ('!=', '<>'): ok = value not in data elif comparator == 'not in':