summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js')
-rw-r--r--addons/web/static/src/js/chrome/view_manager.js6
-rw-r--r--addons/web/static/src/js/fields/abstract_field.js9
-rw-r--r--addons/web/static/src/js/fields/field_utils.js14
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js32
-rw-r--r--addons/web/static/src/js/views/abstract_model.js25
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js50
-rw-r--r--addons/web/static/src/js/views/calendar/calendar_model.js40
-rw-r--r--addons/web/static/src/js/views/calendar/calendar_renderer.js2
-rw-r--r--addons/web/static/src/js/views/graph/graph_model.js9
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_controller.js5
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_quick_create.js30
-rw-r--r--addons/web/static/src/js/views/kanban/kanban_renderer.js10
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js4
-rw-r--r--addons/web/static/src/js/views/pivot/pivot_model.js5
14 files changed, 148 insertions, 93 deletions
diff --git a/addons/web/static/src/js/chrome/view_manager.js b/addons/web/static/src/js/chrome/view_manager.js
index 9b431d3e57a..a4a1d65f7b6 100644
--- a/addons/web/static/src/js/chrome/view_manager.js
+++ b/addons/web/static/src/js/chrome/view_manager.js
@@ -53,7 +53,8 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
*/
on_attach_callback: function() {
this.is_in_DOM = true;
- if (this.active_view && this.active_view.controller.on_attach_callback) {
+ var controller = this.active_view && this.active_view.controller;
+ if (controller && this.active_view.controller.on_attach_callback) {
this.active_view.controller.on_attach_callback();
}
},
@@ -579,10 +580,11 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
// Wrong default_* and search_default_* values will no give the expected result
// Wrong group_by values will simply fail and forbid rendering of the destination view
var ncontext = new Context(
- _.object(_.reject(_.pairs(_.extend({}, action_data.context, self.env.context)), function(pair) {
+ _.object(_.reject(_.pairs(self.env.context), function(pair) {
return pair[0].match('^(?:(?:default_|search_default_|show_).+|.+_view_ref|group_by|group_by_no_leaf|active_id|active_ids)$') !== null;
}))
);
+ ncontext.add(action_data.context || {});
ncontext.add({active_model: self.env.modelName});
if (res_ids) {
ncontext.add({
diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js
index 4ae4eec9c05..fa2f37a123f 100644
--- a/addons/web/static/src/js/fields/abstract_field.js
+++ b/addons/web/static/src/js/fields/abstract_field.js
@@ -65,9 +65,12 @@ var AbstractField = Widget.extend({
* @constructor
* @param {Widget} parent
* @param {string} name The field name defined in the model
- * @param {Object} record A record object (result of the get method of a basic model)
+ * @param {Object} record A record object (result of the get method of
+ * a basic model)
* @param {Object} [options]
* @param {string} [options.mode=readonly] should be 'readonly' or 'edit'
+ * @param {string} [options.standalone=false] this flag indicates that the
+ * field does not have a controller to delegate to
*/
init: function (parent, name, record, options) {
this._super(parent);
@@ -157,6 +160,10 @@ var AbstractField = Widget.extend({
// calls to the format (resp. parse) function.
this.formatOptions = {};
this.parseOptions = {};
+
+ // standalone is a flag indicating that the field does not have a
+ // controller to delegate actions to
+ this.standalone = options.standalone;
},
/**
* When a field widget is appended to the DOM, its start method is called,
diff --git a/addons/web/static/src/js/fields/field_utils.js b/addons/web/static/src/js/fields/field_utils.js
index ac4c06454ea..0a44a52e315 100644
--- a/addons/web/static/src/js/fields/field_utils.js
+++ b/addons/web/static/src/js/fields/field_utils.js
@@ -29,12 +29,20 @@ var _t = core._t;
/**
* @todo Really? it returns a jqueryElement... We should try to move this to a
* module with dom helpers functions, such as web.dom, maybe. And replace this
- * with a function that returns a string
+ * with a function that returns a string so we can get rid of the forceString.
*
* @param {boolean} value
- * @returns {jQueryElement}
+ * @param {Object} [field]
+ * a description of the field (note: this parameter is ignored)
+ * @param {Object} [options] additional options
+ * @param {boolean} [options.forceString=false] if true, returns a string
+* representation of the boolean rather than a jQueryElement
+ * @returns {jQueryElement | string}
*/
-function formatBoolean(value) {
+function formatBoolean(value, field, options) {
+ if (options && options.forceString) {
+ return value ? _t('True') : _t('False');
+ }
var $input = $('<input/>', {
type: 'checkbox',
}).prop({
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index 14512c02912..7522bb3bc37 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -223,21 +223,25 @@ var FieldMany2One = AbstractField.extend({
_quickCreate: function (name) {
var self = this;
var slowCreate = this._searchCreatePopup.bind(this, "form", false, this._createContext(name));
+ var action = function () {
+ return self._rpc({
+ model: self.field.relation,
+ method: 'name_create',
+ args: [name],
+ context: self.record.getContext(self.recordParams),
+ }).then(function (result) {
+ if (self.mode === "edit") {
+ self.reinitialize({id: result[0], display_name: result[1]});
+ }
+ }, slowCreate);
+ };
+
if (this.nodeOptions.quick_create) {
- this.trigger_up('mutexify', {
- action: function () {
- return self._rpc({
- model: self.field.relation,
- method: 'name_create',
- args: [name],
- context: self.record.getContext(self.recordParams),
- }).then(function (result) {
- if (self.mode === "edit") {
- self.reinitialize({id: result[0], display_name: result[1]});
- }
- }, slowCreate);
- },
- });
+ if (this.standalone) {
+ action();
+ } else {
+ this.trigger_up('mutexify', {action: action});
+ }
} else {
slowCreate();
}
diff --git a/addons/web/static/src/js/views/abstract_model.js b/addons/web/static/src/js/views/abstract_model.js
index 27d13a12b06..4ea6b217e65 100644
--- a/addons/web/static/src/js/views/abstract_model.js
+++ b/addons/web/static/src/js/views/abstract_model.js
@@ -15,6 +15,7 @@ odoo.define('web.AbstractModel', function (require) {
*/
var Class = require('web.Class');
+var fieldUtils = require('web.field_utils');
var mixins = require('web.mixins');
var ServicesMixin = require('web.ServicesMixin');
@@ -63,6 +64,30 @@ var AbstractModel = Class.extend(mixins.EventDispatcherMixin, ServicesMixin, {
reload: function (params) {
return $.when();
},
+ /**
+ * Processes date(time) and selection field values sent by the server.
+ * Converts data(time) values to moment instances.
+ * Converts false values of selection fields to 0 if 0 is a valid key,
+ * because the server doesn't make a distinction between false and 0, and
+ * always sends false when value is 0.
+ *
+ * @param {Object} field the field description
+ * @param {*} value
+ * @returns {*} the processed value
+ */
+ _parseServerValue: function (field, value) {
+ if (field.type === 'date' || field.type === 'datetime') {
+ // process date(time): convert into a moment instance
+ value = fieldUtils.parse[field.type](value, field, {isUTC: true});
+ } else if (field.type === 'selection' && value === false) {
+ // process selection: convert false to 0, if 0 is a valid key
+ var hasKey0 = _.find(field.selection, function (option) {
+ return option[0] === 0;
+ });
+ value = hasKey0 ? 0 : value;
+ }
+ return value;
+ },
});
return AbstractModel;
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index 5fb77204ec7..75a0ad02971 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -84,10 +84,9 @@ odoo.define('web.BasicModel', function (require) {
var AbstractModel = require('web.AbstractModel');
var concurrency = require('web.concurrency');
-var core = require('web.core');
var Context = require('web.Context');
+var core = require('web.core');
var Domain = require('web.Domain');
-var fieldUtils = require('web.field_utils');
var session = require('web.session');
var _t = core._t;
@@ -2370,8 +2369,8 @@ var BasicModel = AbstractModel.extend({
* if given, this field's context is added to the context, instead of
* the element's context (except if options.full is true)
* @param {boolean} [options.full=false]
- * if true and fieldName given in options, the element's context
- * is added to the context
+ * if true or nor fieldName or additionalContext given in options,
+ * the element's context is added to the context
* @returns {Object} the evaluated context
*/
_getContext: function (element, options) {
@@ -2379,7 +2378,7 @@ var BasicModel = AbstractModel.extend({
var context = new Context(session.user_context);
context.set_eval_context(this._getEvalContext(element));
- if (options.full || !options.fieldName) {
+ if (options.full || !(options.fieldName || options.additionalContext)) {
context.add(element.context);
}
if (options.fieldName) {
@@ -2656,8 +2655,14 @@ var BasicModel = AbstractModel.extend({
var type = params.type || ('domain' in params && 'list') || 'record';
var res_id, value;
var res_ids = params.res_ids || [];
+ var data = params.data || (type === 'record' ? {} : []);
if (type === 'record') {
- res_id = params.res_id || (params.data && params.data.id) || _.uniqueId('virtual_');
+ res_id = params.res_id || (params.data && params.data.id);
+ if (res_id) {
+ data.id = res_id;
+ } else {
+ res_id = _.uniqueId('virtual_');
+ }
} else {
var isValueArray = params.value instanceof Array;
res_id = isValueArray ? params.value[0] : undefined;
@@ -2675,7 +2680,7 @@ var BasicModel = AbstractModel.extend({
aggregateValues: params.aggregateValues || {},
context: params.context || {},
count: params.count || res_ids.length,
- data: params.data || (type === 'record' ? {} : []),
+ data: data,
domain: params.domain || [],
fields: fields,
fieldsInfo: params.fieldsInfo,
@@ -2941,30 +2946,6 @@ var BasicModel = AbstractModel.extend({
});
},
/**
- * Processes date(time) and selection field values sent by the server.
- * Converts data(time) values to moment instances.
- * Converts false values of selection fields to 0 if 0 is a valid key,
- * because the server doesn't make a distinction between false and 0, and
- * always sends false when value is 0.
- *
- * @param {Object} field the field description
- * @param {*} value
- * @returns {*} the processed value
- */
- _parseServerValue: function (field, value) {
- if (field.type === 'date' || field.type === 'datetime') {
- // process date(time): convert into a moment instance
- value = fieldUtils.parse[field.type](value, field, {isUTC: true});
- } else if (field.type === 'selection' && value === false) {
- // process selection: convert false to 0, if 0 is a valid key
- var hasKey0 = _.find(field.selection, function (option) {
- return option[0] === 0;
- });
- value = hasKey0 ? 0 : value;
- }
- return value;
- },
- /**
* This method is quite important: it is supposed to perform the /onchange
* rpc and apply the result.
*
@@ -3087,7 +3068,8 @@ var BasicModel = AbstractModel.extend({
lazy: true,
})
.then(function (groups) {
- var rawGroupBy = list.groupedBy[0].split(':')[0];
+ var groupByField = list.groupedBy[0];
+ var rawGroupBy = groupByField.split(':')[0];
var previousGroups = _.map(list.data, function (groupID) {
return self.localData[groupID];
});
@@ -3098,13 +3080,13 @@ var BasicModel = AbstractModel.extend({
_.each(groups, function (group) {
var aggregateValues = {};
_.each(group, function (value, key) {
- if (_.contains(fields, key) && key !== list.groupedBy[0]) {
+ if (_.contains(fields, key) && key !== groupByField) {
aggregateValues[key] = value;
}
});
// When a view is grouped, we need to display the name of each group in
// the 'title'.
- var value = group[rawGroupBy];
+ var value = group[groupByField];
if (list.fields[rawGroupBy].type === "selection") {
var choice = _.find(list.fields[rawGroupBy].selection, function (c) {
return c[0] === value;
diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js
index ded7938605f..73868a93fdb 100644
--- a/addons/web/static/src/js/views/calendar/calendar_model.js
+++ b/addons/web/static/src/js/views/calendar/calendar_model.js
@@ -406,12 +406,13 @@ return AbstractModel.extend({
});
}
return eventsDef.then(function (events) {
- self.data.data = _.map(events, self._recordToCalendarEvent.bind(self));
- return $.when(
- self._loadColors(self.data, self.data.data),
- self._loadRecordsToFilters(self.data, self.data.data)
- );
- });
+ self._parseServerData(events);
+ self.data.data = _.map(events, self._recordToCalendarEvent.bind(self));
+ return $.when(
+ self._loadColors(self.data, self.data.data),
+ self._loadRecordsToFilters(self.data, self.data.data)
+ );
+ });
});
},
/**
@@ -566,12 +567,25 @@ return AbstractModel.extend({
if (filter.filters.length && (filter.filters[0].avatar_model in to_read)) {
_.each(filter.filters, function (f) {
f.label = to_read[f.avatar_model][f.value];
- });
+ });
}
});
});
},
/**
+ * parse the server values to javascript framwork
+ *
+ * @param {Object} data the server data to parse
+ */
+ _parseServerData: function (data) {
+ var self = this;
+ _.each(data, function(event) {
+ _.each(self.fieldNames, function (fieldName) {
+ event[fieldName] = self._parseServerValue(self.fields[fieldName], event[fieldName]);
+ });
+ });
+ },
+ /**
* Transform OpenERP event object to fullcalendar event object
*/
_recordToCalendarEvent: function (evt) {
@@ -583,11 +597,11 @@ return AbstractModel.extend({
attendees = [];
if (!all_day) {
- date_start = fieldUtils.parse.datetime(evt[this.mapping.date_start]);
- date_stop = this.mapping.date_stop ? fieldUtils.parse.datetime(evt[this.mapping.date_stop]) : null;
+ date_start = evt[this.mapping.date_start].clone();
+ date_stop = this.mapping.date_stop ? evt[this.mapping.date_stop].clone() : null;
} else {
- date_start = fieldUtils.parse.datetime(evt[this.mapping.date_start].split(' ')[0]);
- date_stop = this.mapping.date_stop ? fieldUtils.parse.datetime(evt[this.mapping.date_stop].split(' ')[0]) : null;
+ date_start = evt[this.mapping.date_start].clone().startOf('day');
+ date_stop = this.mapping.date_stop ? evt[this.mapping.date_stop].clone().startOf('day') : null;
}
if (!date_stop && date_delay) {
@@ -605,8 +619,8 @@ return AbstractModel.extend({
'record': evt,
'start': date_start,
'end': date_stop,
- 'r_start':date_start.clone(),
- 'r_end':date_stop.clone(),
+ 'r_start': date_start,
+ 'r_end': date_stop,
'title': the_title,
'allDay': this.mapping.all_day && evt[this.mapping.all_day] || false,
'id': evt.id,
diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js
index a4930c87d37..b61236131c6 100644
--- a/addons/web/static/src/js/views/calendar/calendar_renderer.js
+++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js
@@ -288,7 +288,7 @@ return AbstractRenderer.extend({
if (field.type === "one2many" || field.type === "many2many") {
return field_utils.format[field.type]({data: record[fieldName]}, field);
} else {
- return field_utils.format[field.type](record[fieldName], field);
+ return field_utils.format[field.type](record[fieldName], field, {forceString: true});
}
},
/**
diff --git a/addons/web/static/src/js/views/graph/graph_model.js b/addons/web/static/src/js/views/graph/graph_model.js
index 5948e07a54f..8a7f5da6d5d 100644
--- a/addons/web/static/src/js/views/graph/graph_model.js
+++ b/addons/web/static/src/js/views/graph/graph_model.js
@@ -60,12 +60,13 @@ return AbstractModel.extend({
this.modelName = params.modelName;
this.chart = {
data: [],
- groupedBy: params.groupBys,
+ groupedBy: params.groupedBy.length ? params.groupedBy : params.groupBys,
measure: params.measure,
mode: params.mode,
domain: params.domain,
context: params.context,
};
+ this.defaultGroupedBy = params.groupedBy;
return this._loadGraph();
},
/**
@@ -88,11 +89,7 @@ return AbstractModel.extend({
this.chart.domain = params.domain;
}
if ('groupBy' in params) {
- if (!params.groupBy.length) {
- this.chart.groupedBy = this.initialGroupBys;
- } else {
- this.chart.groupedBy = params.groupBy;
- }
+ this.chart.groupedBy = params.groupBy.length ? params.groupBy : this.defaultGroupedBy;
}
if ('measure' in params) {
this.chart.measure = params.measure;
diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js
index b5c60fe9ab3..2592e3d96ec 100644
--- a/addons/web/static/src/js/views/kanban/kanban_controller.js
+++ b/addons/web/static/src/js/views/kanban/kanban_controller.js
@@ -164,9 +164,10 @@ var KanbanController = BasicController.extend({
_onAddColumn: function (event) {
var self = this;
this.model.createGroup(event.data.value, this.handle).then(function () {
- self.update({}, {reload: false});
+ return self.update({}, {reload: false});
+ }).then(function () {
self._updateButtons();
- self.trigger_up('scrollTo', {selector: '.o_column_quick_create'});
+ self.renderer.quickCreateToggleFold();
});
},
/**
diff --git a/addons/web/static/src/js/views/kanban/kanban_quick_create.js b/addons/web/static/src/js/views/kanban/kanban_quick_create.js
index 5e40352b26d..e81a290bf2d 100644
--- a/addons/web/static/src/js/views/kanban/kanban_quick_create.js
+++ b/addons/web/static/src/js/views/kanban/kanban_quick_create.js
@@ -192,7 +192,7 @@ var RecordQuickCreate = AbstractQuickCreate.extend({
var ColumnQuickCreate = AbstractQuickCreate.extend({
template: 'KanbanView.ColumnQuickCreate',
events: _.extend({}, AbstractQuickCreate.prototype.events, {
- 'click': '_onClick',
+ 'click': 'toggleFold',
'click input': '_onInputClicked',
'focusout': '_onFocusout',
}),
@@ -214,6 +214,22 @@ var ColumnQuickCreate = AbstractQuickCreate.extend({
},
//--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * Toggle fold/unfold the Column quick create widget
+ */
+ toggleFold: function () {
+ this.folded = !this.folded;
+ this._update();
+ if (!this.folded) {
+ this.$input.focus();
+ this.trigger_up('scrollTo', {selector: '.o_column_quick_create'});
+ }
+ },
+
+ //--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
@@ -243,18 +259,6 @@ var ColumnQuickCreate = AbstractQuickCreate.extend({
/**
* @private
- * @param {MouseEvent} event
- */
- _onClick: function () {
- this.folded = !this.folded;
- this._update();
- if (!this.folded) {
- this.$input.focus();
- this.trigger_up('scrollTo', {selector: '.o_column_quick_create'});
- }
- },
- /**
- * @private
*/
_onFocusout: function () {
var hasFocus = this.$(':focus').length > 0;
diff --git a/addons/web/static/src/js/views/kanban/kanban_renderer.js b/addons/web/static/src/js/views/kanban/kanban_renderer.js
index 87275260b01..ea2f0270017 100644
--- a/addons/web/static/src/js/views/kanban/kanban_renderer.js
+++ b/addons/web/static/src/js/views/kanban/kanban_renderer.js
@@ -113,6 +113,12 @@ var KanbanRenderer = BasicRenderer.extend({
this.widgets[0].addQuickCreate();
},
/**
+ * Toggle fold/unfold the Column quick create widget
+ */
+ quickCreateToggleFold: function () {
+ this.quickCreate.toggleFold();
+ },
+ /**
* Removes a widget (record if ungrouped, column if grouped) from the view.
*
* @param {Widget} widget the instance of the widget to remove
@@ -244,8 +250,8 @@ var KanbanRenderer = BasicRenderer.extend({
// Enable column quickcreate
if (this.columnOptions.group_creatable) {
- var quickCreate = new ColumnQuickCreate(this);
- quickCreate.appendTo(fragment);
+ this.quickCreate = new ColumnQuickCreate(this);
+ this.quickCreate.appendTo(fragment);
}
}
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index a917781b580..802f2f03a09 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -377,6 +377,10 @@ var ListRenderer = BasicRenderer.extend({
$cells.unshift($('<td>'));
}
var name = group.value === undefined ? _t('Undefined') : group.value;
+ var groupBy = this.state.groupedBy[groupLevel];
+ if (group.fields[groupBy.split(':')[0]].type !== 'boolean') {
+ name = name || _t('Undefined');
+ }
var $th = $('<th>')
.addClass('o_group_name')
.text(name + ' (' + group.count + ')');
diff --git a/addons/web/static/src/js/views/pivot/pivot_model.js b/addons/web/static/src/js/views/pivot/pivot_model.js
index 49c00663d47..d74af7fee22 100644
--- a/addons/web/static/src/js/views/pivot/pivot_model.js
+++ b/addons/web/static/src/js/views/pivot/pivot_model.js
@@ -229,11 +229,12 @@ var PivotModel = AbstractModel.extend({
this.data = {
domain: params.domain,
context: _.extend({}, session.user_context, params.context),
- groupedBy: params.rowGroupBys,
+ groupedBy: params.groupedBy,
colGroupBys: params.colGroupBys,
measures: params.measures,
sorted_column: {},
};
+ this.defaultGroupedBy = params.groupedBy;
return this._loadData();
},
/**
@@ -250,7 +251,7 @@ var PivotModel = AbstractModel.extend({
this.data.domain = this.initialDomain;
}
if ('groupBy' in params) {
- this.data.groupedBy = params.groupBy;
+ this.data.groupedBy = params.groupBy.length ? params.groupBy : this.defaultGroupedBy;
}
if (!this.data.has_data) {
return this._loadData();