diff options
Diffstat (limited to 'addons/web/static/src')
15 files changed, 149 insertions, 94 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(); diff --git a/addons/web/static/src/less/list_view.less b/addons/web/static/src/less/list_view.less index a09b8e7ac0c..edb4c184408 100644 --- a/addons/web/static/src/less/list_view.less +++ b/addons/web/static/src/less/list_view.less @@ -127,7 +127,7 @@ } } > .o_field_translate { - position: absolute; + .o-position-absolute(0, 5px); width: 16px; // to have some space on the right } .o_input { |
