Allow to search on rec_name of Reference fields

The syntax is similar to the display of Reference in list view:
<target name>,<search value>.
The target name is the name of the target model and the search value follow the
same behavior as when searching on Many2One.

review1331002
This commit is contained in:
Cédric Krier
2013-11-16 18:44:33 +01:00
parent 5c9080b717
commit aef4813e49
2 changed files with 172 additions and 63 deletions
+111 -60
View File
@@ -596,60 +596,65 @@
if (jQuery.isEmptyObject(clause)) {
return '';
}
var escaped;
if ((typeof clause[0] == 'string') &&
((clause[0] in this.fields) ||
(clause[0] == 'rec_name'))) {
var name = clause[0];
var operator = clause[1];
var value = clause[2];
if (!(name in this.fields)) {
escaped = value.replace('%%', '__');
if (escaped.startsWith('%') && escaped.endsWith('%')) {
value = value.slice(1, -1);
}
return this.quote(value);
}
var field = this.fields[name];
if (operator.contains('ilike')) {
escaped = value.replace('%%', '__');
if (escaped.startsWith('%') && escaped.endsWith('%')) {
value = value.slice(1, -1);
} else if (!escaped.contains('%')) {
if (operator == 'ilike') {
operator = '=';
} else {
operator = '!';
}
value = value.replace('%%', '%');
}
}
var def_operator = this.default_operator(field);
if ((def_operator == operator.trim()) ||
(operator.contains(def_operator) &&
operator.contains('not'))) {
operator = operator.replace(def_operator, '')
.replace('not', '!').trim();
}
if (operator.endsWith('in')) {
if (operator == 'not in') {
operator = '!';
} else {
operator = '';
}
}
var formatted_value = this.format_value(field, value);
if (~this.OPERATORS.indexOf(operator) &&
~['char', 'text', 'sha', 'selection']
.indexOf(field.type) &&
(value === '')) {
formatted_value = '""';
}
return (this.quote(field.string) + ': ' +
operator + formatted_value);
} else {
if ((typeof clause[0] != 'string') ||
~['AND', 'OR'].indexOf(clause[0])) {
return '(' + this.string(clause) + ')';
}
var escaped;
var name = clause[0];
var operator = clause[1];
var value = clause[2];
if (name.endsWith('.rec_name')) {
name = name.slice(0, -9);
}
if (!(name in this.fields)) {
escaped = value.replace('%%', '__');
if (escaped.startsWith('%') && escaped.endsWith('%')) {
value = value.slice(1, -1);
}
return this.quote(value);
}
var field = this.fields[name];
var target = null;
if (clause.length > 3) {
target = clause[3];
}
if (operator.contains('ilike')) {
escaped = value.replace('%%', '__');
if (escaped.startsWith('%') && escaped.endsWith('%')) {
value = value.slice(1, -1);
} else if (!escaped.contains('%')) {
if (operator == 'ilike') {
operator = '=';
} else {
operator = '!';
}
value = value.replace('%%', '%');
}
}
var def_operator = this.default_operator(field);
if ((def_operator == operator.trim()) ||
(operator.contains(def_operator) &&
operator.contains('not'))) {
operator = operator.replace(def_operator, '')
.replace('not', '!').trim();
}
if (operator.endsWith('in')) {
if (operator == 'not in') {
operator = '!';
} else {
operator = '';
}
}
var formatted_value = this.format_value(field, value, target);
if (~this.OPERATORS.indexOf(operator) &&
~['char', 'text', 'sha', 'selection']
.indexOf(field.type) &&
(value === '')) {
formatted_value = '""';
}
return (this.quote(field.string) + ': ' +
operator + formatted_value);
};
string = string.bind(this);
@@ -871,6 +876,14 @@
var operator = clause[1];
var value = clause[2];
var field = this.strings[clause[0].toLowerCase()];
var target = null;
if (field.type == 'reference') {
var split = this.split_target_value(field, value);
target = split[0];
value = split[1];
}
if (operator === null) {
operator = this.default_operator(field);
}
@@ -885,9 +898,6 @@
operator = this.negate_operator(
this.default_operator(field));
}
if (operator.contains('like')) {
value = this.likify(value);
}
if (~['integer', 'float', 'numeric', 'datetime', 'date',
'time'].indexOf(field.type)) {
if (value && value.contains('..')) {
@@ -908,7 +918,15 @@
} else {
value = this.convert_value(field, value);
}
result.push([field.name, operator, value]);
if (operator.contains('like')) {
value = this.likify(value);
}
if (target) {
result.push([field.name + '.rec_name', operator, value,
target]);
} else {
result.push([field.name, operator, value]);
}
} else {
result.push(this.parse_clause(clause));
}
@@ -940,8 +958,8 @@
return value;
},
default_operator: function(field) {
if (~['char', 'text', 'many2one', 'many2many', 'one2many']
.indexOf(field.type)) {
if (~['char', 'text', 'many2one', 'many2many', 'one2many',
'reference'].indexOf(field.type)) {
return 'ilike';
} else {
return '=';
@@ -960,6 +978,23 @@
time_format: function(field) {
return new Sao.PYSON.Decoder({}).decode(field.format);
},
split_target_value: function(field, value) {
var target = null;
if (typeof value == 'string') {
for (var i = 0; i < field.selection.length; i++) {
var selection = field.selection[i];
var key = selection[0];
var text = selection[1];
if (value.toLowerCase().startsWith(
text.toLowerCase() + ',')) {
target = key;
value = value.slice(text.length + 1);
break;
}
}
}
return [target, value];
},
convert_value: function(field, value) {
var convert_selection = function() {
if (typeof value == 'string') {
@@ -1062,7 +1097,10 @@
return value;
}
},
format_value: function(field, value) {
format_value: function(field, value, target) {
if (target === undefined) {
target = null;
}
var format_float = function() {
if (!value && value !== 0 && value !== new Sao.Decimal(0)) {
return '';
@@ -1084,6 +1122,19 @@
return value || '';
};
var format_reference = function() {
if (!target) {
return format_selection();
}
for (var i = 0; i < field.selection.length; i++) {
if (field.selection[i][0] == target) {
target = field.selection[i][1];
break;
}
}
return target + ',' + value;
};
var converts = {
'boolean': function() {
if (value) {
@@ -1102,7 +1153,7 @@
'float': format_float,
'numeric': format_float,
'selection': format_selection,
'reference': format_selection,
'reference': format_reference,
'datetime': function() {
if (!value) {
return '';
+61 -3
View File
@@ -939,6 +939,36 @@
});
});
QUnit.test('DomainParser.split_target_value', function() {
var parser = new Sao.common.DomainParser();
var field = {
'type': 'reference',
'selection': [
['spam', 'Spam'],
['ham', 'Ham'],
['e', 'Eggs']
]
};
var test_func = function(test) {
var value = test[0];
var result = test[1];
QUnit.ok(Sao.common.compare(
parser.split_target_value(this, value), result),
'split_target_value(' + JSON.stringify(this) + ', ' +
JSON.stringify(value) + ')');
};
[
['Spam', [null, 'Spam']],
['foo', [null, 'foo']],
['Spam,', ['spam', '']],
['Ham,bar', ['ham', 'bar']],
['Eggs,foo', ['e', 'foo']]
].forEach(test_func, field);
});
QUnit.test('DomainParser.convert_value', function() {
var parser = new Sao.common.DomainParser();
@@ -1090,6 +1120,15 @@
['male', 'Male'],
['female', 'Female']
]
},
'reference': {
'string': 'Reference',
'name': 'reference',
'type': 'reference',
'selection': [
['spam', 'Spam'],
['ham', 'Ham']
]
}
});
[
@@ -1109,7 +1148,15 @@
[[['Integer', null, '3..5']], [[
['integer', '>=', 3],
['integer', '<', 5]
]]]
]]],
[[['Reference', null, 'foo']], [['reference', 'ilike', '%foo%']]],
[[['Reference', null, 'Spam']], [['reference', 'ilike', '%spam%']]],
[[['Reference', null, 'Spam,bar']], [
['reference.rec_name', 'ilike', '%bar%', 'spam']
]],
[[['Reference', null, ['foo', 'bar']]], [
['reference', 'in', ['foo', 'bar']]
]]
].forEach(function(test) {
var value = test[0];
var result = test[1];
@@ -1237,6 +1284,14 @@
'date': {
'string': 'Date',
'type': 'date'
},
'reference': {
'string': 'Reference',
'type': 'reference',
'selection': [
['spam', 'Spam'],
['ham', 'Ham']
]
}
});
@@ -1272,8 +1327,11 @@
]
], 'Name: Doe (Name: John or Name: Jane)'],
[[], ''],
[[['surname', 'ilike', '%Doe%']], '"(Sur)Name": Doe']
//[[['date', '>=', new Date(2012, 10, 24)]], 'Date: >=10/24/2012']
[[['surname', 'ilike', '%Doe%']], '"(Sur)Name": Doe'],
//[[['date', '>=', new Date(2012, 10, 24)]], 'Date: >=10/24/2012'],
[[['reference', 'ilike', '%foo%']], 'Reference: foo'],
[[['reference', 'ilike', '%bar%', 'spam']], 'Reference: Spam,bar'],
[[['reference', 'in', ['foo', 'bar']]], 'Reference: foo;bar']
].forEach(function(test) {
var value = test[0];
var result = test[1];