diff options
Diffstat (limited to 'addons/web/static/src')
17 files changed, 208 insertions, 73 deletions
diff --git a/addons/web/static/src/js/chrome/action_manager.js b/addons/web/static/src/js/chrome/action_manager.js index 8ae6d93bc58..9885d684dec 100644 --- a/addons/web/static/src/js/chrome/action_manager.js +++ b/addons/web/static/src/js/chrome/action_manager.js @@ -775,17 +775,17 @@ var ActionManager = Widget.extend({ * @param {Object} options see @doAction options */ _preprocessAction: function (action, options) { - action._originalAction = JSON.stringify(action); - - action.jsID = _.uniqueId('action_'); - action.pushState = options.pushState; - // ensure that the context and domain are evaluated var context = new Context(this.userContext, options.additional_context, action.context); action.context = pyeval.eval('context', context); if (action.domain) { action.domain = pyeval.eval('domain', action.domain, action.context); } + + action._originalAction = JSON.stringify(action); + + action.jsID = _.uniqueId('action_'); + action.pushState = options.pushState; }, /** * Unlinks the given action and its controller from the internal structures diff --git a/addons/web/static/src/js/chrome/keyboard_navigation_mixin.js b/addons/web/static/src/js/chrome/keyboard_navigation_mixin.js index dc1b5247cb1..d0eed176d9c 100644 --- a/addons/web/static/src/js/chrome/keyboard_navigation_mixin.js +++ b/addons/web/static/src/js/chrome/keyboard_navigation_mixin.js @@ -26,7 +26,6 @@ odoo.define('web.KeyboardNavigationMixin', function (require) { var KeyboardNavigationMixin = { events: { 'keydown': '_onKeyDown', - 'keypress': '_onKeyPress', 'keyup': '_onKeyUp', }, @@ -127,6 +126,11 @@ odoo.define('web.KeyboardNavigationMixin', function (require) { } } }); + + var elementsWithoutAriaKeyshortcut = this.$el.find('[accesskey]').not('[aria-keyshortcuts]'); + _.each(elementsWithoutAriaKeyshortcut, function (elem) { + elem.setAttribute('aria-keyshortcuts', 'Alt+Shift+' + elem.accessKey); + }); this._addAccessKeyOverlays(); } if (keyDownEvent.altKey && !keyDownEvent.ctrlKey && keyDownEvent.key.length === 1) { // we don't want to catch the Alt key down, only the characters A to Z and number keys @@ -138,6 +142,7 @@ odoo.define('web.KeyboardNavigationMixin', function (require) { if (elementWithAccessKey.length) { if (this.BrowserDetection.isOsMac() || !this.BrowserDetection.isBrowserChrome()) { // on windows and linux, chrome does not prevent the default of the accesskeys + elementWithAccessKey[0].focus(); elementWithAccessKey[0].click(); if (keyDownEvent.preventDefault) keyDownEvent.preventDefault(); else keyDownEvent.returnValue = false; if (keyDownEvent.stopPropagation) keyDownEvent.stopPropagation(); @@ -178,21 +183,6 @@ odoo.define('web.KeyboardNavigationMixin', function (require) { } }, /** - * hides the shortcut overlays when key press event is triggered on the ALT key - * - * @private - * @param keyPressEvent {jQueryKeyboardEvent} the keyboard event triggered - * @return {undefined|false} - */ - _onKeyPress: function (keyPressEvent) { - if ((keyPressEvent.altKey || keyPressEvent.key === 'Alt') && !keyPressEvent.ctrlKey) { - if (keyPressEvent.preventDefault) keyPressEvent.preventDefault(); else keyPressEvent.returnValue = false; - if (keyPressEvent.stopPropagation) keyPressEvent.stopPropagation(); - if (keyPressEvent.cancelBubble) keyPressEvent.cancelBubble = true; - return false; - } - }, - /** * hides the shortcut overlays when keyup event is triggered on the ALT key * * @private diff --git a/addons/web/static/src/js/core/ajax.js b/addons/web/static/src/js/core/ajax.js index 0ee6d2ad1ed..d6c67eacaec 100644 --- a/addons/web/static/src/js/core/ajax.js +++ b/addons/web/static/src/js/core/ajax.js @@ -260,8 +260,20 @@ function get_file(options) { if (options.error) { var body = this.contentDocument.body; var nodes = body.children.length === 0 ? body.childNodes : body.children; - var node = nodes[1] || nodes[0]; - options.error(JSON.parse(node.textContent)); + var errorParams = {}; + + try { // Case of a serialized Odoo Exception: It is Json Parsable + var node = nodes[1] || nodes[0]; + errorParams = JSON.parse(node.textContent); + } catch (e) { // Arbitrary uncaught python side exception + errorParams = { + message: nodes.length > 1 ? nodes[1].textContent : '', + data: { + title: nodes.length > 0 ? nodes[0].textContent : '', + } + } + } + options.error(errorParams); } } finally { complete(); diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 10057d44984..31b0c53dd64 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -622,7 +622,7 @@ var FieldMonetary = InputField.extend({ this.$el.empty(); // Prepare and add the input - this._prepareInput().appendTo(this.$el); + this._prepareInput(this.$input).appendTo(this.$el); if (this.currency) { // Prepare and add the currency symbol diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index 7044a79a535..62cc965cc15 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -340,8 +340,8 @@ var FieldMany2One = AbstractField.extend({ _renderReadonly: function () { var value = _.escape((this.m2o_value || "").trim()).split("\n").join("<br/>"); this.$el.html(value); - if (!this.nodeOptions.no_open) { - this.$el.attr('href', '#'); + if (!this.nodeOptions.no_open && this.value) { + this.$el.attr('href', _.str.sprintf('#id=%s&model=%s', this.value.res_id, this.field.relation)); this.$el.addClass('o_form_uri'); } }, @@ -1334,10 +1334,7 @@ var FieldOne2Many = FieldX2Many.extend({ var self = this; var id = ev.data.id; - var onSaved = function (record, hasChanged) { - if (!hasChanged) { - return; - } + var onSaved = function (record) { if (_.some(self.value.data, {id: record.id})) { // the record already exists in the relation, so trigger an // empty 'UPDATE' operation when the user clicks on 'Save' in diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js index fe4ba14de71..2f9957737ae 100644 --- a/addons/web/static/src/js/views/abstract_controller.js +++ b/addons/web/static/src/js/views/abstract_controller.js @@ -12,6 +12,7 @@ odoo.define('web.AbstractController', function (require) { * reading localstorage, ...) has to go through the controller. */ +var concurrency = require('web.concurrency'); var ControlPanelMixin = require('web.ControlPanelMixin'); var core = require('web.core'); var AbstractAction = require('web.AbstractAction'); @@ -54,6 +55,8 @@ var AbstractController = AbstractAction.extend(ControlPanelMixin, { this.controllerID = params.controllerID; this.initialState = params.initialState; + // use a DropPrevious to correctly handle concurrent updates + this.dp = new concurrency.DropPrevious(); // those arguments are temporary, they won't be necessary as soon as the // ControlPanel will be handled by the View this.displayName = params.displayName; @@ -232,11 +235,11 @@ var AbstractController = AbstractAction.extend(ControlPanelMixin, { var self = this; var shouldReload = (options && 'reload' in options) ? options.reload : true; var def = shouldReload ? this.model.reload(this.handle, params) : $.when(); - return def.then(function (handle) { + return this.dp.add(def).then(function (handle) { self.handle = handle || self.handle; // update handle if we reloaded var state = self.model.get(self.handle); var localState = self.renderer.getLocalState(); - return self.renderer.updateState(state, params).then(function () { + return self.dp.add(self.renderer.updateState(state, params)).then(function () { self.renderer.setLocalState(localState); self._update(state); }); diff --git a/addons/web/static/src/js/views/abstract_renderer.js b/addons/web/static/src/js/views/abstract_renderer.js index 2394fb31a64..059380f87bd 100644 --- a/addons/web/static/src/js/views/abstract_renderer.js +++ b/addons/web/static/src/js/views/abstract_renderer.js @@ -67,6 +67,11 @@ return Widget.extend({ getLocalState: function () { }, /** + * Order to focus to be given to the content of the current view + */ + giveFocus: function () { + }, + /** * This is the reverse operation from getLocalState. With this method, we * expect the renderer to restore all DOM state, if it is relevant. * 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 ec0fad66a06..cc3dbd67595 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1068,7 +1068,13 @@ var BasicModel = AbstractModel.extend({ addFieldsInfo: function (recordID, viewInfo) { var record = this.localData[recordID]; record.fields = _.extend({}, record.fields, viewInfo.fields); - record.fieldsInfo = _.extend({}, record.fieldsInfo, viewInfo.fieldsInfo); + // complete the given fieldsInfo with the fields of the main view, so + // that those field will be reloaded if a reload is triggered by the + // secondary view + var fieldsInfo = _.mapObject(viewInfo.fieldsInfo, function (fieldsInfo) { + return _.defaults({}, fieldsInfo, record.fieldsInfo[record.viewType]); + }); + record.fieldsInfo = _.extend({}, record.fieldsInfo, fieldsInfo); }, /** * For list resources, this freezes the current records order. @@ -1553,8 +1559,10 @@ var BasicModel = AbstractModel.extend({ rec = self._makeDataPoint(params); list._cache[rec.res_id] = rec.id; } - - rec._noAbandon = true; + // Do not abandon the record if it has been created + // from `default_get`. The list has a savepoint only + // after having fully executed `default_get`. + rec._noAbandon = !list._savePoint; list._changes.push({operation: 'ADD', id: rec.id}); if (command[0] === 1) { list._changes.push({operation: 'UPDATE', id: rec.id}); @@ -1973,6 +1981,9 @@ var BasicModel = AbstractModel.extend({ var records = []; var ids = []; list = this._applyX2ManyOperations(list); + if (_.isEmpty(list.data)) { + return $.when(); + } _.each(list.data, function (localId) { var record = self.localData[localId]; var data = record._changes || record.data; @@ -1986,13 +1997,16 @@ var BasicModel = AbstractModel.extend({ return this._rpc({ model: model, method: 'name_get', - args: [ids], + args: [_.uniq(ids)], context: list.context, }) .then(function (name_gets) { - for (var i = 0; i < name_gets.length; i++) { - records[i].data.display_name = name_gets[i][1]; - } + _.each(records, function (record) { + var nameGet = _.find(name_gets, function (nameGet) { + return nameGet[0] === record.data.id; + }); + record.data.display_name = nameGet[1]; + }); }); }, /** @@ -2010,7 +2024,8 @@ var BasicModel = AbstractModel.extend({ */ _fetchRecord: function (record, options) { var self = this; - var fieldNames = options && options.fieldNames || record.getFieldNames(); + options = options || {}; + var fieldNames = options.fieldNames || record.getFieldNames(options); fieldNames = _.uniq(fieldNames.concat(['display_name'])); return this._rpc({ model: record.model, @@ -2477,7 +2492,16 @@ var BasicModel = AbstractModel.extend({ var self = this; var def; if (list.static) { - def = this._readUngroupedList(list); + def = this._readUngroupedList(list).then(function () { + if (list.parentID && self.isNew(list.parentID)) { + // list from a default_get, so fetch display_name for many2one fields + var many2ones = self._getMany2OneFieldNames(list); + var defs = _.map(many2ones, function (name) { + return self._fetchNameGets(list, name); + }); + return $.when.apply($, defs); + } + }); } else { def = this._searchReadUngroupedList(list); } @@ -2510,8 +2534,9 @@ var BasicModel = AbstractModel.extend({ _fetchX2Manys: function (record, options) { var self = this; var defs = []; - var fieldNames = options && options.fieldNames || record.getFieldNames(); - var viewType = options && options.viewType || record.viewType; + options = options || {}; + var fieldNames = options.fieldNames || record.getFieldNames(options); + var viewType = options.viewType || record.viewType; _.each(fieldNames, function (fieldName) { var field = record.fields[fieldName]; if (field.type === 'one2many' || field.type === 'many2many') { @@ -3034,11 +3059,32 @@ var BasicModel = AbstractModel.extend({ * default view type. * * @param {Object} element an element from the localData + * @param {Object} [options] + * @param {Object} [options.viewType] current viewType. If not set, we will + * assume main viewType from the record * @returns {string[]} the list of field names */ - _getFieldNames: function (element) { + _getFieldNames: function (element, options) { var fieldsInfo = element.fieldsInfo; - return Object.keys(fieldsInfo && fieldsInfo[element.viewType] || {}); + var viewType = options && options.viewType || element.viewType; + return Object.keys(fieldsInfo && fieldsInfo[viewType] || {}); + }, + /** + * Get many2one fields names in a datapoint. This is useful in order to + * fetch their names in the case of a default_get. + * + * @private + * @param {Object} datapoint a valid resource object + * @returns {string[]} list of field names that are many2one + */ + _getMany2OneFieldNames: function (datapoint) { + var many2ones = []; + _.each(datapoint.fields, function (field, name) { + if (field.type === 'many2one') { + many2ones.push(name); + } + }); + return many2ones; }, /** * Evaluate the record evaluation context. This method is supposed to be @@ -3533,13 +3579,16 @@ var BasicModel = AbstractModel.extend({ * @see _fetchRecord @see _makeDefaultRecord * * @param {Object} record - * @param {Object} record + * @param {Object} [options] + * @param {Object} [options.viewType] current viewType. If not set, we will + * assume main viewType from the record * @returns {Deferred<Object>} resolves to the finished resource */ _postprocess: function (record, options) { var self = this; var defs = []; - _.each(record.getFieldNames(), function (name) { + + _.each(record.getFieldNames(options), function (name) { var field = record.fields[name]; var fieldInfo = record.fieldsInfo[record.viewType][name] || {}; var options = fieldInfo.options || {}; @@ -3879,14 +3928,20 @@ var BasicModel = AbstractModel.extend({ var emptyGroupsIDs = _.difference(_.pluck(previousGroups, 'id'), list.data); _.each(emptyGroupsIDs, function (groupID) { list.data.push(groupID); + var emptyGroup = self.localData[groupID]; + // this attribute hasn't been updated in the previous + // loop for empty groups + emptyGroup.aggregateValues = {}; }); } return $.when.apply($, defs).then(function () { - // generate the res_ids of the main list, being the concatenation - // of the fetched res_ids in each group - list.res_ids = _.flatten(_.map(arguments, function (group) { - return group ? group.res_ids : []; - })); + if (!options || !options.onlyGroups) { + // generate the res_ids of the main list, being the concatenation + // of the fetched res_ids in each group + list.res_ids = _.flatten(_.map(arguments, function (group) { + return group ? group.res_ids : []; + })); + } return list; }); }); @@ -4116,10 +4171,20 @@ var BasicModel = AbstractModel.extend({ var r2 = self.localData[record2ID]; var data1 = _.extend({}, r1.data, r1._changes); var data2 = _.extend({}, r2.data, r2._changes); - if (data1[order.name] < data2[order.name]) { + + // Default value to sort against: the value of the field + var orderData1 = data1[order.name]; + var orderData2 = data2[order.name]; + + // If the field is a relation, sort on the display_name of those records + if (list.fields[order.name].type === 'many2one') { + orderData1 = orderData1 ? self.localData[orderData1].data.display_name : ""; + orderData2 = orderData2 ? self.localData[orderData2].data.display_name : ""; + } + if (orderData1 < orderData2) { return order.asc ? -1 : 1; } - if (data1[order.name] > data2[order.name]) { + if (orderData1 > orderData2) { return order.asc ? 1 : -1; } return compareRecords(record1ID, record2ID, level + 1); diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index 16507184915..24b053a7bbe 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -135,13 +135,6 @@ var BasicRenderer = AbstractRenderer.extend({ dom.setSelectionRange(field.getFocusableElement().get(0), {start: offset, end: offset}); } }, - - /** - * Order to focus to be given to the content of the current view - */ - giveFocus:function() { - }, - //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- diff --git a/addons/web/static/src/js/views/calendar/calendar_view.js b/addons/web/static/src/js/views/calendar/calendar_view.js index a813a626565..6c12d508937 100644 --- a/addons/web/static/src/js/views/calendar/calendar_view.js +++ b/addons/web/static/src/js/views/calendar/calendar_view.js @@ -118,8 +118,20 @@ var CalendarView = AbstractView.extend({ //if quick_add = is NOT False and IS specified in view, we this one for widgets.QuickCreate' this.controllerParams.quickAddPop = (!('quick_add' in attrs) || utils.toBoolElse(attrs.quick_add+'', true)); this.controllerParams.disableQuickCreate = params.disable_quick_create || !this.controllerParams.quickAddPop; - // If this field is set ot true, we don't open the event in form view, but in a popup with the view_id passed by this parameter - this.controllerParams.formViewId = !attrs.form_view_id || !utils.toBoolElse(attrs.form_view_id, true) ? false : attrs.form_view_id; + + // If form_view_id is set, then the calendar view will open a form view + // with this id, when it needs to edit or create an event. + this.controllerParams.formViewId = + attrs.form_view_id ? parseInt(attrs.form_view_id, 10) : false; + if (!this.controllerParams.formViewId && params.action) { + var formViewDescr = _.find(params.action.views, function (v) { + return v[1] === 'form'; + }); + if (formViewDescr) { + this.controllerParams.formViewId = formViewDescr[0]; + } + } + this.controllerParams.readonlyFormViewId = !attrs.readonly_form_view_id || !utils.toBoolElse(attrs.readonly_form_view_id, true) ? false : attrs.readonly_form_view_id; this.controllerParams.eventOpenPopup = utils.toBoolElse(attrs.event_open_popup || '', false); this.controllerParams.mapping = mapping; diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index dbb64dc3682..be49c12e768 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -233,6 +233,16 @@ var FormController = BasicController.extend({ return changedFields; }); }, + /** + * Overrides to force the viewType to 'form', so that we ensure that the + * correct fields are reloaded (this is only useful for one2many form views). + * + * @override + */ + update: function (params, options) { + params = _.extend({viewType: 'form'}, params); + return this._super(params, options); + }, //-------------------------------------------------------------------------- // Private diff --git a/addons/web/static/src/js/views/form/form_view.js b/addons/web/static/src/js/views/form/form_view.js index 607f9f54a89..f0ea360fed0 100644 --- a/addons/web/static/src/js/views/form/form_view.js +++ b/addons/web/static/src/js/views/form/form_view.js @@ -73,6 +73,14 @@ var FormView = BasicView.extend({ _.each(this.loadParams.fieldsInfo.form, function (attrs, fieldName) { var field = fields[fieldName]; + if (!field) { + // when a one2many record is opened in a form view, the fields + // of the main one2many view (list or kanban) are added to the + // fieldsInfo of its form view, but those fields aren't in the + // loadParams.fields, as they are not displayed in the view, so + // we can ignore them. + return; + } if (field.type !== 'one2many' && field.type !== 'many2many') { return; } diff --git a/addons/web/static/src/js/views/kanban/kanban_model.js b/addons/web/static/src/js/views/kanban/kanban_model.js index 411d4cc7b14..6c4ea1f133c 100644 --- a/addons/web/static/src/js/views/kanban/kanban_model.js +++ b/addons/web/static/src/js/views/kanban/kanban_model.js @@ -398,9 +398,8 @@ var KanbanModel = BasicModel.extend({ return $.when(); }, /** - * Reloads all progressbar data if the given id is a record's one. This is - * done after given deferred and insures that the given deferred's result is - * not lost. + * Reloads all progressbar data. This is done after given deferred and + * insures that the given deferred's result is not lost. * * @private * @param {string} recordID @@ -409,7 +408,9 @@ var KanbanModel = BasicModel.extend({ */ _reloadProgressBarGroupFromRecord: function (recordID, def) { var element = this.localData[recordID]; - if (element.type !== 'record') { + if (element.type === 'list' && !element.parentID) { + // we are reloading the whole view, so there is no need to manually + // reload the progressbars return def; } diff --git a/addons/web/static/src/js/views/kanban/kanban_record.js b/addons/web/static/src/js/views/kanban/kanban_record.js index 072f4d41f27..30bafaab117 100644 --- a/addons/web/static/src/js/views/kanban/kanban_record.js +++ b/addons/web/static/src/js/views/kanban/kanban_record.js @@ -23,7 +23,6 @@ var KanbanRecord = Widget.extend({ events: { 'click .oe_kanban_action': '_onKanbanActionClicked', 'click .o_kanban_manage_toggle_button': '_onManageTogglerClicked', - 'keydown' : '_onKanbanKeyDown', }, /** * @override @@ -289,6 +288,9 @@ var KanbanRecord = Widget.extend({ if (this.$el.hasClass('oe_kanban_global_click') || this.$el.hasClass('oe_kanban_global_click_edit')) { this.$el.on('click', this._onGlobalClick.bind(this)); + this.$el.on('keydown', this._onKeyDownCard.bind(this)); + } else { + this.$el.on('keydown', this._onKeyDownOpenFirstLink.bind(this)); } this._processFields(); this._processWidgets(); @@ -505,11 +507,33 @@ var KanbanRecord = Widget.extend({ this.do_warn("Kanban: no action for type : " + type); } }, - _onKanbanKeyDown: function (event) { + /** + * This event is linked to the kanban card when there is a global_click + * class on this card + * + * @private + * @param {KeyDownEvent} event + */ + _onKeyDownCard: function (event) { + switch (event.keyCode) { + case $.ui.keyCode.ENTER: + event.preventDefault(); + this._onGlobalClick(event); + break; + } + }, + /** + * This event is linked ot the kanban card when there is no global_click + * class on the card + * + * @private + * @param {KeyDownEvent} event + */ + _onKeyDownOpenFirstLink: function (event) { switch (event.keyCode) { case $.ui.keyCode.ENTER: event.preventDefault(); - this._openRecord(); + $(event.target).find('a, button').first().click(); break; } }, diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js index 4d26ba3627e..49fe72cc4b2 100644 --- a/addons/web/static/src/js/views/list/list_editable_renderer.js +++ b/addons/web/static/src/js/views/list/list_editable_renderer.js @@ -539,8 +539,13 @@ ListRenderer.include({ * @returns {Deferred} this deferred is resolved immediately */ _renderView: function () { + var self = this; this.currentRow = null; - return this._super.apply(this, arguments); + return this._super.apply(this, arguments).then(function () { + if (self._isEditable()) { + self.$('table').addClass('o_editable_list'); + } + }); }, /** * Force the resequencing of the items in the list. 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 4a5d9b45b4a..b45f7a9ffdf 100644 --- a/addons/web/static/src/js/views/pivot/pivot_model.js +++ b/addons/web/static/src/js/views/pivot/pivot_model.js @@ -17,6 +17,7 @@ odoo.define('web.PivotModel', function (require) { */ var AbstractModel = require('web.AbstractModel'); +var concurrency = require('web.concurrency'); var core = require('web.core'); var session = require('web.session'); var utils = require('web.utils'); @@ -32,6 +33,7 @@ var PivotModel = AbstractModel.extend({ this._super.apply(this, arguments); this.numbering = {}; this.data = null; + this._loadDataDropPrevious = new concurrency.DropPrevious(); }, //-------------------------------------------------------------------------- @@ -556,7 +558,7 @@ var PivotModel = AbstractModel.extend({ } } - return $.when.apply(null, groupBys.map(function (groupBy) { + return this._loadDataDropPrevious.add($.when.apply(null, groupBys.map(function (groupBy) { return self._rpc({ model: self.modelName, method: 'read_group', @@ -566,7 +568,7 @@ var PivotModel = AbstractModel.extend({ groupBy: groupBy, lazy: false, }); - })).then(function () { + }))).then(function () { var data = Array.prototype.slice.call(arguments); if (data[0][0].__count === 0) { self.data.has_data = false; diff --git a/addons/web/static/src/scss/list_view.scss b/addons/web/static/src/scss/list_view.scss index 36bee4439c6..49cba9409cc 100644 --- a/addons/web/static/src/scss/list_view.scss +++ b/addons/web/static/src/scss/list_view.scss @@ -5,6 +5,14 @@ background-color: $o-view-background-color; margin-bottom: 0px; + // Checkbox in editable list + // should be clickable and activate the row + &.o_editable_list .o_data_row:not(.o_selected_row) .o_data_cell { + .o_checkbox:not(.o_readonly_modifier) { + pointer-events: none; + } + } + &.table { td, th { vertical-align: middle; |
