[MERGE] forward port of branch saas-3 up to fe8106f
[odoo/odoo.git] / addons / web / static / src / js / view_form.js
index e93375c..ee15592 100644 (file)
@@ -102,6 +102,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
         this.fields = {};
         this.fields_order = [];
         this.datarecord = {};
+        this._onchange_specs = {};
+        this.onchanges_defs = [];
         this.default_focus_field = null;
         this.default_focus_button = null;
         this.fields_registry = instance.web.form.widgets;
@@ -117,7 +119,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
         });
         this.is_initialized = $.Deferred();
         this.mutating_mutex = new $.Mutex();
-        this.on_change_list = [];
         this.save_list = [];
         this.reload_mutex = new $.Mutex();
         this.__clicked_inside = false;
@@ -125,6 +126,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
         this.rendering_engine = new instance.web.form.FormRenderingEngine(this);
         self.set({actual_mode: self.options.initial_mode});
         this.has_been_loaded.done(function() {
+            self._build_onchange_specs();
             self.on("change:actual_mode", self, self.check_actual_mode);
             self.check_actual_mode();
             self.on("change:actual_mode", self, self.init_pager);
@@ -335,15 +337,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
         });
         return $.when.apply(null, set_values).then(function() {
             if (!record.id) {
-                // New record: Second pass in order to trigger the onchanges
-                // respecting the fields order defined in the view
-                _.each(self.fields_order, function(field_name) {
-                    if (record[field_name] !== undefined) {
-                        var field = self.fields[field_name];
-                        field._dirty_flag = true;
-                        self.do_onchange(field);
-                    }
-                });
+                // trigger onchanges
+                self.do_onchange(null);
             }
             self.on_form_changed();
             self.rendering_engine.init_fields();
@@ -438,101 +433,83 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
             $(".oe_form_pager_state", this.$pager).html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
         }
     },
-    parse_on_change: function (on_change, widget) {
-        var self = this;
-        var onchange = _.str.trim(on_change);
-        var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/);
-        if (!call) {
-            throw new Error(_.str.sprintf( _t("Wrong on change format: %s"), onchange ));
-        }
 
