diff --git a/addons/sale_product_matrix/static/tests/tours/sale_product_matrix_tour.js b/addons/sale_product_matrix/static/tests/tours/sale_product_matrix_tour.js index 10d15c34843..e5e56c5c4e0 100644 --- a/addons/sale_product_matrix/static/tests/tours/sale_product_matrix_tour.js +++ b/addons/sale_product_matrix/static/tests/tours/sale_product_matrix_tour.js @@ -69,7 +69,8 @@ tour.register('sale_matrix_tour', { run: 'click' // apply the matrix }, { trigger: ".o_form_editable .o_field_many2one[name='partner_id'] input", - extra_trigger: ".o_sale_order", + // wait for qty to be 1 + extra_trigger: '.o_sale_order .o_field_cell.o_data_cell.o_list_number:contains("1.00")', run: 'text Agrolait' }, { trigger: ".ui-menu-item > a", 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 87a47fa1bb1..79e19818e92 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -582,6 +582,7 @@ var BasicModel = AbstractModel.extend({ getFieldNames: element.getFieldNames, id: element.id, isDirty: element.isDirty, + isNew: element.isNew, limit: element.limit, model: element.model, offset: element.offset, @@ -640,6 +641,7 @@ var BasicModel = AbstractModel.extend({ groupsOffset: element.groupsOffset, id: element.id, isDirty: element.isDirty, + isNew: element.isNew, isOpen: element.isOpen, isSample: this.isSampleModel, limit: element.limit, @@ -4129,6 +4131,7 @@ var BasicModel = AbstractModel.extend({ dataPoint.getDomain = this._getDomain.bind(this, dataPoint); dataPoint.getFieldNames = this._getFieldNames.bind(this, dataPoint); dataPoint.isDirty = this.isDirty.bind(this, dataPoint.id); + dataPoint.isNew = this.isNew.bind(this, dataPoint.id); this.localData[dataPoint.id] = dataPoint; diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js index 9313e6b6332..5263eea7cc4 100644 --- a/addons/web/static/src/js/views/list/list_controller.js +++ b/addons/web/static/src/js/views/list/list_controller.js @@ -83,7 +83,7 @@ var ListController = BasicController.extend({ */ canBeRemoved: async function () { const _super = this._super.bind(this); - await this.renderer.unselectRow(); + await this.renderer.unselectRow({ canDiscard: true }); return _super(...arguments); }, /* @@ -415,7 +415,7 @@ var ListController = BasicController.extend({ return null; } return Object.assign(this._super(...arguments), { - validate: () => this.renderer.unselectRow(), + validate: () => this.renderer.unselectRow({ canDiscard: true }), }); }, /** 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 230733448e2..7a215ed322a 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 @@ -495,11 +495,14 @@ ListRenderer.include({ * prevent subsequent editions. These edits would be lost, because the list * view only saves records when unselecting a row. * + * @param {Object} [options] + * @param {boolean} [options.canDiscard=false] when set to true, + * discard the record if it is new and not dirty otherwise save it * @returns {Promise} The promise resolves if the row was unselected (and * possibly removed). If may be rejected, when the row is dirty and the * user refuses to discard its changes. */ - unselectRow: function () { + unselectRow: function (options={}) { // Protect against calling this method when no row is selected if (this.currentRow === null) { return Promise.resolve(); @@ -514,7 +517,23 @@ ListRenderer.include({ } toggleWidgets(true); - return new Promise((resolve, reject) => { + return new Promise(async (resolve, reject) => { + const record = this._getRecord(recordID); + if (options.canDiscard && record.isNew()) { + await this.commitChanges(recordID); + if (!record.isDirty()) { + this.trigger_up('mutexify', { + action: () => { + return this.trigger_up('discard_changes', { + recordID: recordID, + onSuccess: resolve, + onFailure: reject, + }); + }, + }); + return; + } + } this.trigger_up('save_line', { recordID: recordID, onSuccess: resolve, @@ -1325,7 +1344,7 @@ ListRenderer.include({ var recordId = this._getRecordID(rowIndex); // To select a row, the currently selected one must be unselected first var self = this; - return this.unselectRow().then((selectNextRow = true) => { + return this.unselectRow({ canDiscard: true }).then((selectNextRow = true) => { if (!selectNextRow) { return Promise.resolve(); } @@ -1853,7 +1872,11 @@ ListRenderer.include({ return; } - this.unselectRow(); + this.unselectRow({ + // if save button is clicked then save the record forcefully even if + // it is non dirty + canDiscard: ![...event.target.classList].includes('o_list_button_save') + }); }, }); diff --git a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js index bb21df5ccfb..500b61eed1f 100644 --- a/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js +++ b/addons/web/static/tests/fields/relational_fields/field_many2one_tests.js @@ -2880,6 +2880,7 @@ QUnit.module('fields', {}, function () { arch: '