Merge lp:~openerp-dev/openobject-client-web/trunk-fix-custom-filters-xmo into lp:openobject-client-web/trunk

Proposed by Xavier (Open ERP)
Status: Superseded
Proposed branch: lp:~openerp-dev/openobject-client-web/trunk-fix-custom-filters-xmo
Merge into: lp:openobject-client-web/trunk
Diff against target: 558 lines (+232/-188)
2 files modified
addons/openerp/controllers/search.py (+14/-10)
addons/openerp/static/javascript/search.js (+218/-178)
To merge this branch: bzr merge lp:~openerp-dev/openobject-client-web/trunk-fix-custom-filters-xmo
Reviewer Review Type Date Requested Status
Maxime Glorieux Pending
Aline (OpenERP) Pending
Review via email: mp+53764@code.launchpad.net

This proposal has been superseded by a proposal from 2011-03-17.

Description of the change

Re-applied niv's custom filters fixes, plus some fixes performed since

To post a comment you must log in.
4618. By Xavier (Open ERP)

[FIX] database filters: don't match on whole domain, only its first section (e.g. subdomain)

Revision history for this message
Aline (OpenERP) (apr-tinyerp) wrote :

- there is a bug by installing chart of account (this bug is fixed in trunk)
- the feature seems ok (no bug but not really user friendly)

Unmerged revisions

4619. By Xavier (Open ERP)

