1 openerp.base.search = function(openerp) {
3 openerp.base.SearchView = openerp.base.Widget.extend({
4 init: function(parent, element_id, dataset, view_id, defaults) {
5 this._super(parent, element_id);
6 this.dataset = dataset;
7 this.model = dataset.model;
8 this.view_id = view_id;
10 this.defaults = defaults || {};
13 this.enabled_filters = [];
15 this.has_focus = false;
17 this.ready = $.Deferred();
20 //this.log('Starting SearchView '+this.model+this.view_id)
21 this.rpc("/base/searchview/load", {"model": this.model, "view_id":this.view_id}, this.on_loaded);
22 return this.ready.promise();
31 * Builds a list of widget rows (each row is an array of widgets)
33 * @param {Array} items a list of nodes to convert to widgets
34 * @param {Object} fields a mapping of field names to (ORM) field attributes
37 make_widgets: function (items, fields) {
42 _.each(items, function (item) {
43 if (filters.length && item.tag !== 'filter') {
45 new openerp.base.search.FilterGroup(
50 if (item.tag === 'newline') {
53 } else if (item.tag === 'filter') {
54 if (!this.has_focus) {
55 item.attrs.default_focus = '1';
56 this.has_focus = true;
59 new openerp.base.search.Filter(
61 } else if (item.tag === 'separator') {
62 // a separator is a no-op
64 if (item.tag === 'group') {
65 // TODO: group and field should be fetched from registries, maybe even filters
67 new openerp.base.search.Group(
69 } else if (item.tag === 'field') {
70 if (!this.has_focus) {
71 item.attrs.default_focus = '1';
72 this.has_focus = true;
76 item, fields[item['attrs'].name]));
81 row.push(new openerp.base.search.FilterGroup(filters, this));
87 * Creates a field for the provided field descriptor item (which comes
88 * from fields_view_get)
90 * @param {Object} item fields_view_get node for the field
91 * @param {Object} field fields_get result for the field
92 * @returns openerp.base.search.Field
94 make_field: function (item, field) {
96 return new (openerp.base.search.fields.get_object(field.type))
99 if (! e instanceof openerp.base.KeyNotFound) {
102 // KeyNotFound means unknown field type
103 console.group('Unknown field type ' + field.type);
104 console.error('View node', item);
105 console.info('View field', field);
106 console.info('In view', this);
111 on_loaded: function(data) {
113 lines = this.make_widgets(
114 data.fields_view['arch'].children,
115 data.fields_view.fields);
117 // for extended search view
118 var ext = new openerp.base.search.ExtendedSearch(this, this.model);
120 this.inputs.push(ext);
122 var render = QWeb.render("SearchView", {
123 'view': data.fields_view['arch'],
125 'defaults': this.defaults
128 // We don't understand why the following commented line does not work in Chrome but
129 // the non-commented line does. As far as we investigated, only God knows.
130 //this.$element.html(render);
131 jQuery(render).appendTo(this.$element);
132 this.$element.find(".oe_search-view-custom-filter-btn").click(ext.on_activate);
134 var f = this.$element.find('form');
135 this.$element.find('form')
136 .submit(this.do_search)
137 .bind('reset', this.do_clear);
138 // start() all the widgets
139 var widget_starts = _(lines).chain().flatten().map(function (widget) {
140 return widget.start();
143 $.when.apply(null, widget_starts).then(function () {
144 self.ready.resolve();
147 this.reload_managed_filters();
149 reload_managed_filters: function() {
151 return this.rpc('/base/searchview/get_filters', {
152 model: this.dataset.model
153 }).then(function(result) {
154 self.managed_filters = result;
155 var filters = self.$element.find(".oe_search-view-filters-management");
156 filters.html(QWeb.render("SearchView.managed-filters", {filters: result}));
157 filters.change(self.on_filters_management);
161 * Handle event when the user make a selection in the filters management select box.
163 on_filters_management: function(e) {
165 var select = this.$element.find(".oe_search-view-filters-management");
166 var val = select.val();
168 if (val.slice(0,1) == "_") { // useless action
169 select.val("_filters");
172 if (val.slice(0, "get:".length) == "get:") {
173 val = val.slice("get:".length);
175 var filter = this.managed_filters[val];
176 this.on_search([filter.domain], [filter.context], []);
177 } else if (val == "save_filter") {
178 select.val("_filters");
179 var data = this.build_search_data();
180 var context = new openerp.base.CompoundContext();
181 _.each(data.contexts, function(x) {
184 var domain = new openerp.base.CompoundDomain();
185 _.each(data.domains, function(x) {
188 var dial_html = QWeb.render("SearchView.managed-filters.add");
189 var $dial = $(dial_html);
192 title: "Filter Entry",
195 $(this).dialog("close");
198 $(this).dialog("close");
199 var name = $(this).find("input").val();
200 self.rpc('/base/searchview/save_filter', {
201 model: self.dataset.model,
202 context_to_save: context,
206 self.reload_managed_filters();
211 } else { // manage_filters
212 select.val("_filters");
214 res_model: 'ir.filters',
215 views: [[false, 'list'], [false, 'form']],
216 type: 'ir.actions.act_window',
217 context: {"search_default_user_id": this.session.uid,
218 "search_default_model_id": this.dataset.model},
226 * Performs the search view collection of widget data.
228 * If the collection went well (all fields are valid), then triggers
229 * :js:func:`openerp.base.SearchView.on_search`.
231 * If at least one field failed its validation, triggers
232 * :js:func:`openerp.base.SearchView.on_invalid` instead.
234 * @param e jQuery event object coming from the "Search" button
236 do_search: function (e) {
237 // reset filters management
238 var select = this.$element.find(".oe_search-view-filters-management");
239 select.val("_filters");
241 if (e && e.preventDefault) { e.preventDefault(); }
243 var data = this.build_search_data();
245 if (data.errors.length) {
246 this.on_invalid(data.errors);
250 this.on_search(data.domains, data.contexts, data.groupbys);
252 build_search_data: function() {
257 _.each(this.inputs, function (input) {
259 var domain = input.get_domain();
261 domains.push(domain);
264 var context = input.get_context();
266 contexts.push(context);
269 if (e instanceof openerp.base.search.Invalid) {
277 // TODO: do we need to handle *fields* with group_by in their context?
278 var groupbys = _(this.enabled_filters)
280 .map(function (filter) { return filter.get_context();})
283 return {domains: domains, contexts: contexts, errors: errors, groupbys: groupbys};
286 * Triggered after the SearchView has collected all relevant domains and
289 * It is provided with an Array of domains and an Array of contexts, which
290 * may or may not be evaluated (each item can be either a valid domain or
291 * context, or a string to evaluate in order in the sequence)
293 * It is also passed an array of contexts used for group_by (they are in
294 * the correct order for group_by evaluation, which contexts may not be)
297 * @param {Array} domains an array of literal domains or domain references
298 * @param {Array} contexts an array of literal contexts or context refs
299 * @param {Array} groupbys ordered contexts which may or may not have group_by keys
301 on_search: function (domains, contexts, groupbys) {
304 * Triggered after a validation error in the SearchView fields.
306 * Error objects have three keys:
307 * * ``field`` is the name of the invalid field
308 * * ``value`` is the invalid value
309 * * ``message`` is the (in)validation message provided by the field
312 * @param {Array} errors a never-empty array of error objects
314 on_invalid: function (errors) {
315 this.notification.notify("Invalid Search", "triggered from search view");
317 do_clear: function (e) {
318 if (e && e.preventDefault) { e.preventDefault(); }
322 * Triggered when the search view gets cleared
326 on_clear: function () { },
328 * Called by a filter propagating its state changes
330 * @param {openerp.base.search.Filter} filter a filter which got toggled
331 * @param {Boolean} default_enabled filter got enabled through the default values, at render time.
333 do_toggle_filter: function (filter, default_enabled) {
334 if (default_enabled || filter.is_enabled()) {
335 this.enabled_filters.push(filter);
337 this.enabled_filters = _.without(
338 this.enabled_filters, filter);
341 if (!default_enabled) {
342 // selecting a filter after initial loading automatically
344 this.$element.find('form').submit();
350 openerp.base.search = {};
352 * Registry of search fields, called by :js:class:`openerp.base.SearchView` to
353 * find and instantiate its field widgets.
355 openerp.base.search.fields = new openerp.base.Registry({
356 'char': 'openerp.base.search.CharField',
357 'text': 'openerp.base.search.CharField',
358 'boolean': 'openerp.base.search.BooleanField',
359 'integer': 'openerp.base.search.IntegerField',
360 'float': 'openerp.base.search.FloatField',
361 'selection': 'openerp.base.search.SelectionField',
362 'datetime': 'openerp.base.search.DateTimeField',
363 'date': 'openerp.base.search.DateField',
364 'many2one': 'openerp.base.search.ManyToOneField',
365 'many2many': 'openerp.base.search.CharField',
366 'one2many': 'openerp.base.search.CharField'
368 openerp.base.search.Invalid = openerp.base.Class.extend( /** @lends openerp.base.search.Invalid# */{
370 * Exception thrown by search widgets when they hold invalid values,
371 * which they can not return when asked.
374 * @param field the name of the field holding an invalid value
375 * @param value the invalid value
376 * @param message validation failure message
378 init: function (field, value, message) {
381 this.message = message;
383 toString: function () {
384 return ('Incorrect value for field ' + this.field +
385 ': [' + this.value + '] is ' + this.message);
388 openerp.base.search.Widget = openerp.base.Widget.extend( /** @lends openerp.base.search.Widget# */{
391 * Root class of all search widgets
394 * @extends openerp.base.Widget
396 * @param view the ancestor view of this widget
398 init: function (view) {
402 * Sets and returns a globally unique identifier for the widget.
404 * If a prefix is specified, the identifier will be appended to it.
406 * @params prefix prefix sections, empty/falsy sections will be removed
408 make_id: function () {
409 this.element_id = _.uniqueId(
411 _.compact(_.toArray(arguments)),
413 return this.element_id;
416 * "Starts" the widgets. Called at the end of the rendering, this allows
417 * widgets to hook themselves to their view sections.
419 * On widgets, if they kept a reference to a view and have an element_id,
420 * will fetch and set their root element on $element.
424 if (this.view && this.element_id) {
425 // id is unique, and no getElementById on elements
426 this.$element = $(document.getElementById(
431 * "Stops" the widgets. Called when the view destroys itself, this
432 * lets the widgets clean up after themselves.
438 render: function (defaults) {
440 this.template, _.extend(this, {
445 openerp.base.search.FilterGroup = openerp.base.search.Widget.extend({
446 template: 'SearchView.filters',
447 init: function (filters, view) {
449 this.filters = filters;
450 this.length = filters.length;
454 _.each(this.filters, function (filter) {
459 openerp.base.search.add_expand_listener = function($root) {
460 $root.find('a.searchview_group_string').click(function (e) {
461 $root.toggleClass('folded expanded');
466 openerp.base.search.Group = openerp.base.search.Widget.extend({
467 template: 'SearchView.group',
468 init: function (view_section, view, fields) {
470 this.attrs = view_section.attrs;
471 this.lines = view.make_widgets(
472 view_section.children, fields);
473 this.make_id('group');
477 openerp.base.search.add_expand_listener(this.$element);
478 var widget_starts = _(this.lines).chain().flatten()
479 .map(function (widget) { return widget.start(); })
481 return $.when.apply(null, widget_starts);
485 openerp.base.search.Input = openerp.base.search.Widget.extend( /** @lends openerp.base.search.Input# */{
488 * @extends openerp.base.search.Widget
492 init: function (view) {
494 this.view.inputs.push(this);
496 get_context: function () {
498 "get_context not implemented for widget " + this.attrs.type);
500 get_domain: function () {
502 "get_domain not implemented for widget " + this.attrs.type);
505 openerp.base.search.Filter = openerp.base.search.Input.extend({
506 template: 'SearchView.filter',
507 init: function (node, view) {
509 this.attrs = node.attrs;
510 this.classes = [this.attrs.string ? 'filter_label' : 'filter_icon'];
511 this.make_id('filter', this.attrs.name);
516 this.$element.click(function (e) {
517 $(this).toggleClass('enabled');
518 self.view.do_toggle_filter(self);
522 * Returns whether the filter is currently enabled (in use) or not.
526 is_enabled:function () {
527 return this.$element.hasClass('enabled');
530 * If the filter is present in the defaults (and has a truthy value),
533 * @param {Object} defaults the search view's default values
535 render: function (defaults) {
536 if (this.attrs.name && defaults[this.attrs.name]) {
537 this.classes.push('enabled');
538 this.view.do_toggle_filter(this, true);
540 return this._super(defaults);
542 get_context: function () {
543 if (!this.is_enabled()) {
546 return this.attrs.context;
548 get_domain: function () {
549 if (!this.is_enabled()) {
552 return this.attrs.domain;
555 openerp.base.search.Field = openerp.base.search.Input.extend( /** @lends openerp.base.search.Field# */ {
556 template: 'SearchView.field',
557 default_operator: '=',
560 * @extends openerp.base.search.Input
562 * @param view_section
566 init: function (view_section, field, view) {
568 this.attrs = _.extend({}, field, view_section.attrs);
569 this.filters = new openerp.base.search.FilterGroup(_.map(
570 view_section.children, function (filter_node) {
571 return new openerp.base.search.Filter(
574 this.make_id('input', field.type, this.attrs.name);
578 this.filters.start();
580 get_context: function () {
581 var val = this.get_value();
582 // A field needs a value to be "active", and a context to send when
584 var has_value = (val !== null && val !== '');
585 var context = this.attrs.context;
586 if (!(has_value && context)) {
591 {own_values: {self: val}});
593 get_domain: function () {
594 var val = this.get_value();
595 if (val === null || val === '') {
599 var domain = this.attrs['filter_domain'];
603 this.attrs.operator || this.default_operator,
607 return _.extend({}, domain, {own_values: {self: val}});
611 * Implementation of the ``char`` OpenERP field type:
613 * * Default operator is ``ilike`` rather than ``=``
615 * * The Javascript and the HTML values are identical (strings)
618 * @extends openerp.base.search.Field
620 openerp.base.search.CharField = openerp.base.search.Field.extend( /** @lends openerp.base.search.CharField# */ {
621 default_operator: 'ilike',
622 get_value: function () {
623 return this.$element.val();
626 openerp.base.search.NumberField = openerp.base.search.Field.extend(/** @lends openerp.base.search.NumberField# */{
627 get_value: function () {
628 if (!this.$element.val()) {
631 var val = this.parse(this.$element.val()),
632 check = Number(this.$element.val());
633 if (isNaN(val) || val !== check) {
634 this.$element.addClass('error');
635 throw new openerp.base.search.Invalid(
636 this.attrs.name, this.$element.val(), this.error_message);
638 this.$element.removeClass('error');
644 * @extends openerp.base.search.NumberField
646 openerp.base.search.IntegerField = openerp.base.search.NumberField.extend(/** @lends openerp.base.search.IntegerField# */{
647 error_message: "not a valid integer",
648 parse: function (value) {
649 return parseInt(value, 10);
654 * @extends openerp.base.search.NumberField
656 openerp.base.search.FloatField = openerp.base.search.NumberField.extend(/** @lends openerp.base.search.FloatField# */{
657 error_message: "not a valid number",
658 parse: function (value) {
659 return parseFloat(value);
664 * @extends openerp.base.search.Field
666 openerp.base.search.SelectionField = openerp.base.search.Field.extend(/** @lends openerp.base.search.SelectionField# */{
667 template: 'SearchView.field.selection',
668 get_value: function () {
669 return this.$element.val();
672 openerp.base.search.BooleanField = openerp.base.search.SelectionField.extend(/** @lends openerp.base.search.BooleanField# */{
675 * @extends openerp.base.search.BooleanField
678 this._super.apply(this, arguments);
679 this.attrs.selection = [
685 * Search defaults likely to be boolean values (for a boolean field).
687 * In the HTML, we only want/get strings, and our strings here are ``true``
688 * and ``false``, so ensure we use precisely those by truth-testing the
689 * default value (iif there is one in the view's defaults).
691 * @param {Object} defaults default values for this search view
692 * @returns {String} rendered boolean field
694 render: function (defaults) {
695 var name = this.attrs.name;
696 if (name in defaults) {
697 defaults[name] = defaults[name] ? "true" : "false";
699 return this._super(defaults);
701 get_value: function () {
702 switch (this.$element.val()) {
703 case 'false': return false;
704 case 'true': return true;
705 default: return null;
709 openerp.base.search.DateField = openerp.base.search.Field.extend( /** @lends openerp.base.search.DateField# */{
711 * enables date picker on the HTML widgets
715 this.$element.addClass('field_date').datepicker({
716 dateFormat: 'yy-mm-dd'
720 this.$element.datepicker('destroy');
722 get_value: function () {
723 return this.$element.val();
726 openerp.base.search.DateTimeField = openerp.base.search.DateField.extend({
728 openerp.base.search.ManyToOneField = openerp.base.search.CharField.extend({
729 init: function (view_section, field, view) {
730 this._super(view_section, field, view);
732 this.got_name = $.Deferred().then(function () {
733 self.$element.val(self.name);
735 this.dataset = new openerp.base.DataSet(
736 this.view, this.attrs['relation']);
740 this.setup_autocomplete();
741 var started = $.Deferred();
742 this.got_name.then(function () { started.resolve();},
743 function () { started.resolve(); });
744 return started.promise();
746 setup_autocomplete: function () {
748 this.$element.autocomplete({
749 source: function (req, resp) {
750 self.dataset.name_search(
751 req.term, self.attrs.domain, 'ilike', 8, function (data) {
752 resp(_.map(data, function (result) {
753 return {id: result[0], label: result[1]}
757 select: function (event, ui) {
758 self.id = ui.item.id;
759 self.name = ui.item.label;
764 on_name_get: function (name_get) {
765 if (!name_get.length) {
767 this.got_name.reject();
770 this.name = name_get[0][1];
771 this.got_name.resolve();
773 render: function (defaults) {
774 if (defaults[this.attrs.name]) {
775 this.id = defaults[this.attrs.name];
776 // TODO: maybe this should not be completely removed
777 delete defaults[this.attrs.name];
778 this.dataset.name_get([this.id], $.proxy(this, 'on_name_get'));
780 this.got_name.reject();
782 return this._super(defaults);
784 get_domain: function () {
785 if (this.id && this.name) {
786 if (this.$element.val() === this.name) {
787 return [[this.attrs.name, '=', this.id]];
793 return this._super();
797 openerp.base.search.ExtendedSearch = openerp.base.OldWidget.extend({
798 template: 'SearchView.extended_search',
799 identifier_prefix: 'extended-search',
800 init: function (parent, model) {
804 add_group: function() {
805 var group = new openerp.base.search.ExtendedSearchGroup(this, this.fields);
806 group.appendTo(this.$element.find('.searchview_extended_groups_list'));
807 this.check_last_element();
811 if (!this.$element) {
812 return; // not a logical state but sometimes it happens
814 this.$element.closest("table.oe-searchview-render-line").css("display", "none");
816 this.rpc("/base/searchview/fields_get",
817 {"model": this.model}, function(data) {
818 self.fields = data.fields;
819 openerp.base.search.add_expand_listener(self.$element);
820 self.$element.find('.searchview_extended_add_group').click(function (e) {
825 get_context: function() {
828 get_domain: function() {
829 if (!this.$element) {
830 return null; // not a logical state but sometimes it happens
832 if(this.$element.closest("table.oe-searchview-render-line").css("display") == "none") {
835 return _.reduce(this.widget_children,
836 function(mem, x) { return mem.concat(x.get_domain());}, []);
838 on_activate: function() {
840 var table = this.$element.closest("table.oe-searchview-render-line");
841 table.css("display", "");
842 if(this.$element.hasClass("folded")) {
843 this.$element.toggleClass("folded expanded");
847 var table = this.$element.closest("table.oe-searchview-render-line");
848 table.css("display", "none");
849 if(this.$element.hasClass("expanded")) {
850 this.$element.toggleClass("folded expanded");
853 check_last_element: function() {
854 _.each(this.widget_children, function(x) {x.set_last_group(false);});
855 if (this.widget_children.length >= 1) {
856 this.widget_children[this.widget_children.length - 1].set_last_group(true);
861 openerp.base.search.ExtendedSearchGroup = openerp.base.OldWidget.extend({
862 template: 'SearchView.extended_search.group',
863 identifier_prefix: 'extended-search-group',
864 init: function (parent, fields) {
866 this.fields = fields;
868 add_prop: function() {
869 var prop = new openerp.base.search.ExtendedSearchProposition(this, this.fields);
870 var render = prop.render({'index': this.widget_children.length - 1});
871 this.$element.find('.searchview_extended_propositions_list').append(render);
878 this.$element.find('.searchview_extended_add_proposition').click(function () {
881 this.$element.find('.searchview_extended_delete_group').click(function () {
885 get_domain: function() {
886 var props = _(this.widget_children).chain().map(function(x) {
887 return x.get_proposition();
888 }).compact().value();
889 var choice = this.$element.find(".searchview_extended_group_choice").val();
890 var op = choice == "all" ? "&" : "|";
891 return choice == "none" ? ['!'] : [].concat(
892 _.map(_.range(_.max([0,props.length - 1])), function() { return op; }),
896 var parent = this.widget_parent;
897 if (this.widget_parent.widget_children.length == 1)
898 this.widget_parent.hide();
900 parent.check_last_element();
902 set_last_group: function(is_last) {
903 this.$element.toggleClass('last_group', is_last);
907 openerp.base.search.ExtendedSearchProposition = openerp.base.OldWidget.extend({
908 template: 'SearchView.extended_search.proposition',
909 identifier_prefix: 'extended-search-proposition',
910 init: function (parent, fields) {
912 this.fields = _(fields).chain()
913 .map(function(val, key) { return _.extend({}, val, {'name': key}); })
914 .sortBy(function(field) {return field.string;})
916 this.attrs = {_: _, fields: this.fields, selected: null};
921 this.select_field(this.fields.length > 0 ? this.fields[0] : null);
923 this.$element.find(".searchview_extended_prop_field").change(function() {
926 this.$element.find('.searchview_extended_delete_prop').click(function () {
932 if (this.widget_parent.widget_children.length == 1)
933 parent = this.widget_parent;
938 changed: function() {
939 var nval = this.$element.find(".searchview_extended_prop_field").val();
940 if(this.attrs.selected == null || nval != this.attrs.selected.name) {
941 this.select_field(_.detect(this.fields, function(x) {return x.name == nval;}));
945 * Selects the provided field object
947 * @param field a field descriptor object (as returned by fields_get, augmented by the field name)
949 select_field: function(field) {
951 if(this.attrs.selected != null) {
954 this.$element.find('.searchview_extended_prop_op').html('');
956 this.attrs.selected = field;
961 var type = field.type;
963 openerp.base.search.custom_filters.get_object(type);
965 if (! e instanceof openerp.base.KeyNotFound) {
969 this.log('Unknow field type ' + e.key);
971 this.value = new (openerp.base.search.custom_filters.get_object(type))
973 if(this.value.set_field) {
974 this.value.set_field(field);
976 _.each(this.value.operators, function(operator) {
977 var option = jQuery('<option>', {value: operator.value})
979 .appendTo(_this.$element.find('.searchview_extended_prop_op'));
981 this.$element.find('.searchview_extended_prop_value').html(
982 this.value.render({}));
986 get_proposition: function() {
987 if ( this.attrs.selected == null)
989 var field = this.attrs.selected.name;
990 var op = this.$element.find('.searchview_extended_prop_op').val();
991 var value = this.value.get_value();
992 return [field, op, value];
996 openerp.base.search.ExtendedSearchProposition.Char = openerp.base.OldWidget.extend({
997 template: 'SearchView.extended_search.proposition.char',
998 identifier_prefix: 'extended-search-proposition-char',
1000 {value: "ilike", text: "contains"},
1001 {value: "not ilike", text: "doesn't contain"},
1002 {value: "=", text: "is equal to"},
1003 {value: "!=", text: "is not equal to"},
1004 {value: ">", text: "greater than"},
1005 {value: "<", text: "less than"},
1006 {value: ">=", text: "greater or equal than"},
1007 {value: "<=", text: "less or equal than"}
1009 get_value: function() {
1010 return this.$element.val();
1013 openerp.base.search.ExtendedSearchProposition.DateTime = openerp.base.OldWidget.extend({
1014 template: 'SearchView.extended_search.proposition.datetime',
1015 identifier_prefix: 'extended-search-proposition-datetime',
1017 {value: "=", text: "is equal to"},
1018 {value: "!=", text: "is not equal to"},
1019 {value: ">", text: "greater than"},
1020 {value: "<", text: "less than"},
1021 {value: ">=", text: "greater or equal than"},
1022 {value: "<=", text: "less or equal than"}
1024 get_value: function() {
1025 return this.$element.val();
1029 this.$element.datetimepicker({
1030 dateFormat: 'yy-mm-dd',
1031 timeFormat: 'hh:mm:ss'
1035 openerp.base.search.ExtendedSearchProposition.Date = openerp.base.OldWidget.extend({
1036 template: 'SearchView.extended_search.proposition.date',
1037 identifier_prefix: 'extended-search-proposition-date',
1039 {value: "=", text: "is equal to"},
1040 {value: "!=", text: "is not equal to"},
1041 {value: ">", text: "greater than"},
1042 {value: "<", text: "less than"},
1043 {value: ">=", text: "greater or equal than"},
1044 {value: "<=", text: "less or equal than"}
1046 get_value: function() {
1047 return this.$element.val();
1051 this.$element.datepicker({
1052 dateFormat: 'yy-mm-dd',
1053 timeFormat: 'hh:mm:ss'
1057 openerp.base.search.ExtendedSearchProposition.Integer = openerp.base.OldWidget.extend({
1058 template: 'SearchView.extended_search.proposition.integer',
1059 identifier_prefix: 'extended-search-proposition-integer',
1061 {value: "=", text: "is equal to"},
1062 {value: "!=", text: "is not equal to"},
1063 {value: ">", text: "greater than"},
1064 {value: "<", text: "less than"},
1065 {value: ">=", text: "greater or equal than"},
1066 {value: "<=", text: "less or equal than"}
1068 get_value: function() {
1069 var value = parseFloat(this.$element.val());
1070 if(value != 0 && !value) {
1073 return Math.round(value);
1076 openerp.base.search.ExtendedSearchProposition.Float = openerp.base.OldWidget.extend({
1077 template: 'SearchView.extended_search.proposition.float',
1078 identifier_prefix: 'extended-search-proposition-float',
1080 {value: "=", text: "is equal to"},
1081 {value: "!=", text: "is not equal to"},
1082 {value: ">", text: "greater than"},
1083 {value: "<", text: "less than"},
1084 {value: ">=", text: "greater or equal than"},
1085 {value: "<=", text: "less or equal than"}
1087 get_value: function() {
1088 var value = parseFloat(this.$element.val());
1089 if(value != 0 && !value) {
1095 openerp.base.search.ExtendedSearchProposition.Selection = openerp.base.OldWidget.extend({
1096 template: 'SearchView.extended_search.proposition.selection',
1097 identifier_prefix: 'extended-search-proposition-selection',
1099 {value: "=", text: "is"},
1100 {value: "!=", text: "is not"}
1102 set_field: function(field) {
1105 get_value: function() {
1106 return this.$element.val();
1109 openerp.base.search.ExtendedSearchProposition.Boolean = openerp.base.OldWidget.extend({
1110 template: 'SearchView.extended_search.proposition.boolean',
1111 identifier_prefix: 'extended-search-proposition-boolean',
1113 {value: "=", text: "is true"},
1114 {value: "!=", text: "is false"}
1116 get_value: function() {
1121 openerp.base.search.custom_filters = new openerp.base.Registry({
1122 'char': 'openerp.base.search.ExtendedSearchProposition.Char',
1123 'text': 'openerp.base.search.ExtendedSearchProposition.Char',
1124 'one2many': 'openerp.base.search.ExtendedSearchProposition.Char',
1125 'many2one': 'openerp.base.search.ExtendedSearchProposition.Char',
1126 'many2many': 'openerp.base.search.ExtendedSearchProposition.Char',
1128 'datetime': 'openerp.base.search.ExtendedSearchProposition.DateTime',
1129 'date': 'openerp.base.search.ExtendedSearchProposition.Date',
1130 'integer': 'openerp.base.search.ExtendedSearchProposition.Integer',
1131 'float': 'openerp.base.search.ExtendedSearchProposition.Float',
1132 'boolean': 'openerp.base.search.ExtendedSearchProposition.Boolean',
1133 'selection': 'openerp.base.search.ExtendedSearchProposition.Selection'
1138 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: