Gives new values for the fields contained in the view. The new values could not be setted
right after the call to this method. Setting new values can trigger on_changes.
- @param (dict) values A dictonnary with key = field name and value = new value.
- @return (Deferred) Is resolved after all the values are setted.
+ @param {Object} values A dictonary with key = field name and value = new value.
+ @return {$.Deferred} Is resolved after all the values are setted.
*/
set_values: function(values) {},
/**
Computes an OpenERP domain.
- @param (list) expression An OpenERP domain.
- @return (boolean) The computed value of the domain.
+ @param {Array} expression An OpenERP domain.
+ @return {boolean} The computed value of the domain.
*/
compute_domain: function(expression) {},
/**
the field are only supposed to use this context to evualuate their own, they should not
extend it.
- @return (CompoundContext) An OpenERP context.
+ @return {CompoundContext} An OpenERP context.
*/
build_eval_context: function() {},
};
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;
});
this.is_initialized = $.Deferred();
this.mutating_mutex = new $.Mutex();
- this.on_change_list = [];
this.save_list = [];
this.render_value_defs = [];
this.reload_mutex = new $.Mutex();
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);
this.dataset.ids.push(state.id);
}
this.dataset.select_id(state.id);
- this.do_show({ reload: warm });
+ this.do_show();
}
},
/**
});
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();
$(".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)) {
}
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) {
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();
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() {
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];
field.set_value(value_);
field._inhibit_on_change_flag = false;
field._dirty_flag = true;
- if (!_.contains(exclude, field.name)) {
- this.do_onchange(field, exclude);
- }
}
}
}
* 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");
},
/**
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(){
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');
});
},
};
})
.value();
-
var d = new instance.web.Dialog(this, {
title: _t("Set Default"),
args: {
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) {
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"/>');
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);
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) {
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;
this.$el.addClass(this.node.attrs["class"] || "");
},
destroy: function() {
- $.fn.tipsy.clear();
+ $.fn.tooltip('destroy');
this._super.apply(this, arguments);
},
/**
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)) {
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
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, '');
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();
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) {
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;
}
});
+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();
}
});
+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',
}
});
+/**
+ 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',
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
});
}
// 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() {
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;
});
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()));
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;
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');
+ });
});
});
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();
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; }
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) {
.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;
});
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) {
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;
},
}
});
},
+ // 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_);
},
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_);
},
});
});
},
+ // 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_);
},
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();
},
},
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();
},
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]),
});
});
});
- this.searchview.appendTo($(".oe_popup_search", self.$el));
+ this.searchview.appendTo(this.$(".oe_popup_search"));
},
do_search: function(domains, contexts, groupbys) {
var self = this;
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;
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);
initialize_content: function() {
this.$el.on('change', 'input.oe_form_binary_file', this.on_file_change );
},
+ // 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_);
},
},
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));
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);
});
/**
+ 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
'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',
'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',
'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',
});
/**