[FIX] revert 4456

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'addons/openerp/controllers/search.py'
2--- addons/openerp/controllers/search.py 2011-03-11 16:53:08 +0000
3+++ addons/openerp/controllers/search.py 2011-03-17 11:01:40 +0000
4@@ -18,6 +18,7 @@
5 # You can see the MPL licence at: http://www.mozilla.org/MPL/MPL-1.1.html
6 #
7 ###############################################################################
8+import simplejson
9 from openerp.utils import expr_eval, TinyDict, TinyForm, TinyFormError
10 from openobject.i18n.format import convert_date_format_in_domain
11 import actions
12@@ -195,17 +196,17 @@
13
14 record = kw.get('record')
15 record = eval(record)
16+
17 proxy = rpc.RPCProxy(model)
18 data = {}
19 res = proxy.fields_get(False, rpc.get_session().context)
20
21 all_values = {}
22+
23 errors = []
24-
25 for k, v in record.items():
26 values = {}
27 for key, val in v.items():
28- frm_datas = {}
29 for field in val:
30 fld = {
31 'value': val[field],
32@@ -214,16 +215,13 @@
33 if fld['type'] == 'many2many':
34 fld['type'] = 'char'
35 datas = {field: fld}
36- frm_datas[field] = fld
37+
38 try:
39- frm = TinyForm(**frm_datas).to_python()
40+ TinyForm(**data).to_python()
41 except TinyFormError, e:
42- error_field = e.field
43- error = ustr(e)
44- errors.append({e.field: error})
45+ errors.append({e.field: ustr(e)})
46 except Exception, e:
47- error = ustr(e)
48- errors.append({field: error})
49+ errors.append({field: ustr(e)})
50
51 datas['rec'] = field
52
53@@ -347,7 +345,13 @@
54 if not custom_domains:
55 custom_domains = []
56 else:
57- custom_domains = ast.literal_eval(custom_domains)
58+ try:
59+ # from JS custom filters, data is sent as JSON
60+ custom_domains = simplejson.loads(custom_domains)
61+ except simplejson.decoder.JSONDecodeError:
62+ # from switchView, data is sent as Python literals
63+ # (with unicode strings and keys)
64+ custom_domains = ast.literal_eval(custom_domains)
65
66 # conversion of the pseudo domain from the javascript to a valid domain
67 ncustom_domain = []
68
69=== modified file 'addons/openerp/static/javascript/search.js'
70--- addons/openerp/static/javascript/search.js 2011-03-02 09:50:09 +0000
71+++ addons/openerp/static/javascript/search.js 2011-03-17 11:01:40 +0000
72@@ -18,67 +18,63 @@
73 // You can see the MPL licence at: http://www.mozilla.org/MPL/MPL-1.1.html
74 //
75 ////////////////////////////////////////////////////////////////////////////////
76-var OR_LINE = jQuery('<tr>', {'id': 'or'}).append(
77- jQuery('<td>', {'colspan': 5}).append(
78- jQuery('<div>', {'class': 'filter-lsep'}),
79- jQuery('<hr>', {'class': 'filter-hr'}),
80- jQuery('<div>', {'class': 'filter-msep'}).html(_('Or')),
81- jQuery('<div>', {'class': 'filter-rsep'}),
82- jQuery('<hr>', {'class': 'filter-hr'})
83- )
84- );
85+var OR_LINE = '<tr id="or"><td colspan="5">' +
86+ '<div class="filter-lsep"></div><hr class="filter-hr">' +
87+ '<div class="filter-msep">Or</div>' +
88+ '<div class="filter-rsep"></div><hr class="filter-hr">' +
89+ '</td></tr>';
90
91 function add_filter_row(elem) {
92-
93- var $element = jQuery(elem);
94- var $filter_table = jQuery(idSelector('filter_table'));
95- var $filter_opt_tbl = jQuery(idSelector('filter_option_table'));
96+ var $element = jQuery(elem);
97+ var $filter_table = jQuery('#filter_table');
98+ var $filter_opt_tbl = jQuery('#filter_option_table');
99 var $cls_tbody = $element.closest("tbody");
100-
101- var selected_txt = jQuery('option:selected', $element).text();
102-
103- if (jQuery('tbody:visible', $filter_opt_tbl).length == 1 &&
104+ var selected_txt = $element.find('option:selected').text();
105+
106+ if ($filter_opt_tbl.find('tbody:visible').length == 1 &&
107 $cls_tbody.siblings().length == 1) {
108 if($filter_table.is(':hidden')) {
109- var $filterlabel = jQuery(idSelector('filterlabel'));
110+ var $filterlabel = jQuery('#filterlabel');
111 if ($filterlabel.text() == '') {
112- $filterlabel.text(selected_txt).attr('value', $element.val());
113+ $filterlabel.text(selected_txt)
114+ .attr('value', $element.val());
115 }
116 $filter_table.show();
117 }
118 } else {
119- var $position_tr = jQuery('tr:last', $cls_tbody).prev();
120+ var $position_tr = $cls_tbody.find('tr:last').prev();
121 if ($cls_tbody.prev().attr('id') == 'filter_table') {
122- $position_tr = jQuery('tr:last', $filter_table);
123+ $position_tr = $filter_table.find('tr:last');
124 }
125-
126- var old_tr = jQuery('tbody:first tr.filter_row_class:first', $filter_opt_tbl);
127- var new_tr = old_tr.clone();
128- var new_tr_lbl = jQuery(idSelector('filterlabel'), new_tr).text(selected_txt).attr('value', $element.val());
129-
130- var new_tr_qstring = jQuery('input.qstring', new_tr);
131- new_tr_qstring.val('');
132- if (new_tr.is(':hidden')) {
133- new_tr.show();
134+ var $old_tr = $filter_opt_tbl.find('tbody:first tr.filter_row_class:first');
135+
136+ var $new_tr = $old_tr.clone();
137+ var $new_tr_lbl = $new_tr.find('#filterlabel')
138+ .text(selected_txt)
139+ .attr('value', $element.val());
140+
141+ var $new_tr_qstring = $new_tr.find('input.qstring')
142+ .css('background', '#fff').val('');
143+ if ($new_tr.is(':hidden')) {
144+ $new_tr.show();
145 }
146
147 var index_row;
148 var $curr_body = $position_tr.closest('tbody');
149-
150- jQuery(idSelector('filterlabel'), $curr_body).each(function(k, v) {
151+ $curr_body.find('#filterlabel').each(function(k, v) {
152
153 if (jQuery(v).text() != selected_txt) { return; }
154 index_row = k;
155- jQuery('select.expr', new_tr).hide();
156- new_tr_lbl.hide();
157- jQuery('label.and_or', new_tr).remove();
158- jQuery('<label>', {'class': 'and_or'}).text('OR').insertBefore(new_tr_qstring);
159+ $new_tr.find('select.expr').hide();
160+ $new_tr_lbl.hide();
161+ $new_tr.find('label.and_or').remove();
162+ jQuery('<label>', {'class': 'and_or'}).text('OR').insertBefore($new_tr_qstring);
163 });
164
165 if(index_row >= 0) {
166- $position_tr = jQuery('tr.filter_row_class', $curr_body)[index_row];
167+ $position_tr = $curr_body.find('tr.filter_row_class')[index_row];
168 }
169- jQuery($position_tr).after(new_tr);
170+ jQuery($position_tr).after($new_tr);
171 }
172
173 var select_or = jQuery('select.filter_fields_or');
174@@ -90,21 +86,22 @@
175 }
176
177 function addOrBlock(elem){
178- var $filter_option_table = jQuery(idSelector('filter_option_table'));
179- jQuery('tr:last select.filter_fields_or', $filter_option_table).parent().hide();
180+ var $filter_option_table = jQuery('#filter_option_table');
181+ $filter_option_table.find('tr:last select.filter_fields_or').parent().hide();
182
183 var $newtbody = jQuery('<tbody>').append(OR_LINE);
184 $filter_option_table.append($newtbody);
185- var $new_tr = jQuery('tr:first', $filter_option_table).clone();
186- jQuery(idSelector('filterlabel'), $new_tr)
187- .attr('value', jQuery(elem).val())
188- .text(jQuery('select.filter_fields_or option:selected').text());
189-
190- jQuery('input.qstring', $new_tr).val('');
191+
192+ var $new_tr = $filter_option_table.find('tr:first').clone();
193+ $new_tr.find('#filterlabel').attr('value', jQuery(elem).val())
194+ .text(jQuery('select.filter_fields_or option:selected').text());
195+ $new_tr.find('input.qstring').val('');
196 $newtbody.append($new_tr);
197
198- var $action_tr = jQuery(idSelector('filter_table')).next('tbody.actions').find('tr.actions').clone();
199- jQuery('select.filter_fields_or', $action_tr).attr('disabled', false).parent().show();
200+ var $action_tr = jQuery('#filter_table')
201+ .next('tbody.actions').find('tr.actions').clone();
202+ $action_tr.find('select.filter_fields_or')
203+ .attr('disabled', false).parent().show();
204 if ($action_tr.is(':hidden')) {
205 $action_tr.show();
206 }
207@@ -263,22 +260,22 @@
208 jQuery.extend({
209 keys: function(obj){
210 var a = [];
211- $.each(obj, function(k){ a.push(k) });
212+ $.each(obj, function(k){ a.push(k); });
213 return a;
214 }
215 });
216
217 function display_Customfilters(all_domains, group_by_ctx) {
218 var Allrecords = {};
219- var error = false;
220- jQuery('tbody:visible',idSelector('filter_option_table')).each(function () {
221+ jQuery('#filter_option_table > tbody:visible').each(function () {
222+ var missing_field_value = false;
223 var record = {};
224 var pid = jQuery(this).index();
225
226 jQuery(this).children('.filter_row_class').each(function () {
227- var $constraint_value = jQuery('input.qstring', this);
228- var $fieldname = jQuery(idSelector('filterlabel'), this);
229- var id = jQuery('.filter_row_class', jQuery(this).parent()).index(this);
230+ var $constraint_value = jQuery(this).find('input.qstring');
231+ var $fieldname = jQuery(this).find('#filterlabel');
232+ var id = jQuery(this).parent().find('> .filter_row_class').index(this);
233
234 if($constraint_value.val()) {
235 var rec = {};
236@@ -286,132 +283,175 @@
237 record[id] = rec;
238 } else {
239 $constraint_value.addClass('errorfield').val(_('Invalid Value')).click(function() {
240- jQuery(this).val('').removeClass('errorfield')
241+ jQuery(this).val('').removeClass('errorfield');
242 });
243- error = true;
244+ missing_field_value = true;
245 }
246 });
247+ if(missing_field_value) { return; }
248
249 if (jQuery.keys(record).length != 0){
250 Allrecords[pid] = record;
251 }
252 });
253-
254- if (!error) {
255- openobject.http.postJSON('/openerp/search/get', {
256- record: serializeJSON(Allrecords),
257- _terp_model: jQuery(idSelector('_terp_model')).val()
258- }).addCallback(function(obj){
259- var custom_domain = [];
260- if (obj.errors.length) {
261- for (er_field in obj.errors) {
262- for (er in obj.errors[er_field]) {
263- jQuery('tbody .filter_row_class', idSelector('filter_option_table')).each(function(){
264- if (jQuery(idSelector('filterlabel'), this).attr('value') == er) {
265- jQuery('input.qstring', this).addClass('errorfield').val(obj.errors[er_field][er]).click(function(){
266- jQuery(this).val('').removeClass('errorfield');
267- });
268- }
269- })
270- }
271- }
272- return;
273- }
274- var form_result = obj.frm;
275- var tbody_keys = jQuery.keys(form_result);
276-
277- if (form_result) {
278- // By property, we get incorrect ordering
279- for (var ind = 0; ind < tbody_keys.length; ind++) {
280- var All_domain = [];
281- var group = [];
282- var tbody_frm_ind = form_result[tbody_keys[ind]]; //tbody dictionary
283- var trs_keys = jQuery.unique(jQuery.keys(tbody_frm_ind)); //sort trs
284- for (var index = 0; index < trs_keys.length; index++) {
285- var return_record = tbody_frm_ind[trs_keys[index]];
286- var $curr_body = jQuery('tbody', idSelector('filter_option_table')).eq(tbody_keys[ind]);
287- var $row = jQuery('.filter_row_class', $curr_body).eq(trs_keys[index]);
288- var $next_row = [];
289- if ($row.next('tr.filter_row_class').find('input.qstring').val() != '') {
290- $next_row = jQuery($row.next());
291- }
292-
293- var type = return_record.type;
294- var temp_domain = [];
295- var grouping = $next_row.length != 0 ? jQuery('label.and_or', $next_row).text() : null;
296-
297- if (group.length == 0) {
298- var $new_grp = $curr_body.find('tr.filter_row_class:gt(' + trs_keys[index] + ')').find('td#filter_column:not(:has(label)) input.qstring[value]');
299- if ($new_grp.length) {
300- group.push('&')
301- }
302- }
303- if (grouping) {
304- temp_domain.push(grouping == 'AND' ? '&' : '|');
305- }
306-
307- var field = return_record['rec'];
308- var comparison = jQuery('select.expr', $row).val();
309- var value = return_record['rec_val'];
310-
311- switch (comparison) {
312- case 'ilike':
313- case 'not ilike':
314- if (isOrderable(type)) {
315- comparison = (comparison == 'ilike' ? '=' : '!=');
316- }
317- break;
318- case '<':
319- case '>':
320- if (!isOrderable(type)) {
321- comparison = '=';
322- }
323- break;
324- case 'in':
325- case 'not in':
326- if (typeof value == 'string') {
327- value = value.split(',');
328- }
329- else
330- if (type == 'many2many') {
331- /* very weird array-type construct
332- looks a bit like [[6, 0, [list of ids here]]]
333- */
334- value = value[value.length - 1][value[value.length - 1].length - 1]
335- }
336- else
337- if (type == 'one2many') {
338- value = value[0];
339- }
340- else {
341- value = value;
342- }
343- break;
344- }
345-
346- if (jQuery('label.and_or', $row).length > 0 || grouping) {
347- temp_domain.push(field, comparison, value);
348- group.push(temp_domain);
349- }
350- else {
351- group.push(field, comparison, value)
352- }
353-
354- if (!grouping) {
355- All_domain.push(group);
356- group = [];
357- }
358- }
359-
360- if (All_domain.length) {
361- custom_domain.push(All_domain);
362- }
363- }
364- }
365-
366- final_search_domain(serializeJSON(custom_domain), all_domains, group_by_ctx);
367- });
368- }
369+
370+ openobject.http.postJSON('/openerp/search/get', {
371+ record: serializeJSON(Allrecords),
372+ _terp_model: jQuery('#_terp_model').val()
373+ }).addCallback(function(obj) {
374+ var custom_domain = [];
375+ if(obj.errors.length) {
376+ jQuery.each(obj.errors, function (i, error) {
377+ for(var field in error) {
378+ jQuery('#filter_option_table tbody .filter_row_class').each(function () {
379+ if(jQuery(this).find('#filterlabel').attr('value') == field) {
380+ jQuery(this).find('input.qstring').addClass('errorfield').val(error[field]).click(function () {
381+ jQuery(this).val('').removeClass('errorfield');
382+ });
383+ }
384+ });
385+ }
386+ });
387+ return;
388+ }
389+
390+ var form_result = obj.frm;
391+ var tbody_keys = jQuery.keys(form_result);
392+
393+ if(form_result) {
394+ // By property, we get incorrect ordering
395+ for(var ind=0; ind<tbody_keys.length ;ind++){
396+ var All_domain = [];
397+ var group = [];
398+ var tbody_frm_ind = form_result[tbody_keys[ind]]; //tbody dictionary
399+ var trs_keys = jQuery.unique(jQuery.keys(tbody_frm_ind)); //sort trs
400+
401+ for(var index = 0; index<trs_keys.length ; index++) {
402+ var return_record = tbody_frm_ind[trs_keys[index]];
403+ var $curr_body = jQuery('#filter_option_table > tbody').eq(tbody_keys[ind]);
404+ var $row = $curr_body.find('> .filter_row_class').eq(trs_keys[index]);
405+ var $next_row = [];
406+
407+ if ($row.next('tr.filter_row_class').find('input.qstring').val() != ''){
408+ $next_row = jQuery($row.next());
409+ }
410+
411+ var type = return_record.type;
412+ var temp_domain = [];
413+ var grouping = $next_row.length != 0 ? $next_row.find('label.and_or').text(): null;
414+
415+ if (group.length==0) {
416+ var $new_grp = $curr_body.find('tr.filter_row_class:gt('+trs_keys[index]+')')
417+ .find('td#filter_column:not(:has(label)) input.qstring[value]');
418+ if ($new_grp.length){
419+ group.push('&');
420+ }
421+ }
422+ if(grouping) {
423+ temp_domain.push(grouping == 'AND' ? '&' : '|');
424+ }
425+
426+ var field = return_record['rec'];
427+ var comparison = $row.find('select.expr').val();
428+ var value = return_record['rec_val'];
429+
430+ // if there are multiple values we must split them before conversion
431+ var isMultipleValues = comparison == 'in' || comparison == 'not in';
432+ var values;
433+ if(isMultipleValues) {
434+ values = value.split(',');
435+ } else {
436+ values = [value];
437+ }
438+ // converting values
439+ var newValues = jQuery.map(values, function(valuePart, i) {
440+ switch (type) {
441+ case "string":
442+ case "many2one":
443+ case "many2many":
444+ case "one2many":
445+ case "date":
446+ case "reference":
447+ case "char":
448+ case "text":
449+ case "datetime":
450+ case "time":
451+ case "binary":
452+ case "selection":
453+ case "one2one":
454+ return valuePart;
455+ case "boolean":
456+ switch(valuePart.toLowerCase().trim()) {
457+ case 'true':
458+ case 'yes':
459+ case '1':
460+ return true;
461+ case 'false':
462+ case 'no':
463+ case '0':
464+ return false;
465+ default:
466+ return Boolean(valuePart);
467+ }
468+ case "integer":
469+ case "integer_big":
470+ var intValue = parseInt(valuePart, 10);
471+ if(! isNaN(intValue)) {
472+ return intValue;
473+ }
474+ // remove value from resulting array
475+ return;
476+ case "float":
477+ var floatValue = parseFloat(valuePart);
478+ if(! isNaN(floatValue)) {
479+ return floatValue;
480+ }
481+ return;
482+ default:
483+ return;
484+ }
485+ });
486+ if(isMultipleValues) {
487+ value = newValues;
488+ } else {
489+ value = newValues[0];
490+ }
491+
492+ switch (comparison) {
493+ case 'ilike':
494+ case 'not ilike':
495+ if(isOrderable(type)) {
496+ comparison = (comparison == 'ilike' ? '=' : '!=');
497+ }
498+ break;
499+ case '<':
500+ case '>':
501+ if(!isOrderable(type)) {
502+ comparison = '=';
503+ }
504+ break;
505+ }
506+
507+ if ($row.find('label.and_or').length || grouping){
508+ temp_domain.push(field, comparison, value);
509+ group.push(temp_domain);
510+ } else {
511+ group.push(field, comparison, value);
512+ }
513+
514+ if (!grouping) {
515+ All_domain.push(group);
516+ group = [];
517+ }
518+ }
519+
520+ if (All_domain.length) {
521+ custom_domain.push(All_domain);
522+ }
523+ }
524+ }
525+ final_search_domain(serializeJSON(custom_domain), all_domains, group_by_ctx);
526+ });
527 }
528
529 var group_by = [];
530@@ -423,22 +463,22 @@
531 var domains = {};
532 var search_context = {};
533 var all_boxes = [];
534- var $filter_list = jQuery(idSelector('filter_list'));
535+ var $filter_list = jQuery('#filter_list');
536 var domain = 'None';
537 if (jQuery('div.group-data').length) {
538- jQuery('button', 'div.group-data').each(function(){
539+ jQuery('div.group-data button').each(function(){
540 if (jQuery(this).hasClass('active')) {
541 var _grp = jQuery(this).next('input').attr('group_by_ctx');
542 if (jQuery.inArray(_grp, group_by) < 0) {
543- group_by.push(_grp)
544+ group_by.push(_grp);
545 }
546 }
547- })
548+ });
549 }
550- if (jQuery(idSelector('filter_list')).attr('selectedIndex') > 0) {
551+ if (jQuery('#filter_list').attr('selectedIndex') > 0) {
552 all_domains['selection_domain'] = $filter_list.val();
553 var selected_index = $filter_list.attr('selectedIndex');
554- var filter_grps = jQuery('option:selected', idSelector('filter_list')).attr('group_by');
555+ var filter_grps = jQuery('#filter_list option:selected').attr('group_by');
556 if(selected_index > 0) {
557 if(filter_grps && filter_grps!='[]') {
558 group_by = eval(filter_grps);