diff options
| author | Aaron Bohy <aab@odoo.com> | 2017-06-08 14:57:09 +0200 |
|---|---|---|
| committer | Aaron Bohy <aab@odoo.com> | 2017-06-13 08:24:59 +0200 |
| commit | 5c4485d182b6ab68c7d5c97161f25e67a2fa60d2 (patch) | |
| tree | 941d407b94acba6d32c2bcd39caeea841c87ed56 | |
| parent | 76262f2043db1afd6d67a386796107dce91100e4 (diff) | |
[FIX] web: Many2one in X2Manys: quick create then save line
A problem occured with a required Many2One inside an X2Many (e.g.
order_line field in sale.order form view). If the user clicked on
'Add an item' to add a row, then typed some text in the Many2One,
then clicked on 'Create ...' to quick create a new product, and
then directly clicked on 'Add an item' to add another row (before
the name_create returned), the first row was discarded.
This was because before the name_create returns, the Many2one still
has no value, and as it is required, the row was considered invalid
and then discarded.
This rev. ensures to wait for the name_create to return before
trying to save the row and to create a new one.
| -rw-r--r-- | addons/web/static/src/js/fields/relational_fields.js | 43 | ||||
| -rw-r--r-- | addons/web/static/tests/fields/relational_fields_tests.js | 81 |
2 files changed, 105 insertions, 19 deletions
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index ae3b06d3a02..3b47b8c64b0 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -619,12 +619,15 @@ var FieldX2Many = AbstractField.extend({ * @returns {Deferred} */ commitChanges: function () { + var self = this; var inEditionRecordID = this.renderer && this.renderer.viewType === "list" && this.renderer.getEditableRecordID(); if (inEditionRecordID) { - return this._saveLine(inEditionRecordID); + return this.renderer.commitChanges(inEditionRecordID).then(function () { + return self._saveLine(inEditionRecordID); + }); } return this._super.apply(this, arguments); }, @@ -767,21 +770,17 @@ var FieldX2Many = AbstractField.extend({ * did not agree to discard. */ _saveLine: function (recordID) { - var self = this; var def = $.Deferred(); - this.renderer.commitChanges(recordID).then(function () { // TODO wrong as no mutex protection - var fieldNames = self.renderer.canBeSaved(recordID); - if (fieldNames.length) { - self.trigger_up('discard_changes', { - recordID: recordID, - onSuccess: def.resolve.bind(def), - onFailure: def.reject.bind(def), - }); - } else { - self.renderer.setRowMode(recordID, 'readonly') - .done(def.resolve.bind(def)); - } - }); + var fieldNames = this.renderer.canBeSaved(recordID); + if (fieldNames.length) { + this.trigger_up('discard_changes', { + recordID: recordID, + onSuccess: def.resolve.bind(def), + onFailure: def.reject.bind(def), + }); + } else { + this.renderer.setRowMode(recordID, 'readonly').done(def.resolve.bind(def)); + } return def; }, @@ -900,9 +899,15 @@ var FieldX2Many = AbstractField.extend({ ev.stopPropagation(); this.trigger_up('mutexify', { action: function () { - return self._saveLine(ev.data.recordID) - .done(ev.data.onSuccess) - .fail(ev.data.onFailure); + return self.renderer.commitChanges(ev.data.recordID).then(function () { + self.trigger_up('mutexify', { + action: function () { + return self._saveLine(ev.data.recordID) + .done(ev.data.onSuccess) + .fail(ev.data.onFailure); + }, + }); + }); }, }); }, @@ -922,7 +927,7 @@ var FieldX2Many = AbstractField.extend({ id: rowID, data: data, }); - }) + }); }, /** * Adds field name information to the event, so that the view upstream is diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 559a9387baa..a1f851eb91f 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -659,6 +659,87 @@ QUnit.module('relational_fields', { }); }); + QUnit.test('list in form: quick create then add a new line directly', function (assert) { + // required many2one inside a one2many list: directly after quick creating + // a new many2one value (before the name_create returns), click on add an item: + // at this moment, the many2one has still no value, and as it is required, + // the row is discarded if a saveLine is requested. However, it should + // wait for the name_create to return before trying to save the line. + var done = assert.async(); + assert.expect(8); + + this.data.partner.onchanges = { + trululu: function () {}, + }; + + var M2O_DELAY = relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY; + relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0; + + var def = $.Deferred(); + var newRecordID; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '<form>' + + '<sheet>' + + '<field name="p">' + + '<tree editable="bottom">' + + '<field name="trululu" required="1"/>' + + '</tree>' + + '</field>' + + '</sheet>' + + '</form>', + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'name_create') { + return def.then(_.constant(result)).then(function (nameGet) { + newRecordID = nameGet[0]; + return nameGet; + }); + } + if (args.method === 'create') { + assert.deepEqual(args.args[0].p[0][2].trululu, newRecordID); + } + return result; + }, + }); + + form.$('.o_field_x2many_list_row_add a').click(); + + var $dropdown = form.$('.o_field_many2one input').autocomplete('widget'); + form.$('.o_field_many2one input').val('b').trigger('keydown'); + concurrency.delay(0).then(function () { + $dropdown.find('li:first()').click(); // quick create 'b' + form.$('.o_field_x2many_list_row_add a').click(); + + assert.strictEqual(form.$('.o_data_row').length, 1, + "there should still be only one row"); + assert.ok(form.$('.o_data_row').hasClass('o_selected_row'), + "the row should still be in edition"); + + def.resolve(); + + assert.strictEqual(form.$('.o_data_row:first .o_data_cell').text(), 'b', + "first row should have the correct m2o value"); + assert.strictEqual(form.$('.o_data_row').length, 2, + "there should now be 2 rows"); + assert.ok(form.$('.o_data_row:nth(1)').hasClass('o_selected_row'), + "the second row should be in edition"); + + form.$buttons.find('.o_form_button_save').click(); + + assert.strictEqual(form.$('.o_data_row').length, 1, + "there should be 1 row saved (the second one was empty and invalid)"); + assert.strictEqual(form.$('.o_data_row .o_data_cell').text(), 'b', + "should have the correct m2o value"); + + relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = M2O_DELAY; + form.destroy(); + done(); + }); + }); + QUnit.test('autocompletion in a many2one, in form view with a domain', function (assert) { assert.expect(1); |
