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:
+111
-60
@@ -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
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user