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:
+10
-4
@@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user