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/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/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index fdd8ffdd124..1a7badd353f 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -1,11 +1,13 @@ odoo.define('web.relational_fields_tests', function (require) { "use strict"; +var BasicModel = require('web.BasicModel'); var concurrency = require('web.concurrency'); var FormView = require('web.FormView'); var ListView = require('web.ListView'); var relationalFields = require('web.relational_fields'); var testUtils = require('web.test_utils'); +var Widget = require('web.Widget'); var createView = testUtils.createView; @@ -361,7 +363,6 @@ QUnit.module('relational_fields', { form.destroy(); }); - QUnit.test('many2one readonly fields with option "no_open"', function (assert) { assert.expect(1); @@ -564,10 +565,71 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('many2one field with option standalone', function (assert) { + assert.expect(3); + var done = assert.async(); + + var M2O_DELAY = relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY; + relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0; + + var fixture = $('#qunit-fixture'); + var self = this; + + var model = testUtils.createModel({ + Model: BasicModel, + data: this.data, + }); + + model.makeRecord('coucou', [{ + name: 'partner_id', + relation: 'partner', + type: 'many2one', + value: [1, 'first partner'], + }], { + partner_id: { + options: { + no_open: true, + }, + }, + }).then(function (recordID) { + var record = model.get(recordID); + var parent = new Widget(); + testUtils.addMockEnvironment(parent, { + data: self.data, + mockRPC: function (route, args) { + assert.step(args.method); + return this._super.apply(this, arguments); + }, + }); + + var relField = new relationalFields.FieldMany2One(parent, + 'partner_id', + record, + { + mode: 'edit', + standalone: true, + }); + + relField.appendTo(fixture); + $('input.o_input').val('xyzzrot').trigger('input'); + + concurrency.delay(0).then(function () { + var $dropdown = $('input.o_input').autocomplete('widget'); + $dropdown.find('.o_m2o_dropdown_option:contains(Create)') + .first().mouseenter().click(); + assert.verifySteps(['name_search', 'name_create']); + parent.destroy(); + model.destroy(); + relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = M2O_DELAY; + done(); + }); + }); + }); + // QUnit.test('onchange on a many2one to a different model', function (assert) { - // This test is commented because the mock server does not give the correct response. - // It should return a couple [id, display_name], but I don't know the logic used - // by the server, so it's hard to emulate it correctly + // This test is commented because the mock server does not give the correct response. + // It should return a couple [id, display_name], but I don't know the logic used + // by the server, so it's hard to emulate it correctly // assert.expect(2); // this.data.partner.records[0].product_id = 41;