diff --git a/addons/sale_product_matrix/static/tests/section_and_note_widget_tests.js b/addons/sale_product_matrix/static/tests/section_and_note_widget_tests.js
new file mode 100644
index 0000000000000000000000000000000000000000..708102ec835b110f5e0d56992acb1cc2b6507228
--- /dev/null
+++ b/addons/sale_product_matrix/static/tests/section_and_note_widget_tests.js
@@ -0,0 +1,106 @@
+odoo.define('product_matrix.section_and_note_widget_tests', function (require) {
+"use strict";
+
+var FormView = require('web.FormView');
+var testUtils = require('web.test_utils');
+var createView = testUtils.createView;
+
+QUnit.module('section_and_note: product_matrix', {
+    beforeEach: function () {
+        this.data = {
+            sale_order: {
+                fields: {
+                    order_line_ids: {
+                        string: "Lines",
+                        type: 'one2many',
+                        relation: 'order_line',
+                        relation_field: 'order_id',
+                    },
+                    grid: {string: "Grid", type: 'char'},
+                },
+            },
+            order_line: {
+                fields: {
+                    order_id: {string: "Invoice", type: 'many2one', relation: 'invoice'},
+                    product_template_id: {string: "Product", type: 'many2one', relation: 'product'},
+                },
+            },
+            product: {
+                fields: {
+                    name: {string: "Name", type: 'char'},
+                },
+                records: [
+                    {id: 1, name: 'A configurable product'},
+                ],
+            },
+        };
+
+        this.grid = JSON.stringify({
+            header: [{name: "My Company Tshirt (GRID)"}, {name: "M"}, {name: "L"}],
+            matrix: [[
+                {name: "Men"},
+                {ptav_ids: [10, 13], qty: 0, is_possible_combination: true},
+                {ptav_ids: [11, 13], qty: 0, is_possible_combination: true},
+            ], [
+                {name: "Women"},
+                {ptav_ids: [10, 14], qty: 0, is_possible_combination: true},
+                {ptav_ids: [11, 14], qty: 0, is_possible_combination: true},
+            ]],
+        });
+    },
+}, function () {
+    QUnit.test('can configure a product with the matrix', async function (assert) {
+        assert.expect(4);
+
+        this.data.sale_order.onchanges = {
+            order_line_ids: obj => {
+                obj.grid = this.grid;
+            },
+            grid: () => {},
+        };
+        var form = await createView({
+            View: FormView,
+            model: 'sale_order',
+            data: this.data,
+            arch: `<form>
+                    <field name="grid" invisible="1"/>
+                    <field name="order_line_ids" widget="section_and_note_one2many">
+                        <tree editable="bottom">
+                            <field name="product_template_id" widget="product_configurator"/>
+                        </tree>
+                    </field>
+                </form>`,
+            mockRPC: function (route, args) {
+                if (args.method === 'onchange' && args.args[2] === 'grid') {
+                    // should trigger an onchange on the grid field and let the
+                    // business logic create rows according to the matrix content
+                    assert.deepEqual(args.args[1].grid, JSON.stringify({
+                        changes: [{qty: 2, ptav_ids: [10, 13]}, {qty: 3, ptav_ids: [11, 14]}],
+                        product_template_id: 1,
+                    }));
+                }
+                if (args.method === 'get_single_product_variant') {
+                    assert.strictEqual(args.args[0], 1);
+                    return Promise.resolve({mode: 'matrix'});
+                }
+                return this._super.apply(this, arguments);
+            },
+        });
+
+        await testUtils.dom.click('.o_field_x2many_list_row_add a');
+        await testUtils.fields.many2one.searchAndClickItem("product_template_id", {item: 'configurable'});
+
+        assert.containsOnce(document.body, '.modal .o_product_variant_matrix');
+        const $matrix = $('.modal .o_product_variant_matrix');
+        assert.strictEqual($matrix.text().replace(/[\n\r\s\u00a0]+/g, ' '),
+            ' My Company Tshirt (GRID) M L Men Women ');
+
+        // select 2 M-Men and 3 L-Women
+        await testUtils.fields.editInput($matrix.find('.o_matrix_input[ptav_ids="10,13"]'), '2');
+        await testUtils.fields.editInput($matrix.find('.o_matrix_input[ptav_ids="11,14"]'), '3');
+        await testUtils.dom.click($('.modal .modal-footer .btn-primary'));
+
+        form.destroy();
+    });
+});
+});
diff --git a/addons/sale_product_matrix/views/assets.xml b/addons/sale_product_matrix/views/assets.xml
index 780fb0f3f83c237c702cf5b46f54487bd7ff8aa0..e3858d3b053b840f114968fd933c0e767e27a123 100644
--- a/addons/sale_product_matrix/views/assets.xml
+++ b/addons/sale_product_matrix/views/assets.xml
@@ -5,4 +5,10 @@
             <script type="text/javascript" src="/sale_product_matrix/static/src/js/product_matrix_configurator.js"/>
         </xpath>
     </template>
+
+    <template id="qunit_suite" name="sale_product_matrix tests" inherit_id="web.qunit_suite">
+        <xpath expr="//t[@t-set='head']" position="inside">
+            <script type="text/javascript" src="/sale_product_matrix/static/tests/section_and_note_widget_tests.js"></script>
+        </xpath>
+    </template>
 </odoo>
diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js
index 6134bada68177297759bf0a0b5387126b07ed223..2a39742b206fe4315196a057002a6e823e335ead 100644
--- a/addons/web/static/src/js/views/form/form_controller.js
+++ b/addons/web/static/src/js/views/form/form_controller.js
@@ -288,8 +288,9 @@ var FormController = BasicController.extend({
      * @override
      */
     _applyChanges: async function () {
-        await this._super.apply(this, arguments);
+        const result = await this._super.apply(this, arguments);
         core.bus.trigger('DOM_updated');
+        return result;
     },
 
     /**