From 25aaf7afcff9b361d809c112bc8e93c91c357519 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 20 Mar 2020 11:14:25 +0000 Subject: [PATCH] [FIX] sale,*: product_configurator: can drag&drop row *sale_product_configurator Since commit 50bf830, a crash occurred when a row containing a many2one displayed with widget 'product_configurator' was dragged and dropped. For instance, open a quotation with at least two rows, and drag and drop one of them. Issue reported on task 2206228 closes odoo/odoo#48128 X-original-commit: d9c35b40b7df588f9e81142a2b272d9c7da06e04 Signed-off-by: Lucas Perais (lpe) Signed-off-by: Aaron Bohy (aab) --- .../src/js/product_configurator_widget.js | 2 +- .../tests/product_configurator_tests.js | 100 ++++++++++++++++++ addons/sale/views/assets.xml | 1 + .../src/js/product_configurator_widget.js | 2 +- .../static/tests/product_configurator.test.js | 53 +++++++++- 5 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 addons/sale/static/tests/product_configurator_tests.js diff --git a/addons/sale/static/src/js/product_configurator_widget.js b/addons/sale/static/src/js/product_configurator_widget.js index 5aab1cc988f..599cc4790cf 100644 --- a/addons/sale/static/src/js/product_configurator_widget.js +++ b/addons/sale/static/src/js/product_configurator_widget.js @@ -116,7 +116,7 @@ var ProductConfiguratorWidget = relationalFields.FieldMany2One.extend({ */ reset: async function (record, ev) { await this._super(...arguments); - if (ev.target === this) { + if (ev && ev.target === this) { if (ev.data.changes && !ev.data.preventProductIdCheck && ev.data.changes.product_template_id) { this._onTemplateChange(record.data.product_template_id.data.id, ev.data.dataPointID); } else if (ev.data.changes && ev.data.changes.product_id) { diff --git a/addons/sale/static/tests/product_configurator_tests.js b/addons/sale/static/tests/product_configurator_tests.js new file mode 100644 index 00000000000..eaed84889cd --- /dev/null +++ b/addons/sale/static/tests/product_configurator_tests.js @@ -0,0 +1,100 @@ +odoo.define('sale.product_configurator_tests', function (require) { + "use strict"; + + const FormView = require('web.FormView'); + const testUtils = require('web.test_utils'); + + const createView = testUtils.createView; + + QUnit.module('product_configurator', { + beforeEach: function () { + this.data = { + order: { + fields: { + line_ids: { + string: "Lines", + type: 'one2many', + relation: 'line', + relation_field: 'order_id' + }, + }, + records: [ + {id: 1, line_ids: [1, 2]}, + ], + }, + line: { + fields: { + product_id: { + string: "Product", + type: 'many2one', + relation: 'product', + }, + order_id: { + string: "Order", + type: 'many2one', + relation: 'order' + }, + sequence: { + string: "Sequence", + type: 'number', + }, + }, + records: [ + {id: 1, sequence: 4, product_id: 3, order_id: 1}, + {id: 2, sequence: 14, product_id: 4, order_id: 1}, + ] + }, + product: { + fields: { + name: { + string: "Name", + type: 'char', + }, + }, + records: [ + {id: 3, name: "Chair"}, + {id: 4, name: "Table"}, + ], + }, + }; + }, + }, function () { + QUnit.test('drag and drop rows containing product_configurator many2one', async function (assert) { + assert.expect(4); + + const form = await createView({ + View: FormView, + model: 'order', + data: this.data, + arch: ` +
+ + `, + archs: { + 'line,false,list': ` + + + + `, + }, + res_id: 1, + viewOptions: { + mode: 'edit', + }, + }); + + assert.containsN(form, '.o_data_row', 2); + assert.strictEqual(form.$('.o_data_row').text(), 'ChairTable'); + assert.containsN(form, '.o_data_row .o_row_handle', 2); + + // move first row below second + const $firstHandle = form.$('.o_data_row:nth(0) .o_row_handle'); + const $secondHandle = form.$('.o_data_row:nth(1) .o_row_handle'); + await testUtils.dom.dragAndDrop($firstHandle, $secondHandle); + + assert.strictEqual(form.$('.o_data_row').text(), 'TableChair'); + + form.destroy(); + }); + }); +}); diff --git a/addons/sale/views/assets.xml b/addons/sale/views/assets.xml index 1733dab1a50..e5fac59fb2e 100644 --- a/addons/sale/views/assets.xml +++ b/addons/sale/views/assets.xml @@ -31,6 +31,7 @@ diff --git a/addons/sale_product_configurator/static/src/js/product_configurator_widget.js b/addons/sale_product_configurator/static/src/js/product_configurator_widget.js index 48f15bbe63e..7769a72fc90 100644 --- a/addons/sale_product_configurator/static/src/js/product_configurator_widget.js +++ b/addons/sale_product_configurator/static/src/js/product_configurator_widget.js @@ -34,7 +34,7 @@ ProductConfiguratorWidget.include({ * @private */ reset: function (record, ev) { - if (ev.target === this) { + if (ev && ev.target === this) { this.restoreProductTemplateId = this.recordData.product_template_id; this.optionalProducts = (ev.data && ev.data.optionalProducts) || this.optionalProducts; } diff --git a/addons/sale_product_configurator/static/tests/product_configurator.test.js b/addons/sale_product_configurator/static/tests/product_configurator.test.js index 757e2f52743..2d737820c43 100644 --- a/addons/sale_product_configurator/static/tests/product_configurator.test.js +++ b/addons/sale_product_configurator/static/tests/product_configurator.test.js @@ -82,7 +82,8 @@ QUnit.module('Product Configurator', { type: 'one2many', relation: 'product_custom_attribute_value' }, - product_uom_qty: {type: 'integer'} + product_uom_qty: {type: 'integer'}, + sequence: {type: 'integer'}, } }, product_custom_attribute_value: { @@ -136,7 +137,7 @@ QUnit.module('Product Configurator', { } }; } -}, function (){ +}, function () { QUnit.test('Select a non configurable product template and verify that the product_id is correctly set', async function (assert) { assert.expect(2); @@ -225,7 +226,7 @@ QUnit.module('Product Configurator', { form.destroy(); }); - QUnit.test('Select a product in the list and check for template loading', async function (assert){ + QUnit.test('Select a product in the list and check for template loading', async function (assert) { assert.expect(1); var product_configurator_form = await createView({ @@ -257,6 +258,52 @@ QUnit.module('Product Configurator', { await testUtils.dom.click($("ul.ui-autocomplete li a:contains('Customizable Desk')").mouseenter()); product_configurator_form.destroy(); }); + + QUnit.test('drag and drop rows containing product_configurator many2one', async function (assert) { + assert.expect(4); + + this.data.sale_order.records = [ + { id: 1, sale_order_line: [1, 2] } + ]; + this.data.sale_order_line.records = [ + { id: 1, sequence: 5, product_id: 1 }, + { id: 2, sequence: 15, product_id: 2 }, + ]; + + const form = await createView({ + View: FormView, + model: 'sale_order', + data: this.data, + arch: ` +
+ + `, + archs: { + 'sale_order_line,false,list': ` + + + + `, + }, + res_id: 1, + viewOptions: { + mode: 'edit', + }, + }); + + assert.containsN(form, '.o_data_row', 2); + assert.strictEqual(form.$('.o_data_row').text(), 'Customizable Desk (1)Customizable Desk (2)'); + assert.containsN(form, '.o_data_row .o_row_handle', 2); + + // move first row below second + const $firstHandle = form.$('.o_data_row:nth(0) .o_row_handle'); + const $secondHandle = form.$('.o_data_row:nth(1) .o_row_handle'); + await testUtils.dom.dragAndDrop($firstHandle, $secondHandle); + + assert.strictEqual(form.$('.o_data_row').text(), 'Customizable Desk (2)Customizable Desk (1)'); + + form.destroy(); + }); }); });