-        var method = call[1];
-        if (!_.str.trim(call[2])) {
-            return {method: method, args: []};
-        }
-
-        var argument_replacement = {
-            'False': function () {return false;},
-            'True': function () {return true;},
-            'None': function () {return null;},
-            'context': function () {
-                return new instance.web.CompoundContext(
-                        self.dataset.get_context(),
-                        widget.build_context() ? widget.build_context() : {});
-            }
-        };
-        var parent_fields = null;
-        var args = _.map(call[2].split(','), function (a, i) {
-            var field = _.str.trim(a);
-
-            // literal constant or context
-            if (field in argument_replacement) {
-                return argument_replacement[field]();
-            }
-            // literal number
-            if (/^-?\d+(\.\d+)?$/.test(field)) {
-                return Number(field);
-            }
-            // form field
-            if (self.fields[field]) {
-                var value_ = self.fields[field].get_value();
-                return value_ === null || value_ === undefined ? false : value_;
-            }
-            // parent field
-            var splitted = field.split('.');
-            if (splitted.length > 1 && _.str.trim(splitted[0]) === "parent" && self.dataset.parent_view) {
-                if (parent_fields === null) {
-                    parent_fields = self.dataset.parent_view.get_fields_values();
+    _build_onchange_specs: function() {
+        var self = this;
+        var find = function(field_name, root) {
+            var fields = [root];
+            while (fields.length) {
+                var node = fields.pop();
+                if (!node) {
+                    continue;
                 }
-                var p_val = parent_fields[_.str.trim(splitted[1])];
-                if (p_val !== undefined) {
-                    return p_val === null || p_val === undefined ? false : p_val;
+                if (node.tag === 'field' && node.attrs.name === field_name) {
+                    return node.attrs.on_change || "";
                 }
+                fields = _.union(fields, node.children);
             }
-            // string literal
-            var first_char = field[0], last_char = field[field.length-1];
-            if ((first_char === '"' && last_char === '"')
-                || (first_char === "'" && last_char === "'")) {
-                return field.slice(1, -1);
-            }
+            return "";
+        };
 
-            throw new Error("Could not get field with name '" + field +
-                            "' for onchange '" + onchange + "'");
+        self._onchange_specs = {};
+        _.each(this.fields, function(field, name) {
+            self._onchange_specs[name] = find(name, field.node);
+            _.each(field.field.views, function(view) {
+                _.each(view.fields, function(_, subname) {
+                    self._onchange_specs[name + '.' + subname] = find(subname, view.arch);
+                });
+            });
         });
-
-        return {
-            method: method,
-            args: args
-        };
     },
-    do_onchange: function(widget, processed) {
-        var self = this;
-        this.on_change_list = [{widget: widget, processed: processed}].concat(this.on_change_list);
-        return this._process_operations();
+    _get_onchange_values: function() {
+        var field_values = this.get_fields_values();
+        if (field_values.id.toString().match(instance.web.BufferedDataSet.virtual_id_regex)) {
+            delete field_values.id;
+        }
+        if (this.dataset.parent_view) {
+            // this belongs to a parent view: add parent field if possible
+            var parent_view = this.dataset.parent_view;
+            var child_name = this.dataset.child_name;
+            var parent_name = parent_view.get_field_desc(child_name).relation_field;
+            if (parent_name) {
+                // consider all fields except the inverse of the parent field
+                var parent_values = parent_view.get_fields_values();
+                delete parent_values[child_name];
+                field_values[parent_name] = parent_values;
+            }
+        }
+        return field_values;
     },
-    _process_onchange: function(on_change_obj) {
+
+    do_onchange: function(widget) {
         var self = this;
-        var widget = on_change_obj.widget;
-        var processed = on_change_obj.processed;
+        var onchange_specs = self._onchange_specs;
         try {
-            var def;
-            processed = processed || [];
-            processed.push(widget.name);
-            var on_change = widget.node.attrs.on_change;
-            if (on_change) {
-                var change_spec = self.parse_on_change(on_change, widget);
-                var ids = [];
+            var def = $.when({});
+            var change_spec = widget ? onchange_specs[widget.name] : null;
+            if (!widget || (!_.isEmpty(change_spec) && change_spec !== "0")) {
+                var ids = [],
+                    trigger_field_name = widget ? widget.name : false,
+                    values = self._get_onchange_values(),
+                    context = new instance.web.CompoundContext(self.dataset.get_context());
+
+                if (widget && widget.build_context()) {
+                    context.add(widget.build_context());
+                }
+                if (self.dataset.parent_view) {
+                    var parent_name = self.dataset.parent_view.get_field_desc(self.dataset.child_name).relation_field;
+                    context.add({field_parent: parent_name});
+                }
+
                 if (self.datarecord.id && !instance.web.BufferedDataSet.virtual_id_regex.test(self.datarecord.id)) {
                     // In case of a o2m virtual id, we should pass an empty ids list
                     ids.push(self.datarecord.id);
                 }
                 def = self.alive(new instance.web.Model(self.dataset.model).call(
-                    change_spec.method, [ids].concat(change_spec.args)));
-            } else {
-                def = $.when({});
+                    "onchange", [ids, values, trigger_field_name, onchange_specs, context]));
             }
-            return def.then(function(response) {
-                if (widget.field['change_default']) {
+            var onchange_def = def.then(function(response) {
+                if (widget && widget.field['change_default']) {
                     var fieldname = widget.name;
                     var value_;
                     if (response.value && (fieldname in response.value)) {
@@ -565,15 +542,17 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
                 }
                 return response;
             }).then(function(response) {
-                return self.on_processed_onchange(response, processed);
+                return self.on_processed_onchange(response);
             });
+            this.onchanges_defs.push(onchange_def);
+            return onchange_def;
         } catch(e) {
             console.error(e);
             instance.webclient.crashmanager.show_message(e);
             return $.Deferred().reject();
         }
     },
-    on_processed_onchange: function(result, processed) {
+    on_processed_onchange: function(result) {
         try {
         var fields = this.fields;
         _(result.domain).each(function (domain, fieldname) {
@@ -581,18 +560,19 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
             if (!field) { return; }
             field.node.attrs.domain = domain;
         });
-            
-        if (result.value) {
-            this._internal_set_values(result.value, processed);
+
+        if (!_.isEmpty(result.value)) {
+            this._internal_set_values(result.value);
         }
+        // FIXME XXX a list of warnings?
         if (!_.isEmpty(result.warning)) {
-            instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), {
+            new instance.web.Dialog(this, {
+                size: 'medium',
                 title:result.warning.title,
-                modal: true,
                 buttons: [
-                    {text: _t("Ok"), click: function() { $(this).dialog("close"); }}
+                    {text: _t("Ok"), click: function() { this.parents('.modal').modal('hide'); }}
                 ]
-            });
+            }, QWeb.render("CrashManager.warning", result.warning)).open();
         }
 
         return $.Deferred().resolve();
@@ -606,21 +586,21 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
         var self = this;
         return this.mutating_mutex.exec(function() {
             function iterate() {
-                var on_change_obj = self.on_change_list.shift();
-                if (on_change_obj) {
-                    return self._process_onchange(on_change_obj).then(function() {
-                        return iterate();
+                var start = $.Deferred();
+                start.resolve();
+                start = _.reduce(self.onchanges_defs, function(memo, d){
+                    return memo.then(function(){
+                        return d;
+                    }, function(){
+                        return d;
                     });
-                }
-                var defs = [];
+                }, start);
+                var defs = [start];
                 _.each(self.fields, function(field) {
                     defs.push(field.commit_value());
                 });
                 var args = _.toArray(arguments);
                 return $.when.apply($, defs).then(function() {
-                    if (self.on_change_list.length !== 0) {
-                        return iterate();
-                    }
                     var save_obj = self.save_list.pop();
                     if (save_obj) {
                         return self._process_save(save_obj).then(function() {
@@ -639,8 +619,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
             return iterate();
         });
     },
-    _internal_set_values: function(values, exclude) {
-        exclude = exclude || [];
+    _internal_set_values: function(values) {
         for (var f in values) {
             if (!values.hasOwnProperty(f)) { continue; }
             var field = this.fields[f];
@@ -652,9 +631,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
                     field.set_value(value_);
                     field._inhibit_on_change_flag = false;
                     field._dirty_flag = true;
-                    if (!_.contains(exclude, field.name)) {
-                        this.do_onchange(field, exclude);
-                    }
                 }
             }
         }
@@ -678,6 +654,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
      * if the current record is not yet saved. It will then stay in create mode.
      */
     to_edit_mode: function() {
+        this.onchanges_defs = [];
         this._actualize_mode("edit");
     },
     /**
@@ -737,9 +714,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
             self.trigger("save", result);
             self.reload().then(function() {
                 self.to_view_mode();
-                var parent = self.ViewManager.ActionManager.getParent();
-                if(parent){
-                    parent.menu.do_reload_needaction();
+                var menu = instance.webclient.menu;
+                if (menu) {
+                    menu.do_reload_needaction();
                 }
             });
         }).always(function(){
@@ -826,11 +803,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
         var self = this;
         var save_obj = {prepend_on_create: prepend_on_create, ret: null};
         this.save_list.push(save_obj);
-        return this._process_operations().then(function() {
+        return self._process_operations().then(function() {
             if (save_obj.error)
                 return $.Deferred().reject();
             return $.when.apply($, save_obj.ret);
-        }).done(function() {
+        }).done(function(result) {
             self.$el.removeClass('oe_form_dirty');
         });
     },
@@ -1086,7 +1063,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
                 };
             })
             .value();
-
         var d = new instance.web.Dialog(this, {
             title: _t("Set Default"),
             args: {
@@ -1198,7 +1174,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
         this.fvg = fvg;
         this.version = parseFloat(this.fvg.arch.attrs.version);
         if (isNaN(this.version)) {
-            this.version = 6.1;
+            this.version = 7.0;
         }
     },
     set_tags_registry: function(tags_registry) {
@@ -1210,7 +1186,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
     set_widgets_registry: function(widgets_registry) {
         this.widgets_registry = widgets_registry;
     },
-    // Backward compatibility tools, current default version: v6.1
+    // Backward compatibility tools, current default version: v7
     process_version: function() {
         if (this.version < 7.0) {
             this.$form.find('form:first').wrapInner('<group col="4"/>');
@@ -1286,6 +1262,9 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
         var defs = [];
         _.each(this.to_replace, function(el) {
             defs.push(el[0].replace(el[1]));
+            if (el[1].children().length) {
+                el[0].$el.append(el[1].children());
+            }
         });
         this.to_replace = [];
         return $.when.apply($, defs);
@@ -1313,7 +1292,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
         var tagname = $tag[0].nodeName.toLowerCase();
         if (this.tags_registry.contains(tagname)) {
             this.tags_to_init.push($tag);
-            return $tag;
+            return (tagname === 'button') ? this.process_button($tag) : $tag;
         }
         var fn = self['process_' + tagname];
         if (fn) {
@@ -1330,6 +1309,13 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
             return $tag;
         }
     },
+    process_button: function ($button) {
+        var self = this;
+        $button.children().each(function() {
+            self.process($(this));
+        });
+        return $button;
+    },
     process_widget: function($widget) {
         this.widgets_to_init.push($widget);
         return $widget;
@@ -1829,7 +1815,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
         this.$el.addClass(this.node.attrs["class"] || "");
     },
     destroy: function() {
-        $.fn.tipsy.clear();
+        $.fn.tooltip('destroy');
         this._super.apply(this, arguments);
     },
     /**
@@ -1861,9 +1847,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
         widget = widget || this;
         trigger = trigger || this.$el;
         options = _.extend({
-                delayIn: 500,
-                delayOut: 0,
-                fade: true,
+                delay: { show: 500, hide: 0 },
                 title: function() {
                     var template = widget.template + '.tooltip';
                     if (!QWeb.has_template(template)) {
@@ -1874,12 +1858,12 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
                         widget: widget
                     });
                 },
-                gravity: $.fn.tipsy.autoBounds(50, 'nw'),
-                html: true,
-                opacity: 0.85,
-                trigger: 'hover'
             }, options || {});
-        $(trigger).tipsy(options);
+        //only show tooltip if we are in debug or if we have a help to show, otherwise it will display
+        //as empty
+        if (instance.session.debug || widget.node.attrs.help || (widget.field && widget.field.help)){
+            $(trigger).tooltip(options);
+        }
     },
     /**
      * Builds a new context usable for operations related to fields by merging
@@ -1915,6 +1899,8 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
     template: 'WidgetButton',
     init: function(field_manager, node) {
         node.attrs.type = node.attrs['data-button-type'];
+        this.is_stat_button = /\boe_stat_button\b/.test(node.attrs['class']);
+        this.icon_class = node.attrs.icon && "stat_button_icon fa " + node.attrs.icon + " fa-fw";
         this._super(field_manager, node);
         this.force_disabled = false;
         this.string = (this.node.attrs.string || '').replace(/_/g, '');
@@ -1950,26 +1936,23 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
         var exec_action = function() {
             if (self.node.attrs.confirm) {
                 var def = $.Deferred();
-                var dialog = instance.web.dialog($('<div/>').text(self.node.attrs.confirm), {
+                var dialog = new instance.web.Dialog(this, {
                     title: _t('Confirm'),
-                    modal: true,
                     buttons: [
                         {text: _t("Cancel"), click: function() {
-                                $(this).dialog("close");
+                                this.parents('.modal').modal('hide');
                             }
                         },
                         {text: _t("Ok"), click: function() {
                                 var self2 = this;
                                 self.on_confirmed().always(function() {
-                                    $(self2).dialog("close");
+                                    self2.parents('.modal').modal('hide');
                                 });
                             }
                         }
                     ],
-                    beforeClose: function() {
-                        def.resolve();
-                    },
-                });
+                }, $('<div/>').text(self.node.attrs.confirm)).open();
+                dialog.on("closing", null, function() {def.resolve();});
                 return def.promise();
             } else {
                 return self.on_confirmed();
@@ -1985,7 +1968,6 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
         var self = this;
 
         var context = this.build_context();
-
         return this.view.do_execute_action(
             _.extend({}, this.node.attrs, {context: context}),
             this.view.dataset, this.view.datarecord.id, function (reason) {
@@ -2118,8 +2100,8 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
             this.$el.find('.oe_field_translate').click(this.on_translate);
         }
         this.$label = this.view ? this.view.$el.find('label[for=' + this.id_for_label + ']') : $();
+        this.do_attach_tooltip(this, this.$label[0] || this.$el);
         if (instance.session.debug) {
-            this.do_attach_tooltip(this, this.$label[0] || this.$el);
             this.$label.off('dblclick').on('dblclick', function() {
                 console.log("Field '%s' of type '%s' in View: %o", self.name, (self.node.attrs.widget || self.field.type), self.view);
                 window.w = self;
@@ -2343,6 +2325,119 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we
     }
 });
 
+instance.web.form.KanbanSelection = instance.web.form.FieldChar.extend({
+    init: function (field_manager, node) {
+        this._super(field_manager, node);
+    },
+    prepare_dropdown_selection: function() {
+        var self = this;
+        var data = [];
+        var selection = self.field.selection || [];
+        _.map(selection, function(res) {
+            var value = {
+                'name': res[0],
+                'tooltip': res[1],
+                'state_name': res[1],
+            }
+            if (res[0] == 'normal') { value['state_class'] = 'oe_kanban_status'; }
+            else if (res[0] == 'done') { value['state_class'] = 'oe_kanban_status oe_kanban_status_green'; }
+            else { value['state_class'] = 'oe_kanban_status oe_kanban_status_red'; }
+            data.push(value);
+        });
+        return data;
+    },
+    render_value: function() {
+        var self = this;
+        this.record_id = this.view.datarecord.id;
+        this.states = this.prepare_dropdown_selection();;
+        this.$el.html(QWeb.render("KanbanSelection", {'widget': self}));
+        this.$el.find('li').on('click', this.set_kanban_selection.bind(this));
+    },
+    /* setting the value: in view mode, perform an asynchronous call and reload
+    the form view; in edit mode, use set_value to save the new value that will
+    be written when saving the record. */
+    set_kanban_selection: function (ev) {
+        var self = this;
+        var li = $(ev.target).closest('li');
+        if (li.length) {
+            var value = String(li.data('value'));
+            if (this.view.get('actual_mode') == 'view') {
+                var write_values = {}
+                write_values[self.name] = value;
+                return this.view.dataset._model.call(
+                    'write', [
+                        [self.record_id],
+                        write_values,
+                        self.view.dataset.get_context()
+                    ]).done(self.reload_record.bind(self));
+            }
+            else {
+                return this.set_value(value);
+            }
+        }
+    },
+    reload_record: function() {
+        this.view.reload();
+    },
+});
+
+instance.web.form.Priority = instance.web.form.FieldChar.extend({
+    init: function (field_manager, node) {
+        this._super(field_manager, node);
+    },
+    prepare_priority: function() {
+        var self = this;
+        var selection = this.field.selection || [];
+        var init_value = selection && selection[0][0] || 0;
+        var data = _.map(selection.slice(1), function(element, index) {
+            var value = {
+                'value': element[0],
+                'name': element[1],
+                'click_value': element[0],
+            }
+            if (index == 0 && self.get('value') == element[0]) {
+                value['click_value'] = init_value;
+            }
+            return value;
+        });
+        return data;
+    },
+    render_value: function() {
+        var self = this;
+        this.record_id = this.view.datarecord.id;
+        this.priorities = this.prepare_priority();
+        this.$el.html(QWeb.render("Priority", {'widget': this}));
+        this.$el.find('li').on('click', this.set_priority.bind(this));
+    },
+    /* setting the value: in view mode, perform an asynchronous call and reload
+    the form view; in edit mode, use set_value to save the new value that will
+    be written when saving the record. */
+    set_priority: function (ev) {
+        var self = this;
+        var li = $(ev.target).closest('li');
+        if (li.length) {
+            var value = String(li.data('value'));
+            if (this.view.get('actual_mode') == 'view') {
+                var write_values = {}
+                write_values[self.name] = value;
+                return this.view.dataset._model.call(
+                    'write', [
+                        [self.record_id],
+                        write_values,
+                        self.view.dataset.get_context()
+                    ]).done(self.reload_record.bind(self));
+            }
+            else {
+                return this.set_value(value);
+            }
+        }
+
+    },
+    reload_record: function() {
+        this.view.reload();
+    },
+});
+
 instance.web.form.FieldID = instance.web.form.FieldChar.extend({
     process_modifiers: function () {
         this._super();
@@ -2434,6 +2529,81 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
     }
 });
 
+instance.web.form.FieldCharDomain = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
+    init: function(field_manager, node) {
+        this._super.apply(this, arguments);
+    },
+    start: function() {
+        var self = this;
+        this._super.apply(this, arguments);
+        this.on("change:effective_readonly", this, function () {
+            this.display_field();
+        });
+        this.display_field();
+        return this._super();
+    },
+    set_value: function(value_) {
+        var self = this;
+        this.set('value', value_ || false);
+        this.display_field();
+     },
+    display_field: function() {
+        var self = this;
+        this.$el.html(instance.web.qweb.render("FieldCharDomain", {widget: this}));
+        if (this.get('value')) {
+            var model = this.options.model || this.field_manager.get_field_value(this.options.model_field);
+            var domain = instance.web.pyeval.eval('domain', this.get('value'));
+            var ds = new instance.web.DataSetStatic(self, model, self.build_context());
+            ds.call('search_count', [domain]).then(function (results) {
+                $('.oe_domain_count', self.$el).text(results + ' records selected');
+                if (self.get('effective_readonly')) {
+                    $('button span', self.$el).text(' See selection');
+                }
+                else {
+                    $('button span', self.$el).text(' Change selection');
+                }
+            });
+        } else {
+            $('.oe_domain_count', this.$el).text('0 record selected');
+            $('button span', this.$el).text(' Select records');
+        };
+        this.$('.select_records').on('click', self.on_click);
+    },
+    on_click: function(event) {
+        event.preventDefault();
+        var self = this;
+        var model = this.options.model || this.field_manager.get_field_value(this.options.model_field);
+        this.pop = new instance.web.form.SelectCreatePopup(this);
+        this.pop.select_element(
+            model, {
+                title: this.get('effective_readonly') ? 'Selected records' : 'Select records...',
+                readonly: this.get('effective_readonly'),
+                disable_multiple_selection: this.get('effective_readonly'),
+                no_create: this.get('effective_readonly'),
+            }, [], this.build_context());
+        this.pop.on("elements_selected", self, function(element_ids) {
+            if (this.pop.$('input.oe_list_record_selector').prop('checked')) {
+                var search_data = this.pop.searchview.build_search_data();
+                var domain_done = instance.web.pyeval.eval_domains_and_contexts({
+                    domains: search_data.domains,
+                    contexts: search_data.contexts,
+                    group_by_seq: search_data.groupbys || []
+                }).then(function (results) {
+                    return results.domain;
+                });
+            }
+            else {
+                var domain = [["id", "in", element_ids]];
+                var domain_done = $.Deferred().resolve(domain);
+            }
+            $.when(domain_done).then(function (domain) {
+                var domain = self.pop.dataset.domain.concat(domain || []);
+                self.set_value(domain);
+            });
+        });
+    },
+});
+
 instance.web.DateTimeWidget = instance.web.Widget.extend({
     template: "web.datepicker",
     jqueryui_object: 'datetimepicker',
@@ -2819,6 +2989,87 @@ instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({
     }
 });
 
+/**
+    The PercentPie field expect a float from 0 to 100.
+*/
+instance.web.form.FieldPercentPie = instance.web.form.AbstractField.extend({
+    template: 'FieldPercentPie',
+
+    render_value: function() {
+        var value = this.get('value'),
+            formatted_value = Math.round(value || 0) + '%',
+            svg = this.$('svg')[0];
+
+        svg.innerHTML = "";
+        nv.addGraph(function() {
+            var width = 42, height = 42;
+            var chart = nv.models.pieChart()
+                .width(width)
+                .height(height)
+                .margin({top: 0, right: 0, bottom: 0, left: 0})
+                .donut(true) 
+                .showLegend(false)
+                .showLabels(false)
+                .tooltips(false)
+                .color(['#7C7BAD','#DDD'])
+                .donutRatio(0.62);
+   
+            d3.select(svg)
+                .datum([{'x': 'value', 'y': value}, {'x': 'complement', 'y': 100 - value}])
+                .transition()
+                .call(chart)
+                .attr('style', 'width: ' + width + 'px; height:' + height + 'px;');
+
+            d3.select(svg)
+                .append("text")
+                .attr({x: width/2, y: height/2 + 3, 'text-anchor': 'middle'})
+                .style({"font-size": "10px", "font-weight": "bold"})
+                .text(formatted_value);
+
+            return chart;
+        });
+   
+    }
+});
+
+/**
+    The FieldBarChart expectsa list of values (indeed)
+*/
+instance.web.form.FieldBarChart = instance.web.form.AbstractField.extend({
+    template: 'FieldBarChart',
+
+    render_value: function() {
+        var value = JSON.parse(this.get('value'));
+        var svg = this.$('svg')[0];
+        svg.innerHTML = "";
+        nv.addGraph(function() {
+            var width = 34, height = 34;
+            var chart = nv.models.discreteBarChart()
+                .x(function (d) { return d.tooltip })
+                .y(function (d) { return d.value })
+                .width(width)
+                .height(height)
+                .margin({top: 0, right: 0, bottom: 0, left: 0})
+                .tooltips(false)
+                .showValues(false)
+                .transitionDuration(350)
+                .showXAxis(false)
+                .showYAxis(false);
+   
+            d3.select(svg)
+                .datum([{key: 'values', values: value}])
+                .transition()
+                .call(chart)
+                .attr('style', 'width: ' + (width + 4) + 'px; height: ' + (height + 8) + 'px;');
+
+            nv.utils.windowResize(chart.update);
+
+            return chart;
+        });
+   
+    }
+});
+
 
 instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
     template: 'FieldSelection',
@@ -3070,11 +3321,14 @@ instance.web.form.CompletionFieldMixin = {
         var self = this;
 
         var dataset = new instance.web.DataSet(this, this.field.relation, self.build_context());
-        var blacklist = this.get_search_blacklist();
         this.last_query = search_val;
+        var exclusion_domain = [], ids_blacklist = this.get_search_blacklist();
+        if (!_(ids_blacklist).isEmpty()) {
+            exclusion_domain.push(['id', 'not in', ids_blacklist]);
+        }
 
         return this.orderer.add(dataset.name_search(
-                search_val, new instance.web.CompoundDomain(self.build_domain(), [["id", "not in", blacklist]]),
+                search_val, new instance.web.CompoundDomain(self.build_domain(), exclusion_domain),
                 'ilike', this.limit + 1, self.build_context())).then(function(data) {
             self.last_search = data;
             // possible selections for the m2o
@@ -3115,7 +3369,7 @@ instance.web.form.CompletionFieldMixin = {
                 });
             }
             // create...
-            if (!(self.options && self.options.no_create)){
+            if (!(self.options && (self.options.no_create || self.options.no_create_edit))){
                 values.push({
                     label: _t("Create and Edit..."),
                     action: function() {
@@ -3124,6 +3378,12 @@ instance.web.form.CompletionFieldMixin = {
                     classname: 'oe_m2o_dropdown_option'
                 });
             }
+            else if (values.length == 0)
+               values.push({
+                       label: _t("No results to show..."),
+                       action: function() {},
+                       classname: 'oe_m2o_dropdown_option'
+               });
 
             return values;
         });
@@ -3186,18 +3446,26 @@ instance.web.form.CompletionFieldMixin = {
 instance.web.form.M2ODialog = instance.web.Dialog.extend({
     template: "M2ODialog",
     init: function(parent) {
+        this.name = parent.string;
         this._super(parent, {
-            title: _.str.sprintf(_t("Add %s"), parent.string),
-            width: 312,
+            title: _.str.sprintf(_t("Create a %s"), parent.string),
+            size: 'medium',
         });
     },
     start: function() {
         var self = this;
+        var text = _.str.sprintf(_t("You are creating a new %s, are you sure it does not exist yet?"), self.name);
+        this.$("p").text( text );
         this.$buttons.html(QWeb.render("M2ODialog.buttons"));
         this.$("input").val(this.getParent().last_query);
-        this.$buttons.find(".oe_form_m2o_qc_button").click(function(){
-            self.getParent()._quick_create(self.$("input").val());
-            self.destroy();
+        this.$buttons.find(".oe_form_m2o_qc_button").click(function(e){
+            if (self.$("input").val() != ''){
+                self.getParent()._quick_create(self.$("input").val());
+                self.destroy();
+            } else{
+                e.preventDefault();
+                self.$("input").focus();
+            }
         });
         this.$buttons.find(".oe_form_m2o_sc_button").click(function(){
             self.getParent()._search_create_popup("form", undefined, self.getParent()._create_context(self.$("input").val()));
@@ -3257,7 +3525,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
             delete this.$drop_down;
         }
         if (this.$input) {
-            this.$input.closest(".ui-dialog .ui-dialog-content").off('scroll');
+            this.$input.closest(".modal .modal-content").off('scroll');
             this.$input.off('keyup blur autocompleteclose autocompleteopen ' +
                             'focus focusout change keydown');
             delete this.$input;
@@ -3300,20 +3568,25 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
                 return;
             }
             var pop = new instance.web.form.FormOpenPopup(self);
-            pop.show_element(
-                self.field.relation,
-                self.get("value"),
-                self.build_context(),
-                {
-                    title: _t("Open: ") + self.string
-                }
-            );
-            pop.on('write_completed', self, function(){
-                self.display_value = {};
-                self.display_value_backup = {};
-                self.render_value();
-                self.focus();
-                self.trigger('changed_value');
+            var context = self.build_context().eval();
+            var model_obj = new instance.web.Model(self.field.relation);
+            model_obj.call('get_formview_id', [self.get("value"), context]).then(function(view_id){
+                pop.show_element(
+                    self.field.relation,
+                    self.get("value"),
+                    self.build_context(),
+                    {
+                        title: _t("Open: ") + self.string,
+                        view_id: view_id
+                    }
+                );
+                pop.on('write_completed', self, function(){
+                    self.display_value = {};
+                    self.display_value_backup = {};
+                    self.render_value();
+                    self.focus();
+                    self.trigger('changed_value');
+                });
             });
         });
 
@@ -3350,7 +3623,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
                 self.$input.autocomplete("close");
             }
         }, 50);
-        this.$input.closest(".ui-dialog .ui-dialog-content").on('scroll', this, close_autocomplete);
+        this.$input.closest(".modal .modal-content").on('scroll', this, close_autocomplete);
 
         self.ed_def = $.Deferred();
         self.uned_def = $.Deferred();
@@ -3405,10 +3678,10 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
             focusout: anyoneLoosesFocus,
             focus: function () { self.trigger('focused'); },
             autocompleteopen: function () { ignore_blur = true; },
-            autocompleteclose: function () { ignore_blur = false; },
+            autocompleteclose: function () { setTimeout(function() {ignore_blur = false;},0); },
             blur: function () {
                 // autocomplete open
-                if (ignore_blur) { return; }
+                if (ignore_blur) { $(this).focus(); return; }
                 if (_(self.getChildren()).any(function (child) {
                     return child instanceof instance.web.form.AbstractFormPopup;
                 })) { return; }
@@ -3448,6 +3721,8 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
             minLength: 0,
             delay: 250
         });
+        // set position for list of suggestions box
+        this.$input.autocomplete( "option", "position", { my : "left top", at: "left bottom" } );
         this.$input.autocomplete("widget").openerpClass();
         // used to correct a bug when selecting an element by pushing 'enter' in an editable list
         this.$input.keyup(function(e) {
@@ -3510,13 +3785,10 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
                  .html(link);
             if (! this.options.no_open)
                 $link.click(function () {
-                    self.do_action({
-                        type: 'ir.actions.act_window',
-                        res_model: self.field.relation,
-                        res_id: self.get("value"),
-                        views: [[false, 'form']],
-                        target: 'current',
-                        context: self.build_context().eval(),
+                    var context = self.build_context().eval();
+                    var model_obj = new instance.web.Model(self.field.relation);
+                    model_obj.call('get_formview_action', [self.get("value"), context]).then(function(action){
+                        self.do_action(action);
                     });
                     return false;
                  });
@@ -3880,28 +4152,41 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
     reload_current_view: function() {
         var self = this;
         self.is_loaded = self.is_loaded.then(function() {
-            var active_view = self.viewmanager.active_view;
-            var view = self.viewmanager.views[active_view].controller;
-            if(active_view === "list") {
-                return view.reload_content();
-            } else if (active_view === "form") {
+            var view = self.get_active_view();
+            if (view.type === "list") {
+                return view.controller.reload_content();
+            } else if (view.type === "form") {
                 if (self.dataset.index === null && self.dataset.ids.length >= 1) {
                     self.dataset.index = 0;
                 }
                 var act = function() {
-                    return view.do_show();
+                    return view.controller.do_show();
                 };
                 self.form_last_update = self.form_last_update.then(act, act);
                 return self.form_last_update;
-            } else if (view.do_search) {
-                return view.do_search(self.build_domain(), self.dataset.get_context(), []);
+            } else if (view.controller.do_search) {
+                return view.controller.do_search(self.build_domain(), self.dataset.get_context(), []);
             }
         }, undefined);
         return self.is_loaded;
     },
+    get_active_view: function () {
+        /**
+         * Returns the current active view if any.
+         */
+        if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
+            this.viewmanager.views[this.viewmanager.active_view] &&
+            this.viewmanager.views[this.viewmanager.active_view].controller) {
+            return {
+                type: this.viewmanager.active_view,
+                controller: this.viewmanager.views[this.viewmanager.active_view].controller
+            };
+        }
+    },
     set_value: function(value_) {
         value_ = value_ || [];
         var self = this;
+        var view = this.get_active_view();
         this.dataset.reset_ids([]);
         var ids;
         if(value_.length >= 1 && value_[0] instanceof Array) {
@@ -3986,33 +4271,32 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
         return this.save_any_view();
     },
     save_any_view: function() {
-        if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
-            this.viewmanager.views[this.viewmanager.active_view] &&
-            this.viewmanager.views[this.viewmanager.active_view].controller) {
-            var view = this.viewmanager.views[this.viewmanager.active_view].controller;
+        var view = this.get_active_view();
+        if (view) {
             if (this.viewmanager.active_view === "form") {
-                if (view.is_initialized.state() !== 'resolved') {
+                if (view.controller.is_initialized.state() !== 'resolved') {
                     return $.when(false);
                 }
-                return $.when(view.save());
+                return $.when(view.controller.save());
             } else if (this.viewmanager.active_view === "list") {
-                return $.when(view.ensure_saved());
+                return $.when(view.controller.ensure_saved());
             }
         }
         return $.when(false);
     },
     is_syntax_valid: function() {
-        if (! this.viewmanager || ! this.viewmanager.views[this.viewmanager.active_view])
+        var view = this.get_active_view();
+        if (!view){
             return true;
-        var view = this.viewmanager.views[this.viewmanager.active_view].controller;
+        }
         switch (this.viewmanager.active_view) {
         case 'form':
-            return _(view.fields).chain()
+            return _(view.controller.fields).chain()
                 .invoke('is_valid')
                 .all(_.identity)
                 .value();
         case 'list':
-            return view.is_valid();
+            return view.controller.is_valid();
         }
         return true;
     },
@@ -4402,10 +4686,21 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
                 }
             });
     },
+    // WARNING: duplicated in 4 other M2M widgets
     set_value: function(value_) {
         value_ = value_ || [];
         if (value_.length >= 1 && value_[0] instanceof Array) {
-            value_ = value_[0][2];
+            // value_ is a list of m2m commands. We only process
+            // LINK_TO and REPLACE_WITH in this context
+            var val = [];
+            _.each(value_, function (command) {
+                if (command[0] === commands.LINK_TO) {
+                    val.push(command[1]);                   // (4, id[, _])
+                } else if (command[0] === commands.REPLACE_WITH) {
+                    val = command[2];                       // (6, _, ids)
+                }
+            });
+            value_ = val;
         }
         this._super(value_);
     },
@@ -4536,10 +4831,21 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend(instan
         this.list_view.destroy();
         this.list_view = undefined;
     },
+    // WARNING: duplicated in 4 other M2M widgets
     set_value: function(value_) {
         value_ = value_ || [];
         if (value_.length >= 1 && value_[0] instanceof Array) {
-            value_ = value_[0][2];
+            // value_ is a list of m2m commands. We only process
+            // LINK_TO and REPLACE_WITH in this context
+            var val = [];
+            _.each(value_, function (command) {
+                if (command[0] === commands.LINK_TO) {
+                    val.push(command[1]);                   // (4, id[, _])
+                } else if (command[0] === commands.REPLACE_WITH) {
+                    val = command[2];                       // (6, _, ids)
+                }
+            });
+            value_ = val;
         }
         this._super(value_);
     },
@@ -4666,10 +4972,21 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
             });
         });
     },
+    // WARNING: duplicated in 4 other M2M widgets
     set_value: function(value_) {
         value_ = value_ || [];
         if (value_.length >= 1 && value_[0] instanceof Array) {
-            value_ = value_[0][2];
+            // value_ is a list of m2m commands. We only process
+            // LINK_TO and REPLACE_WITH in this context
+            var val = [];
+            _.each(value_, function (command) {
+                if (command[0] === commands.LINK_TO) {
+                    val.push(command[1]);                   // (4, id[, _])
+                } else if (command[0] === commands.REPLACE_WITH) {
+                    val = command[2];                       // (6, _, ids)
+                }
+            });
+            value_ = val;
         }
         this._super(value_);
     },
@@ -4888,13 +5205,12 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
         var self = this;
         this.renderElement();
         var dialog = new instance.web.Dialog(this, {
-            min_width: '800px',
             dialogClass: 'oe_act_window',
-            close: function() {
-                self.check_exit(true);
-            },
             title: this.options.title || "",
         }, this.$el).open();
+        dialog.on('closing', this, function (e){
+            self.check_exit(true);
+        });
         this.$buttonpane = dialog.$buttons;
         this.start();
     },
@@ -4961,8 +5277,8 @@ instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
     },
     destroy: function () {
         this.trigger('closed');
-        if (this.$el.is(":data(dialog)")) {
-            this.$el.dialog('close');
+        if (this.$el.is(":data(bs.modal)")) {
+            this.$el.parents('.modal').modal('hide');
         }
         this._super();
     },
@@ -5032,8 +5348,12 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
         if (this.searchview) {
             this.searchview.destroy();
         }
+        if (this.searchview_drawer) {
+            this.searchview_drawer.destroy();
+        }
         this.searchview = new instance.web.SearchView(this,
                 this.dataset, false,  search_defaults);
+        this.searchview_drawer = new instance.web.SearchViewDrawer(this, this.searchview);
         this.searchview.on('search_data', self, function(domains, contexts, groupbys) {
             if (self.initial_ids) {
                 self.do_search(domains.concat([[["id", "in", self.initial_ids]], self.domain]),
@@ -5079,7 +5399,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
                 });
             });
         });
-        this.searchview.appendTo($(".oe_popup_search", self.$el));
+        this.searchview.appendTo(this.$(".oe_popup_search"));
     },
     do_search: function(domains, contexts, groupbys) {
         var self = this;
@@ -5227,9 +5547,13 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
         this._super.apply(this, arguments);
     },
     initialize_content: function() {
+        var self= this;
         this.$el.find('input.oe_form_binary_file').change(this.on_file_change);
         this.$el.find('button.oe_form_binary_file_save').click(this.on_save_as);
         this.$el.find('.oe_form_binary_file_clear').click(this.on_clear);
+        this.$el.find('.oe_form_binary_file_edit').click(function(event){
+            self.$el.find('input.oe_form_binary_file').click();
+        });
     },
     on_file_change: function(e) {
         var self = this;
@@ -5397,8 +5721,6 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
                 return;
             $img.css("max-width", "" + self.options.size[0] + "px");
             $img.css("max-height", "" + self.options.size[1] + "px");
-            $img.css("margin-left", "" + (self.options.size[0] - $img.width()) / 2 + "px");
-            $img.css("margin-top", "" + (self.options.size[1] - $img.height()) / 2 + "px");
         });
         $img.on('error', function() {
             $img.attr('src', self.placeholder);
@@ -5438,7 +5760,7 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
  * Options on attribute ; "blockui" {Boolean} block the UI or not
  * during the file is uploading
  */
-instance.web.form.FieldMany2ManyBinaryMultiFiles = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
+instance.web.form.FieldMany2ManyBinaryMultiFiles = instance.web.form.AbstractField.extend({
     template: "FieldBinaryFileUploader",
     init: function(field_manager, node) {
         this._super(field_manager, node);
@@ -5456,14 +5778,22 @@ instance.web.form.FieldMany2ManyBinaryMultiFiles = instance.web.form.AbstractFie
     start: function() {
         this._super(this);
         this.$el.on('change', 'input.oe_form_binary_file', this.on_file_change );
-        this.on("change:effective_readonly", this, function () {
-            this.render_value();
-        });
     },
+    // WARNING: duplicated in 4 other M2M widgets
     set_value: function(value_) {
         value_ = value_ || [];
         if (value_.length >= 1 && value_[0] instanceof Array) {
-            value_ = value_[0][2];
+            // value_ is a list of m2m commands. We only process
+            // LINK_TO and REPLACE_WITH in this context
+            var val = [];
+            _.each(value_, function (command) {
+                if (command[0] === commands.LINK_TO) {
+                    val.push(command[1]);                   // (4, id[, _])
+                } else if (command[0] === commands.REPLACE_WITH) {
+                    val = command[2];                       // (6, _, ids)
+                }
+            });
+            value_ = val;
         }
         this._super(value_);
     },
@@ -5495,7 +5825,6 @@ instance.web.form.FieldMany2ManyBinaryMultiFiles = instance.web.form.AbstractFie
     },
     render_value: function () {
         var self = this;
-        this.$('.oe_add').css('visibility', this.get('effective_readonly') ? 'hidden': '');
         this.read_name_values().then(function (ids) {
             var render = $(instance.web.qweb.render('FieldBinaryFileUploader.files', {'widget': self, 'values': ids}));
             render.on('click', '.oe_delete', _.bind(self.on_file_delete, self));
@@ -5825,13 +6154,24 @@ instance.web.form.FieldMany2ManyCheckBoxes = instance.web.form.AbstractField.ext
         if (! _.isEqual(new_value, this.get("value")))
             this.internal_set_value(new_value);
     },
-    set_value: function(value) {
-        value = value || [];
-        if (value.length >= 1 && value[0] instanceof Array) {
-            value = value[0][2];
+    // WARNING: (mostly) duplicated in 4 other M2M widgets
+    set_value: function(value_) {
+        value_ = value_ || [];
+        if (value_.length >= 1 && value_[0] instanceof Array) {
+            // value_ is a list of m2m commands. We only process
+            // LINK_TO and REPLACE_WITH in this context
+            var val = [];
+            _.each(value_, function (command) {
+                if (command[0] === commands.LINK_TO) {
+                    val.push(command[1]);                   // (4, id[, _])
+                } else if (command[0] === commands.REPLACE_WITH) {
+                    val = command[2];                       // (6, _, ids)
+                }
+            });
+            value_ = val;
         }
         var formatted = {};
-        _.each(value, function(el) {
+        _.each(value_, function(el) {
             formatted[JSON.stringify(el)] = true;
         });
         this._super(formatted);
@@ -5901,6 +6241,36 @@ instance.web.form.X2ManyCounter = instance.web.form.AbstractField.extend(instanc
 });
 
 /**
+    This widget is intended to be used on stat button numeric fields.  It will display
+    the value   many2many and one2many. It is a read-only field that will 
+    display a simple string "<value of field> <label of the field>"
+*/
+instance.web.form.StatInfo = instance.web.form.AbstractField.extend({
+    is_field_number: true,
+    init: function() {
+        this._super.apply(this, arguments);
+        this.internal_set_value(0);
+    },
+    set_value: function(value_) {
+        if (value_ === false || value_ === undefined) {
+            value_ = 0;
+        }
+        this._super.apply(this, [value_]);
+    },
+    render_value: function() {
+        var options = {
+            value: this.get("value") || 0,
+        };
+        if (! this.node.attrs.nolabel) {
+            options.text = this.string
+        }
+        this.$el.html(QWeb.render("StatInfo", options));
+    },
+
+});
+
+
+/**
  * Registry of form fields, called by :js:`instance.web.FormView`.
  *
  * All referenced classes must implement FieldInterface. Those represent the classes whose instances
@@ -5913,6 +6283,7 @@ instance.web.form.widgets = new instance.web.Registry({
     'url' : 'instance.web.form.FieldUrl',
     'text' : 'instance.web.form.FieldText',
     'html' : 'instance.web.form.FieldTextHtml',
+    'char_domain': 'instance.web.form.FieldCharDomain',
     'date' : 'instance.web.form.FieldDate',
     'datetime' : 'instance.web.form.FieldDatetime',
     'selection' : 'instance.web.form.FieldSelection',
@@ -5927,6 +6298,8 @@ instance.web.form.widgets = new instance.web.Registry({
     'reference' : 'instance.web.form.FieldReference',
     'boolean' : 'instance.web.form.FieldBoolean',
     'float' : 'instance.web.form.FieldFloat',
+    'percentpie': 'instance.web.form.FieldPercentPie',
+    'barchart': 'instance.web.form.FieldBarChart',
     'integer': 'instance.web.form.FieldFloat',
     'float_time': 'instance.web.form.FieldFloat',
     'progressbar': 'instance.web.form.FieldProgressBar',
@@ -5937,6 +6310,9 @@ instance.web.form.widgets = new instance.web.Registry({
     'monetary': 'instance.web.form.FieldMonetary',
     'many2many_checkboxes': 'instance.web.form.FieldMany2ManyCheckBoxes',
     'x2many_counter': 'instance.web.form.X2ManyCounter',
+    'priority':'instance.web.form.Priority',
+    'kanban_state_selection':'instance.web.form.KanbanSelection',
+    'statinfo': 'instance.web.form.StatInfo',
 });
 
 /**