diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index 44f884f130c..df7f018369d 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -1785,15 +1785,7 @@ var FieldSelection = AbstractField.extend({
*/
init: function () {
this._super.apply(this, arguments);
- if (this.field.type === 'many2one') {
- this.values = this.record.specialData[this.name];
- this.formatType = 'many2one';
- } else {
- this.values = _.reject(this.field.selection, function (v) {
- return v[0] === false && v[1] === '';
- });
- }
- this.values = [[false, this.attrs.placeholder || '']].concat(this.values);
+ this._setValues();
},
//--------------------------------------------------------------------------
@@ -1823,13 +1815,12 @@ var FieldSelection = AbstractField.extend({
* @private
*/
_renderEdit: function () {
- if (!this.$el.children().length) {
- for (var i = 0 ; i < this.values.length ; i++) {
- this.$el.append($('', {
- value: JSON.stringify(this.values[i][0]),
- html: this.values[i][1]
- }));
- }
+ this.$el.empty();
+ for (var i = 0 ; i < this.values.length ; i++) {
+ this.$el.append($('', {
+ value: JSON.stringify(this.values[i][0]),
+ html: this.values[i][1]
+ }));
}
var value = this.value;
if (this.field.type === 'many2one' && value) {
@@ -1844,6 +1835,31 @@ var FieldSelection = AbstractField.extend({
_renderReadonly: function () {
this.$el.empty().text(this._formatValue(this.value));
},
+ /**
+ * @override
+ */
+ _reset: function () {
+ this._super.apply(this, arguments);
+ this._setValues();
+ },
+ /**
+ * Sets the possible field values. If the field is a many2one, those values
+ * may change during the lifecycle of the widget if the domain change (an
+ * onchange may change the domain).
+ *
+ * @private
+ */
+ _setValues: function () {
+ if (this.field.type === 'many2one') {
+ this.values = this.record.specialData[this.name];
+ this.formatType = 'many2one';
+ } else {
+ this.values = _.reject(this.field.selection, function (v) {
+ return v[0] === false && v[1] === '';
+ });
+ }
+ this.values = [[false, this.attrs.placeholder || '']].concat(this.values);
+ },
//--------------------------------------------------------------------------
// Handlers
@@ -1869,7 +1885,9 @@ var FieldSelection = AbstractField.extend({
});
var FieldRadio = FieldSelection.extend({
- template: 'FieldRadio',
+ template: null,
+ className: 'o_field_radio',
+ tagName: 'span',
specialData: "_fetchSpecialMany2ones",
supportedFieldTypes: ['selection', 'many2one'],
events: _.extend({}, AbstractField.prototype.events, {
@@ -1880,14 +1898,12 @@ var FieldRadio = FieldSelection.extend({
*/
init: function () {
this._super.apply(this, arguments);
- if (this.field.type === 'selection') {
- this.values = this.field.selection || [];
- } else if (this.field.type === 'many2one') {
- this.values = _.map(this.record.specialData[this.name], function (val) {
- return [val.id, val.display_name];
- });
+ if (this.mode === 'edit') {
+ this.tagName = 'div';
+ this.className += this.nodeOptions.horizontal ? 'o_horizontal' : 'o_vertical';
}
this.unique_id = _.uniqueId("radio");
+ this._setValues();
},
//--------------------------------------------------------------------------
@@ -1911,17 +1927,45 @@ var FieldRadio = FieldSelection.extend({
* @override
*/
_renderEdit: function () {
- var value;
+ var self = this;
+ var currentValue;
if (this.field.type === 'many2one') {
- value = this.value && this.value.data.id;
+ currentValue = this.value && this.value.data.id;
} else {
- value = this.value;
+ currentValue = this.value;
}
- var index = _.findIndex(this.values, function (option) {
- return option[0] === value;
+ this.$el.empty();
+ _.each(this.values, function (value, index) {
+ self.$el.append(qweb.render('FieldRadio.button', {
+ checked: value[0] === currentValue,
+ id: self.unique_id + '_' + value[0],
+ index: index,
+ value: value,
+ }));
});
- this.$("input").prop("checked", false);
- this.$('input[data-index="' + index + '"]').prop('checked', true);
+ },
+ /**
+ * @override
+ */
+ _reset: function () {
+ this._super.apply(this, arguments);
+ this._setValues();
+ },
+ /**
+ * Sets the possible field values. If the field is a many2one, those values
+ * may change during the lifecycle of the widget if the domain change (an
+ * onchange may change the domain).
+ *
+ * @private
+ */
+ _setValues: function () {
+ if (this.field.type === 'selection') {
+ this.values = this.field.selection || [];
+ } else if (this.field.type === 'many2one') {
+ this.values = _.map(this.record.specialData[this.name], function (val) {
+ return [val.id, val.display_name];
+ });
+ }
},
//--------------------------------------------------------------------------
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 69c6c13f9a7..01b2a18091a 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -1791,7 +1791,9 @@ var BasicModel = AbstractModel.extend({
var context = record.getContext({fieldName: fieldName});
var domain = record.getDomain({fieldName: fieldName});
if (domain.length) {
- var localID = record._changes && record._changes[fieldName] || record.data[fieldName];
+ var localID = (record._changes && fieldName in record._changes) ?
+ record._changes[fieldName] :
+ record.data[fieldName];
if (localID) {
var element = this.localData[localID];
domain = ["|", ["id", "=", element.data.id]].concat(domain);
@@ -2994,6 +2996,12 @@ var BasicModel = AbstractModel.extend({
});
record._warning = true;
}
+ if (result.domain) {
+ var fieldsInfo = record.fieldsInfo[viewType || record.viewType];
+ for (var fieldName in result.domain) {
+ fieldsInfo[fieldName].domain = result.domain[fieldName];
+ }
+ }
return self._applyOnChange(result.value, record).then(function () {
return result;
});
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index bcc832c822c..06e6b8b2285 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -724,15 +724,11 @@
t-att-autofocus="widget.attrs.autofocus"
t-att-id="widget.idForLabel"/>
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js
index 03a39ca1d1e..3c738812fb1 100644
--- a/addons/web/static/tests/fields/relational_fields_tests.js
+++ b/addons/web/static/tests/fields/relational_fields_tests.js
@@ -1250,6 +1250,55 @@ QUnit.module('relational_fields', {
});
});
+ QUnit.test('many2one: domain updated by an onchange', function (assert) {
+ assert.expect(2);
+
+ this.data.partner.onchanges = {
+ int_field: function () {},
+ };
+
+ var domain = [];
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ res_id: 1,
+ mockRPC: function (route, args) {
+ if (args.method === 'onchange') {
+ domain = [['id', 'in', [10]]];
+ return $.when({
+ domain: {
+ trululu: domain,
+ }
+ });
+ }
+ if (args.method === 'name_search') {
+ assert.deepEqual(args.kwargs.args, domain,
+ "sent domain should be correct");
+ }
+ return this._super(route, args);
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ // trigger a name_search (domain should be [])
+ form.$('.o_field_widget[name=trululu] input').click();
+ // close the dropdown
+ form.$('.o_field_widget[name=trululu] input').click();
+ // trigger an onchange that will update the domain
+ form.$('.o_field_widget[name=int_field]').val(2).trigger('input');
+ // trigger a name_search (domain should be [['id', 'in', [10]]])
+ form.$('.o_field_widget[name=trululu] input').click();
+
+ form.destroy();
+ });
+
QUnit.module('FieldOne2Many');
QUnit.test('one2many basic properties', function (assert) {
@@ -5884,6 +5933,55 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('widget selection on a many2one: domain updated by an onchange', function (assert) {
+ assert.expect(4);
+
+ this.data.partner.onchanges = {
+ int_field: function () {},
+ };
+
+ var domain = [];
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ res_id: 1,
+ mockRPC: function (route, args) {
+ if (args.method === 'onchange') {
+ domain = [['id', 'in', [10]]];
+ return $.when({
+ domain: {
+ trululu: domain,
+ }
+ });
+ }
+ if (args.method === 'name_search') {
+ assert.deepEqual(args.args[1], domain,
+ "sent domain should be correct");
+ }
+ return this._super(route, args);
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ assert.strictEqual(form.$('.o_field_widget[name=trululu] option').length, 4,
+ "should be 4 options in the selection");
+
+ // trigger an onchange that will update the domain
+ form.$('.o_field_widget[name=int_field]').val(2).trigger('input');
+
+ assert.strictEqual(form.$('.o_field_widget[name=trululu] option').length, 1,
+ "should be 1 option in the selection");
+
+ form.destroy();
+ });
+
QUnit.module('FieldMany2ManyTags');
QUnit.test('fieldmany2many tags: rendering and edition', function (assert) {
@@ -6298,6 +6396,58 @@ QUnit.module('relational_fields', {
form.destroy();
});
+ QUnit.test('widget radio on a many2one: domain updated by an onchange', function (assert) {
+ assert.expect(4);
+
+ this.data.partner.onchanges = {
+ int_field: function () {},
+ };
+
+ var domain = [];
+ var form = createView({
+ View: FormView,
+ model: 'partner',
+ data: this.data,
+ arch: '',
+ res_id: 1,
+ mockRPC: function (route, args) {
+ if (args.method === 'onchange') {
+ domain = [['id', 'in', [10]]];
+ return $.when({
+ value: {
+ trululu: false,
+ },
+ domain: {
+ trululu: domain,
+ },
+ });
+ }
+ if (args.method === 'search_read') {
+ assert.deepEqual(args.kwargs.domain, domain,
+ "sent domain should be correct");
+ }
+ return this._super(route, args);
+ },
+ viewOptions: {
+ mode: 'edit',
+ },
+ });
+
+ assert.strictEqual(form.$('.o_field_widget[name=trululu] .o_radio_item').length, 3,
+ "should be 3 radio buttons");
+
+ // trigger an onchange that will update the domain
+ form.$('.o_field_widget[name=int_field]').val(2).trigger('input');
+
+ assert.strictEqual(form.$('.o_field_widget[name=trululu] .o_radio_item').length, 0,
+ "should be no more radio button");
+
+ form.destroy();
+ });
+
QUnit.module('FieldMany2ManyCheckBoxes');
QUnit.test('widget many2many_checkboxes', function (assert) {