Add support to domain parser to search on empty relation field
Closes #14106
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add search on empty relation field to domain parser
|
||||
* Escape completion content with custom format (issue14363)
|
||||
* Use sandboxed iframe to display document (issue14290)
|
||||
* Add support for multiple button in the tree view
|
||||
|
||||
+14
-9
@@ -1171,8 +1171,7 @@
|
||||
string_prefix = string_prefix || '';
|
||||
for (var name in fields) {
|
||||
var field = fields[name];
|
||||
if ((field.searchable || (field.searchable === undefined)) &&
|
||||
(name !== 'rec_name')) {
|
||||
if (field.searchable || (field.searchable === undefined)) {
|
||||
field = jQuery.extend({}, field);
|
||||
var fullname = prefix ? prefix + '.' + name : name;
|
||||
var string = string_prefix ?
|
||||
@@ -1227,7 +1226,7 @@
|
||||
}
|
||||
var name = clause[0];
|
||||
var value = clause[2];
|
||||
if (name.endsWith('.rec_name')) {
|
||||
if (name.endsWith('.rec_name') && value) {
|
||||
name = name.slice(0, -9);
|
||||
}
|
||||
if (name in this.fields) {
|
||||
@@ -1276,11 +1275,11 @@
|
||||
var name = clause[0];
|
||||
var operator = clause[1];
|
||||
var value = clause[2];
|
||||
if (name.endsWith('.rec_name')) {
|
||||
if (name.endsWith('.rec_name') && value) {
|
||||
name = name.slice(0, -9);
|
||||
}
|
||||
if (!(name in this.fields)) {
|
||||
if (this.is_full_text(value)) {
|
||||
if ((value !== null) && this.is_full_text(value)) {
|
||||
value = value.slice(1, -1);
|
||||
}
|
||||
return this.quote(value);
|
||||
@@ -1434,7 +1433,7 @@
|
||||
name = clause[0];
|
||||
operator = clause[1];
|
||||
value = clause[2];
|
||||
if (name.endsWith('.rec_name')) {
|
||||
if (name.endsWith('.rec_name') && value) {
|
||||
name = name.substring(0, name.length - 9);
|
||||
}
|
||||
}
|
||||
@@ -1854,7 +1853,7 @@
|
||||
var split = this.split_target_value(field, value);
|
||||
target = split[0];
|
||||
value = split[1];
|
||||
if (target) {
|
||||
if (target && value) {
|
||||
field_name += '.rec_name';
|
||||
}
|
||||
} else if (field.type == 'multiselection') {
|
||||
@@ -1901,7 +1900,7 @@
|
||||
}
|
||||
}
|
||||
if (['many2one', 'one2many', 'many2many', 'one2one',
|
||||
'many2many', 'one2one'].includes(field.type)) {
|
||||
'many2many', 'one2one'].includes(field.type) && value) {
|
||||
field_name += '.rec_name';
|
||||
}
|
||||
if (value instanceof Array) {
|
||||
@@ -2116,7 +2115,13 @@
|
||||
},
|
||||
'selection': convert_selection,
|
||||
'multiselection': convert_selection,
|
||||
'reference': convert_selection,
|
||||
'reference': function() {
|
||||
if (value === '') {
|
||||
return null;
|
||||
} else {
|
||||
return convert_selection();
|
||||
}
|
||||
},
|
||||
'datetime': () => Sao.common.parse_datetime(
|
||||
Sao.common.date_format(context.date_format) + ' ' +
|
||||
this.time_format(field), value),
|
||||
|
||||
@@ -2051,6 +2051,8 @@
|
||||
[['many2one.rec_name', 'ilike', '%John%']]],
|
||||
[[c(['Many2One', null, ['John', 'Jane']])],
|
||||
[['many2one.rec_name', 'in', ['John', 'Jane']]]],
|
||||
[[c(['Many2One', '=', null])], [['many2one', '=', null]]],
|
||||
[[c(['Many2One', '=', ''])], [['many2one', '=', null]]],
|
||||
[[[c(['John'])]], [[['rec_name', 'ilike', '%John%']]]],
|
||||
[[c(['Relation', null, 'John'])],
|
||||
[['relation.rec_name', 'ilike', '%John%']]],
|
||||
@@ -2308,6 +2310,10 @@
|
||||
'string': "Name",
|
||||
'type': 'char',
|
||||
},
|
||||
'rec_name': {
|
||||
'string': "Record Name",
|
||||
'type': 'char',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -2371,6 +2377,9 @@
|
||||
[[['reference', 'in', ['foo', 'bar']]], 'Reference: foo;bar'],
|
||||
[[['many2one', 'ilike', '%John%']], 'Many2One: John'],
|
||||
[[['many2one.rec_name', 'in', ['John', 'Jane']]], 'Many2One: John;Jane'],
|
||||
[[['many2one.rec_name', '=', 'John']], 'Many2One: =John'],
|
||||
[[['many2one.rec_name', '=', '']], '"Many2One.Record Name": =""'],
|
||||
[[['many2one.rec_name', '=', null]], '"Many2One.Record Name": ='],
|
||||
[[['many2one.name', 'ilike', '%Foo%']], 'Many2One.Name: Foo'],
|
||||
].forEach(function(test) {
|
||||
var value = test[0];
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
* Add search on empty relation field to domain parser
|
||||
* Add support for multiple button in the tree view
|
||||
|
||||
Version 7.6.0 - 2025-04-28
|
||||
|
||||
@@ -292,6 +292,11 @@ def convert_value(field, value, context=None):
|
||||
return None
|
||||
return value
|
||||
|
||||
def convert_reference():
|
||||
if value == '':
|
||||
return None
|
||||
return convert_selection()
|
||||
|
||||
converts = {
|
||||
'boolean': convert_boolean,
|
||||
'float': convert_float,
|
||||
@@ -299,7 +304,7 @@ def convert_value(field, value, context=None):
|
||||
'numeric': convert_numeric,
|
||||
'selection': convert_selection,
|
||||
'multiselection': convert_selection,
|
||||
'reference': convert_selection,
|
||||
'reference': convert_reference,
|
||||
'datetime': convert_datetime,
|
||||
'timestamp': convert_datetime,
|
||||
'date': convert_date,
|
||||
@@ -543,7 +548,7 @@ class DomainParser(object):
|
||||
|
||||
def update_fields(fields, prefix='', string_prefix=''):
|
||||
for name, field in fields.items():
|
||||
if not field.get('searchable', True) or name == 'rec_name':
|
||||
if not field.get('searchable', True):
|
||||
continue
|
||||
field = field.copy()
|
||||
fullname = '.'.join(filter(None, [prefix, name]))
|
||||
@@ -584,7 +589,7 @@ class DomainParser(object):
|
||||
and all(isinstance(c, (list, tuple)) for c in clause[1:])):
|
||||
return self.stringable(clause)
|
||||
name, _, value = clause[:3]
|
||||
if name.endswith('.rec_name'):
|
||||
if name.endswith('.rec_name') and value:
|
||||
name = name[:-len('.rec_name')]
|
||||
if name in self.fields:
|
||||
field = self.fields[name]
|
||||
@@ -619,10 +624,10 @@ class DomainParser(object):
|
||||
or clause[0] in ('AND', 'OR')):
|
||||
return '(%s)' % self.string(clause)
|
||||
name, operator, value = clause[:3]
|
||||
if name.endswith('.rec_name'):
|
||||
if name.endswith('.rec_name') and value:
|
||||
name = name[:-9]
|
||||
if name not in self.fields:
|
||||
if is_full_text(value):
|
||||
if value is not None and is_full_text(value):
|
||||
value = value[1:-1]
|
||||
return quote(value)
|
||||
field = self.fields[name]
|
||||
@@ -724,7 +729,7 @@ class DomainParser(object):
|
||||
name, operator, value = clause
|
||||
else:
|
||||
name, operator, value, target = clause
|
||||
if name.endswith('.rec_name'):
|
||||
if name.endswith('.rec_name') and value:
|
||||
name = name[:-9]
|
||||
value = target
|
||||
if name == 'rec_name':
|
||||
@@ -846,7 +851,7 @@ class DomainParser(object):
|
||||
target = None
|
||||
if field['type'] == 'reference':
|
||||
target, value = split_target_value(field, value)
|
||||
if target:
|
||||
if target and value:
|
||||
field_name += '.rec_name'
|
||||
elif field['type'] == 'multiselection':
|
||||
if value is not None and not isinstance(value, list):
|
||||
@@ -881,7 +886,7 @@ class DomainParser(object):
|
||||
continue
|
||||
if field['type'] in {
|
||||
'many2one', 'one2many', 'many2many', 'one2one',
|
||||
}:
|
||||
} and value:
|
||||
field_name += '.rec_name'
|
||||
if isinstance(value, list):
|
||||
value = [convert_value(field, v, self.context)
|
||||
|
||||
@@ -663,6 +663,10 @@ class DomainParserTestCase(TestCase):
|
||||
'string': "Name",
|
||||
'type': 'char',
|
||||
},
|
||||
'rec_name': {
|
||||
'string': "Record Name",
|
||||
'type': 'char',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -776,6 +780,15 @@ class DomainParserTestCase(TestCase):
|
||||
self.assertEqual(
|
||||
dom.string([('many2one.rec_name', 'in', ['John', 'Jane'])]),
|
||||
'Many2One: John;Jane')
|
||||
self.assertEqual(
|
||||
dom.string([('many2one.rec_name', '=', 'John')]),
|
||||
'Many2One: =John')
|
||||
self.assertEqual(
|
||||
dom.string([('many2one.rec_name', '=', '')]),
|
||||
'"Many2One.Record Name": =""')
|
||||
self.assertEqual(
|
||||
dom.string([('many2one.rec_name', '=', None)]),
|
||||
'"Many2One.Record Name": =')
|
||||
self.assertEqual(
|
||||
dom.string([('many2one.name', 'ilike', '%Foo%')]),
|
||||
"Many2One.Name: Foo")
|
||||
@@ -1112,6 +1125,14 @@ class DomainParserTestCase(TestCase):
|
||||
rlist(dom.parse_clause([('Many2One', None, ['John', 'Jane'])])), [
|
||||
('many2one.rec_name', 'in', ['John', 'Jane']),
|
||||
])
|
||||
self.assertEqual(
|
||||
rlist(dom.parse_clause([('Many2One', '=', None)])), [
|
||||
('many2one', '=', None),
|
||||
])
|
||||
self.assertEqual(
|
||||
rlist(dom.parse_clause([('Many2One', '=', '')])), [
|
||||
('many2one', '=', None),
|
||||
])
|
||||
self.assertEqual(
|
||||
rlist(dom.parse_clause(iter([iter([['John']])]))), [
|
||||
[('rec_name', 'ilike', '%John%')]])
|
||||
|
||||
Reference in New Issue
Block a user