diff --git a/sao/src/common.js b/sao/src/common.js index 077c76914a..78886531e5 100644 --- a/sao/src/common.js +++ b/sao/src/common.js @@ -1275,7 +1275,8 @@ var name = clause[0]; var operator = clause[1]; var value = clause[2]; - if (name.endsWith('.rec_name') && value) { + if (name.endsWith('.rec_name') + && (value || (clause.length > 3))) { name = name.slice(0, -9); } if (!(name in this.fields)) { @@ -1853,7 +1854,7 @@ var split = this.split_target_value(field, value); target = split[0]; value = split[1]; - if (target && value) { + if (target) { field_name += '.rec_name'; } } else if (field.type == 'multiselection') { @@ -2207,7 +2208,7 @@ break; } } - return target + ',' + value; + return target + ',' + (value || ''); }; var converts = { diff --git a/sao/tests/sao.js b/sao/tests/sao.js index 03c61b3bf7..f143d0d17b 100644 --- a/sao/tests/sao.js +++ b/sao/tests/sao.js @@ -2040,6 +2040,9 @@ [[c(['Reference', null, 'Spam,bar'])], [ c(['reference.rec_name', 'ilike', '%bar%', 'spam']) ]], + [[c(['Reference', null, 'Spam,'])], [ + c(['reference.rec_name', 'ilike', '%', 'spam']) + ]], [[c(['Reference', null, ['foo', 'bar']])], [ c(['reference', 'in', ['foo', 'bar']]) ]], @@ -2374,6 +2377,7 @@ [[['multiselection', 'not in', ['foo', 'bar']]], "MultiSelection: !Foo;Bar"], [[['reference', 'ilike', '%foo%']], 'Reference: foo'], [[['reference', 'ilike', '%bar%', 'spam']], 'Reference: Spam,bar'], + [[['reference.rec_name', '=', null, 'spam']], 'Reference: =Spam,'], [[['reference', 'in', ['foo', 'bar']]], 'Reference: foo;bar'], [[['many2one', 'ilike', '%John%']], 'Many2One: John'], [[['many2one.rec_name', 'in', ['John', 'Jane']]], 'Many2One: John;Jane'], diff --git a/tryton/tryton/common/domain_parser.py b/tryton/tryton/common/domain_parser.py index c0569c2e0b..35f05189fd 100644 --- a/tryton/tryton/common/domain_parser.py +++ b/tryton/tryton/common/domain_parser.py @@ -368,7 +368,7 @@ def format_value(field, value, target=None, context=None, _quote_empty=False): if not target: return format_selection() selections = dict(field['selection']) - return '%s,%s' % (selections.get(target, target), value) + return '%s,%s' % (selections.get(target, target), value or '') def format_datetime(): if not value: @@ -624,7 +624,8 @@ class DomainParser(object): or clause[0] in ('AND', 'OR')): return '(%s)' % self.string(clause) name, operator, value = clause[:3] - if name.endswith('.rec_name') and value: + if (name.endswith('.rec_name') + and (value or len(clause) > 3)): name = name[:-9] if name not in self.fields: if value is not None and is_full_text(value): @@ -851,7 +852,7 @@ class DomainParser(object): target = None if field['type'] == 'reference': target, value = split_target_value(field, value) - if target and value: + if target: field_name += '.rec_name' elif field['type'] == 'multiselection': if value is not None and not isinstance(value, list): diff --git a/tryton/tryton/tests/test_common_domain_parser.py b/tryton/tryton/tests/test_common_domain_parser.py index b7dba8dc49..5f946d06c6 100644 --- a/tryton/tryton/tests/test_common_domain_parser.py +++ b/tryton/tryton/tests/test_common_domain_parser.py @@ -772,6 +772,9 @@ class DomainParserTestCase(TestCase): self.assertEqual( dom.string([('reference.rec_name', 'ilike', '%bar%', 'spam')]), 'Reference: Spam,bar') + self.assertEqual( + dom.string([('reference.rec_name', '=', None, 'spam')]), + "Reference: =Spam,") self.assertEqual( dom.string([('reference', 'in', ['foo', 'bar'])]), 'Reference: foo;bar') @@ -1106,6 +1109,10 @@ class DomainParserTestCase(TestCase): rlist(dom.parse_clause([('Reference', None, 'Spam,bar')])), [ ('reference.rec_name', 'ilike', '%bar%', 'spam'), ]) + self.assertEqual( + rlist(dom.parse_clause([('Reference', None, 'Spam,')])), [ + ('reference.rec_name', 'ilike', '%', 'spam'), + ]) self.assertEqual( rlist(dom.parse_clause([('Reference', None, ['foo', 'bar'])])), [ ('reference', 'in', ['foo', 'bar']),