From ed15911ab3d6afecbc535fe7ef52dcfc25bfc42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 2 Oct 2024 17:06:58 +0200 Subject: [PATCH] Quote empty string when formatting list value of domain This is needed to keep the empty string in the list on a second parsing. Closes #13558 (grafted from 747825bc93a35fef995094be5ca9d7e04c414692) --HG-- branch : 6.0 --- sao/src/common.js | 12 ++++++++---- sao/tests/sao.js | 1 + tryton/tryton/common/domain_parser.py | 12 ++++++++---- tryton/tryton/tests/test_common_domain_parser.py | 2 ++ 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/sao/src/common.js b/sao/src/common.js index 91c169d1b6..9397692d36 100644 --- a/sao/src/common.js +++ b/sao/src/common.js @@ -1904,10 +1904,13 @@ .replace(escape + '%', '%') .replace(escape + '_', '_'); }, - quote: function(value) { + quote: function(value, empty=false) { if (typeof value != 'string') { return value; } + if (empty && value === '') { + return '""'; + } if (value.contains('\\')) { value = value.replace(new RegExp('\\\\', 'g'), '\\\\'); } @@ -2069,7 +2072,8 @@ return value; } }, - format_value: function(field, value, target, context) { + format_value: function( + field, value, target, context, _quote_empty=false) { if (target === undefined) { target = null; } @@ -2190,7 +2194,7 @@ converts.timestamp = converts.datetime; if (value instanceof Array) { return value.map(function(v) { - return this.format_value(field, v); + return this.format_value(field, v, null, context, true); }.bind(this)).join(';'); } else { var func = converts[field.type]; @@ -2199,7 +2203,7 @@ } else if (value === null) { return ''; } else { - return this.quote(value); + return this.quote(value, _quote_empty); } } }, diff --git a/sao/tests/sao.js b/sao/tests/sao.js index cd3f40a009..af9fdbac25 100644 --- a/sao/tests/sao.js +++ b/sao/tests/sao.js @@ -2192,6 +2192,7 @@ [[['name', 'ilike', 'Doe\\%']], 'Name: =Doe%'], [[['name', 'not ilike', '%Doe%']], 'Name: !Doe'], [[['name', 'in', ['John', 'Jane']]], 'Name: John;Jane'], + [[['name', 'in', ['John', '']]], 'Name: John;""'], [[['name', 'in', ['John']]], 'Name: =John'], [[['name', 'not in', ['John', 'Jane']]], 'Name: !John;Jane'], [[['name', 'not in', ['John']]], 'Name: !=John'], diff --git a/tryton/tryton/common/domain_parser.py b/tryton/tryton/common/domain_parser.py index dbf2bf8ae6..2c3e56bec4 100644 --- a/tryton/tryton/common/domain_parser.py +++ b/tryton/tryton/common/domain_parser.py @@ -126,10 +126,12 @@ def unescape(value, escape='\\'): return value.replace(escape + '%', '%').replace(escape + '_', '_') -def quote(value): +def quote(value, empty=False): "Quote string if needed" if not isinstance(value, str): return value + if empty and value == '': + return '""' if '\\' in value: value = value.replace('\\', '\\\\') if '"' in value: @@ -305,7 +307,7 @@ def convert_value(field, value, context=None): return converts.get(field['type'], lambda: value)() -def format_value(field, value, target=None, context=None): +def format_value(field, value, target=None, context=None, _quote_empty=False): "Format value for field" if context is None: context = {} @@ -408,9 +410,11 @@ def format_value(field, value, target=None, context=None): 'many2one': format_many2one, } if isinstance(value, (list, tuple)): - return ';'.join(format_value(field, x, context=context) for x in value) + return ';'.join( + format_value(field, x, context=context, _quote_empty=True) + for x in value) return quote(converts.get(field['type'], - lambda: value if value is not None else '')()) + lambda: value if value is not None else '')(), empty=_quote_empty) def complete_value(field, value): diff --git a/tryton/tryton/tests/test_common_domain_parser.py b/tryton/tryton/tests/test_common_domain_parser.py index 25b15d952d..26e98d2ac2 100644 --- a/tryton/tryton/tests/test_common_domain_parser.py +++ b/tryton/tryton/tests/test_common_domain_parser.py @@ -681,6 +681,8 @@ class DomainParserTestCase(TestCase): dom.string([('name', 'not ilike', '%Doe%')]), 'Name: !Doe') self.assertEqual( dom.string([('name', 'in', ['John', 'Jane'])]), 'Name: John;Jane') + self.assertEqual( + dom.string([('name', 'in', ['John', ''])]), 'Name: John;""') self.assertEqual( dom.string([('name', 'in', ['John'])]), 'Name: =John') self.assertEqual(