diff --git a/addons/web/static/src/legacy/js/fields/basic_fields.js b/addons/web/static/src/legacy/js/fields/basic_fields.js index 9e0b87c752f..5e79aec3d91 100644 --- a/addons/web/static/src/legacy/js/fields/basic_fields.js +++ b/addons/web/static/src/legacy/js/fields/basic_fields.js @@ -3795,7 +3795,7 @@ var FieldDomain = AbstractField.extend({ // we don't want to recompute the count if the domain has been edited // from the debug textarea (for performance reasons, as it might be costly) this.debugEdition = !!e.data.debug; - this._setValue(Domain.prototype.arrayToString(e.data.domain)); + this._setValue(e.data.domain); }, /** * Called when the in-dialog domain selector value is confirmed diff --git a/addons/web/static/src/legacy/js/widgets/domain_selector.js b/addons/web/static/src/legacy/js/widgets/domain_selector.js index 5f444d59e76..7994e476956 100644 --- a/addons/web/static/src/legacy/js/widgets/domain_selector.js +++ b/addons/web/static/src/legacy/js/widgets/domain_selector.js @@ -377,10 +377,11 @@ var DomainTree = DomainNode.extend({ */ _renderChildrenTo: function ($to) { var $div = $("
"); - return Promise.all(_.map(this.children, (function (child) { + const children = this.children; + return Promise.all(_.map(children, (function (child) { return child.appendTo($div); }).bind(this))).then((function () { - _.each(this.children, function (child) { + _.each(children, function (child) { child.$el.appendTo($to); // Forced to do it this way so that the // children are not misordered }); @@ -469,6 +470,12 @@ var DomainSelector = DomainTree.extend({ domain_changed: "_onDomainChange", }), + init(parent, model, domain) { + this._super(...arguments); + this.rawDomain = domain; + this._redrawId = 0; + }, + start: function () { var self = this; return this._super.apply(this, arguments).then(function () { @@ -541,7 +548,7 @@ var DomainSelector = DomainTree.extend({ // Display technical domain if in debug mode this.$debugInput = this.$(".o_domain_debug_input"); if (this.$debugInput.length) { - this.$debugInput.val(Domain.prototype.arrayToString(this.getDomain())); + this.$debugInput.val(this.rawDomain); dom.autoresize(this.$debugInput); } @@ -557,9 +564,13 @@ var DomainSelector = DomainTree.extend({ * @returns {Promise} */ _redraw: function (domain) { + const _redrawId = ++this._redrawId; var oldChildren = this.children.slice(); this._initialize(domain || this.getDomain()); return this._renderChildrenTo($("")).then((function () { + if (_redrawId !== this._redrawId) { + return; + } _.each(oldChildren, function (child) { child.destroy(); }); this.renderElement(); this._postRender(); @@ -589,14 +600,19 @@ var DomainSelector = DomainTree.extend({ // is syntax-valid a "domain_changed" event is triggered to notify the // parent, but the widget isn't redrawn. // If the domain is not valid, a warning is shown to the user. - var domain; + const rawDomain = e.currentTarget.value; try { - domain = Domain.prototype.stringToArray(e.currentTarget.value); + Domain.prototype.stringToArray(rawDomain); } catch (err) { // If there is a syntax error, just ignore the change this.displayNotification({ title: _t("Syntax error"), message: _t("Domain not properly formed"), type: 'danger' }); return; } - this.trigger_up("domain_changed", { child: this, noRedraw: true, domain, debug: true }); + this.trigger_up("domain_changed", { + child: this, + noRedraw: true, + domain: rawDomain, + debug: true, + }); }, /** * Called when a (child's) domain has changed -> redraw the entire tree @@ -607,6 +623,7 @@ var DomainSelector = DomainTree.extend({ _onDomainChange: function (e) { // Add the current domain to the payload if not already there e.data.domain = e.data.domain || this.getDomain(); + this.rawDomain = Domain.prototype.arrayToString(e.data.domain); // If a subdomain notifies that it underwent some modifications, the // DomainSelector catches the message and performs a full re-rendering. if (!e.data.noRedraw) { diff --git a/addons/web/static/tests/legacy/fields/basic_fields_tests.js b/addons/web/static/tests/legacy/fields/basic_fields_tests.js index 159f9885311..5b584c5fcc9 100644 --- a/addons/web/static/tests/legacy/fields/basic_fields_tests.js +++ b/addons/web/static/tests/legacy/fields/basic_fields_tests.js @@ -8056,6 +8056,55 @@ QUnit.module('basic_fields', { form.destroy(); }); + QUnit.test('domain field: edit domain with dynamic content', async function (assert) { + assert.expect(2); + + const originalDebug = odoo.debug; + odoo.debug = true; + let rawDomain = ` + [ + ["date", ">=", datetime.datetime.combine(context_today() + relativedelta(days = -365), datetime.time(0, 0, 0)).to_utc().strftime("%Y-%m-%d %H:%M:%S")] + ] + `; + this.data.partner.records[0].foo = rawDomain; + this.data.partner.fields.bar.type = "char"; + this.data.partner.records[0].bar = "partner"; + + const form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: ` + `, + async mockRPC(route, args) { + if (args.method === 'write') { + assert.strictEqual(args.args[1].foo, rawDomain); + } + return this._super.apply(this, arguments); + }, + res_id: 1, + viewOptions: { + mode: 'edit', + }, + }); + + assert.strictEqual(form.$(".o_domain_debug_input").val(), rawDomain); + + rawDomain = ` + [ + ["date", ">=", datetime.datetime.combine(context_today() + relativedelta(days = -1), datetime.time(0, 0, 0)).to_utc().strftime("%Y-%m-%d %H:%M:%S")] + ] + `; + await testUtils.fields.editAndTrigger(form.$('.o_domain_debug_input'), rawDomain, ["change"]); + await testUtils.form.clickSave(form); + + form.destroy(); + odoo.debug = originalDebug; + }); + QUnit.module('FieldProgressBar'); QUnit.test('Field ProgressBar: max_value should update', async function (assert) { diff --git a/addons/web/static/tests/legacy/widgets/domain_selector_tests.js b/addons/web/static/tests/legacy/widgets/domain_selector_tests.js index 7ce2c2372df..50e1fb85210 100644 --- a/addons/web/static/tests/legacy/widgets/domain_selector_tests.js +++ b/addons/web/static/tests/legacy/widgets/domain_selector_tests.js @@ -272,15 +272,13 @@ QUnit.module('DomainSelector', { assert.expect(5); const $target = $("#qunit-fixture"); + let newValue; // Create the domain selector and its mock environment const Parent = Widget.extend({ custom_events: { domain_changed: (e) => { - assert.deepEqual(e.data.domain, [ - ["product_id", "ilike", 1], - ["id", "=", 0] - ]); + assert.deepEqual(e.data.domain, newValue); assert.ok(e.data.debug); }, }, @@ -294,7 +292,7 @@ QUnit.module('DomainSelector', { await domainSelector.appendTo($target); assert.containsOnce(domainSelector, ".o_domain_node", "should have a single domain node"); - const newValue = ` + newValue = ` [ ['product_id', 'ilike', 1], ['id', '=', 0]