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 : 7.0
This commit is contained in:
Cédric Krier
2024-10-02 17:06:58 +02:00
parent 91b274123a
commit e8f99b2cbb
4 changed files with 21 additions and 8 deletions
+10 -4
View File
@@ -1953,10 +1953,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'), '\\\\');
}
@@ -2115,7 +2118,8 @@
return value;
}
},
format_value: function(field, value, target=null, context={}) {
format_value: function(
field, value, target=null, context={}, _quote_empty=false) {
var format_float = function() {
if (!value && value !== 0 && value !== new Sao.Decimal(0)) {
return '';
@@ -2226,7 +2230,9 @@
};
converts.timestamp = converts.datetime;
if (value instanceof Array) {
return value.map(v => this.format_value(field, v)).join(';');
return value.map(
v => this.format_value(field, v, null, context, true))
.join(';');
} else {
var func = converts[field.type];
if (func) {
@@ -2234,7 +2240,7 @@
} else if (value === null) {
return '';
} else {
return this.quote(value);
return this.quote(value, _quote_empty);
}
}
},
+1
View File
@@ -2266,6 +2266,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'],
+8 -4
View File
@@ -128,10 +128,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:
@@ -307,7 +309,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 = {}
@@ -410,9 +412,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):
@@ -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(