From f58f17762643a211e77227a326b5fc3a721a2120 Mon Sep 17 00:00:00 2001 From: Julien Mougenot Date: Tue, 17 Sep 2019 12:31:22 +0000 Subject: [IMP] web: added increments to the minutes in daterange Minutes can now be chosen by increments of 5 instead of 10. Task 2068164 --- addons/web/static/tests/fields/basic_fields_tests.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'addons/web/static/tests/fields/basic_fields_tests.js') diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index aa86ff925e4..63ce02be614 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -2636,7 +2636,7 @@ QUnit.module('basic_fields', { QUnit.module('FieldDateRange'); QUnit.test('Datetime field', async function (assert) { - assert.expect(19); + assert.expect(20); this.data.partner.fields.datetime_end = {string: 'Datetime End', type: 'datetime'}; this.data.partner.records[0].datetime_end = '2017-03-13 00:00:00'; @@ -2685,6 +2685,8 @@ QUnit.module('basic_fields', { "active end date hour should be '5' in date range picker"); assert.strictEqual($('.daterangepicker:first .drp-calendar.right .minuteselect').val(), '30', "active end date minute should be '30' in date range picker"); + assert.containsN($('.daterangepicker:first .drp-calendar.left .minuteselect'), 'option', 12, + "minute selection should contain 12 options (1 for each 5 minutes)"); // Close picker await testUtils.dom.click($('.daterangepicker:first .cancelBtn')); -- cgit v1.2.3 From fd3c3821861dcfecdd63079c799863002fd67d71 Mon Sep 17 00:00:00 2001 From: Julien Mougenot Date: Mon, 16 Sep 2019 11:49:00 +0000 Subject: [FIX] web: field color: adapted for views The FieldColor widget had been designed exclusively for the document layout configurator. Because of this, the widget would not display/behave correctly in other views. Before this commit: - in list views, the widget had a "white-space" attribute adapted for char fields, which gave it much more space than it needed - there was no handling of the widget in readonly state: it was always editable - its size in list views was a bit too large Now: - all styling problems have been adjusted for list views (unchanged for forms) - the widget is disabled when in readonly mode Task 2072466 closes odoo/odoo#36949 Signed-off-by: VincentSchippefilt --- .../web/static/tests/fields/basic_fields_tests.js | 42 +++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) (limited to 'addons/web/static/tests/fields/basic_fields_tests.js') diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index 63ce02be614..1d91f581317 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -6442,7 +6442,7 @@ QUnit.module('basic_fields', { }); QUnit.test('Field Color: default widget state', async function (assert) { - assert.expect(3); + assert.expect(4); var form = await createView({ View: FormView, @@ -6467,6 +6467,46 @@ QUnit.module('basic_fields', { await testUtils.dom.click($('.modal .btn:contains("Discard")')); + assert.strictEqual(document.activeElement, form.$('.o_field_color')[0], + "Focus should go back to the color field"); + + form.destroy(); + }); + + QUnit.test('Field Color: behaviour in different views', async function (assert) { + assert.expect(2); + + this.data.partner.records[0].p = [4, 2]; + this.data.partner.records[1].hex_color = '#ff0080'; + + const form = await createView({ + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '', + data: this.data, + model: 'partner', + res_id: 1, + View: FormView, + }); + + await testUtils.dom.click(form.$('.o_field_color:first()')); + assert.containsNone($(document.body), '.modal', + "Color field in readonly shouldn't be editable"); + + const rowInitialHeight = form.$('.o_data_row:first()').height(); + + await testUtils.form.clickEdit(form); + await testUtils.dom.click(form.$('.o_data_row:first() .o_data_cell:first()')); + + assert.strictEqual(rowInitialHeight, form.$('.o_data_row:first()').height(), + "Color field shouldn't change the color height when edited"); + form.destroy(); }); -- cgit v1.2.3 From ac85176ce81427ba6f55b59e28c1ff6e7e080c32 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Thu, 12 Sep 2019 08:05:59 +0000 Subject: [FIX] web: date(time) fields: trigger fieldChanged once Before this rev., the 'fieldChanged' event was triggered each time the user selected a value (a day, an hour, a minute or a second) in the datepicker. This means that if there was an onchange on the field, a lot of RPCs could have been done when a user set a datetime. In addition, in a list view with multi edition, the user was asked to save the changes at each step of the datetime selection, making the feature unusable. Task 2068280 --- .../web/static/tests/fields/basic_fields_tests.js | 57 ++++++++++++++++++++++ 1 file changed, 57 insertions(+) (limited to 'addons/web/static/tests/fields/basic_fields_tests.js') diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index 1a93fce5d4c..c6759aff971 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -3066,6 +3066,63 @@ QUnit.module('basic_fields', { form.destroy(); }); + QUnit.test('datetime fields do not trigger fieldChange before datetime completly picked', async function (assert) { + assert.expect(6); + + this.data.partner.onchanges = { + datetime: function () {}, + }; + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
', + res_id: 1, + translateParameters: { // Avoid issues due to localization formats + date_format: '%m/%d/%Y', + time_format: '%H:%M:%S', + }, + session: { + getTZOffset: function () { + return 120; + }, + }, + mockRPC: function (route, args) { + if (args.method === 'onchange') { + assert.step('onchange'); + } + return this._super.apply(this, arguments); + }, + viewOptions: { + mode: 'edit', + }, + }); + + + testUtils.dom.openDatepicker(form.$('.o_datepicker')); + assert.containsOnce($('body'), '.bootstrap-datetimepicker-widget'); + + // select a date and time + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch').first()); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .picker-switch:eq(1)')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .year:contains(2017)')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .month').eq(3)); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .day:contains(22)')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .fa-clock-o')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker-hour')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .hour:contains(08)')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker-minute')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .minute:contains(25)')); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .timepicker-second')); + assert.verifySteps([], "should not have done any onchange yet"); + await testUtils.dom.click($('.bootstrap-datetimepicker-widget .second:contains(35)')); + + assert.containsNone($('body'), '.bootstrap-datetimepicker-widget'); + assert.strictEqual(form.$('.o_datepicker_input').val(), "04/22/2017 08:25:35"); + assert.verifySteps(['onchange'], "should have done only one onchange"); + + form.destroy(); + }); QUnit.test('datetime field not visible in form view should not capture the focus on keyboard navigation', async function (assert) { assert.expect(1); -- cgit v1.2.3 From af65475a4cd33ca8c6b25ba525d2f59cc28b3f43 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Thu, 12 Sep 2019 08:31:05 +0000 Subject: [FIX] web: list: multi edit input of date(time) fields The Date(Time)Fields listen to 'input' and 'change' events (like all InputFields) to keep track of changes. However, the datepicker already listens to the 'change' event. As a consequence, in multi edition in a list view, if the user changed the value of a date(time) field by directly editing the input, the fieldChanged event was triggered twice, and there were 2 dialogs asking the user if he wanted to save the change. This rev. makes the Date(Time)Fields stop listening themselves to 'input' and 'change' events, and instead completely rely on the datepicker widget. Task 2068280 --- .../web/static/tests/fields/basic_fields_tests.js | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'addons/web/static/tests/fields/basic_fields_tests.js') diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index c6759aff971..37f7b31fe12 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -3245,6 +3245,45 @@ QUnit.module('basic_fields', { list.destroy(); }); + QUnit.test('multi edition of datetime field in list view: edit date in input', async function (assert) { + assert.expect(4); + + var list = await createView({ + View: ListView, + model: 'partner', + data: this.data, + arch: '' + + '' + + '', + translateParameters: { // Avoid issues due to localization formats + date_format: '%m/%d/%Y', + time_format: '%H:%M:%S', + }, + session: { + getTZOffset: function () { + return 120; + }, + }, + }); + + // select two records and edit them + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); + await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_list_record_selector input')); + + await testUtils.dom.click(list.$('.o_data_row:first .o_data_cell')); + assert.containsOnce(list, 'input.o_datepicker_input'); + list.$('.o_datepicker_input').val("10/02/2019 09:00:00"); + await testUtils.dom.triggerEvents(list.$('.o_datepicker_input'), ['change']); + + assert.containsOnce(document.body, '.modal'); + await testUtils.dom.click($('.modal .modal-footer .btn-primary')); + + assert.strictEqual(list.$('.o_data_row:first .o_data_cell').text(), "10/02/2019 09:00:00"); + assert.strictEqual(list.$('.o_data_row:nth(1) .o_data_cell').text(), "10/02/2019 09:00:00"); + + list.destroy(); + }); + QUnit.test('datetime field remove value', async function (assert) { assert.expect(4); -- cgit v1.2.3 From 3ee3f7d497328dd09b0bc07349872f1b03bbe8e5 Mon Sep 17 00:00:00 2001 From: "Andrea Grazioso (agr-odoo)" Date: Tue, 17 Sep 2019 11:48:11 +0000 Subject: [FIX] web: digits option not applied in list view From the settings activate: - Multiple Sales Prices per Product -- Prices computed from formulas - Discounts Create a pricelist with discount policy "Show public price & discount to the customer", set the price calculation formula: - Rounding: 10 - Price discount: 10% Modify the sale order xml list view adding 'digits="[3, 2]"' to the discount shown in the sale order line. Create a sale order with such pricelist and on the so line put a product with quantity 1 and sale price 4190 (VAT 19% included in the price). The discount will not display the requested number of decimal digits (2) in list view, but it will when editing the field. The cell formatting is done calling the appropriate function (formatFloat), which search the digits option from the 'options' parameter or from the field definition. The latter stores the default digits coming from the DecimalAccuracy settings, which, as side effect, alter the behavior of the discount calculation. https://www.odoo.com/web#view_type=form&model=project.task&id=2069939&active_id=2069939 When passing into edit mode the field is created as widget, parsing its options which have 'digits', instead when coming back in list mode it is "just a float field" rendered directly from the list_renderer which no options, so the formatFloat will look at the field definition https://github.com/odoo/odoo/blob/12.0/addons/web/static/src/js/fields/field_utils.js#L172 Adding the correct parameter to the dictionary passed to formatFloat during the list rendering is a possible solution, because it is the "closest" point in which parsing the parameter is possible without touching higher level code or forcing the creation of a widget (adding widget='float' as xml parameter). closes odoo/odoo#37167 Signed-off-by: Lucas Perais (lpe) --- .../web/static/tests/fields/basic_fields_tests.js | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'addons/web/static/tests/fields/basic_fields_tests.js') diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index 37f7b31fe12..94d7a5097d3 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -529,6 +529,42 @@ QUnit.module('basic_fields', { form.destroy(); }); + QUnit.test('float field in list view no widget', async function (assert) { + assert.expect(5); + + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch:'
' + + '' + + '' + + '' + + '
', + res_id: 2, + }); + + assert.doesNotHaveClass(form.$('.o_field_widget'), 'o_field_empty', + 'Float field should be considered set for value 0.'); + assert.strictEqual(form.$('.o_field_widget').first().text(), '0.000', + 'The value should be displayed properly.'); + + await testUtils.form.clickEdit(form); + assert.strictEqual(form.$('input[name=qux]').val(), '0.000', + 'The value should be rendered with correct precision.'); + + await testUtils.fields.editInput(form.$('input[name=qux]'), '108.2458938598598'); + assert.strictEqual(form.$('input[name=qux]').val(), '108.2458938598598', + 'The value should not be formated yet.'); + + await testUtils.fields.editInput(form.$('input[name=qux]'), '18.8958938598598'); + await testUtils.form.clickSave(form); + assert.strictEqual(form.$('.o_field_widget').first().text(), '18.896', + 'The new value should be rounded properly.'); + + form.destroy(); + }); + QUnit.test('float field in form view', async function (assert) { assert.expect(5); -- cgit v1.2.3 From 2471ee506f38f5ca54d7bd54084b0ac3d74abeae Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 20 Sep 2019 12:46:07 +0000 Subject: [FIX] web: translate html fields This commit fixes: - body of a email.template in a small field - translation of website_description of a product does not show the source of the translation - translation of website_description of a product shows the full content for the current language HTML fields are typically fields with longer of content. If the field is an HTML field, it should be displayed in a textarea, like the text fields. Most of HTML fields are split in meaningful chunks of content (using xml_translate or html_translate method). For these fields, the source must be displayed. Also sort the entries on the source to have a consistent order closes odoo/odoo#37215 Signed-off-by: Martin Trigaux (mat) --- .../web/static/tests/fields/basic_fields_tests.js | 129 ++++++++++++++++++++- 1 file changed, 127 insertions(+), 2 deletions(-) (limited to 'addons/web/static/tests/fields/basic_fields_tests.js') diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index e8db402ec49..5b5b5196e90 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -1109,7 +1109,7 @@ QUnit.module('basic_fields', { }); QUnit.test('char field translatable', async function (assert) { - assert.expect(4); + assert.expect(12); this.data.partner.fields.foo.translate = true; @@ -1128,6 +1128,9 @@ QUnit.module('basic_fields', { '' + '', res_id: 1, + session: { + user_context: {lang: 'en_US'}, + }, mockRPC: function (route, args) { if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { assert.deepEqual(args.args, ["partner",1,"foo"], 'should call "call_button" route'); @@ -1137,7 +1140,18 @@ QUnit.module('basic_fields', { }); } if (route === "/web/dataset/call_kw/res.lang/get_installed") { - return Promise.resolve([["en_US"], ["fr_BE"]]); + return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]); + } + if (args.method === "search_read" && args.model == "ir.translation") { + return Promise.resolve([ + {lang: 'en_US', src: 'yop', value: 'yop', id: 42}, + {lang: 'fr_BE', src: 'yop', value: 'valeur français', id: 43} + ]); + } + if (args.method === "write" && args.model == "ir.translation") { + assert.deepEqual(args.args[1], {value: "english value"}, + "the new translation value should be written"); + return Promise.resolve(); } return this._super.apply(this, arguments); }, @@ -1150,6 +1164,117 @@ QUnit.module('basic_fields', { await testUtils.nextTick(); assert.containsOnce($(document), '.modal', 'a translate modal should be visible'); + assert.containsN($('.modal .o_translation_dialog'), '.translation', 2, + 'two rows should be visible'); + + var $enField = $('.modal .o_translation_dialog .translation:first() input'); + assert.strictEqual($enField.val(), 'yop', + 'English translation should be filled'); + assert.strictEqual($('.modal .o_translation_dialog .translation:last() input').val(), 'valeur français', + 'French translation should be filled'); + + await testUtils.fields.editInput($enField, "english value"); + await testUtils.dom.click($('.modal button.btn-primary')); // save + await testUtils.nextTick(); + + var $foo = form.$('input[type="text"].o_field_char'); + assert.strictEqual($foo.val(), "english value", + "the new translation was not transfered to modified record"); + + await testUtils.fields.editInput($foo, "new english value"); + + await testUtils.dom.click($button); + await testUtils.nextTick(); + + assert.strictEqual($('.modal .o_translation_dialog .translation:first() input').val(), 'new english value', + 'Modified value should be used instead of translation'); + assert.strictEqual($('.modal .o_translation_dialog .translation:last() input').val(), 'valeur français', + 'French translation should be filled'); + + form.destroy(); + + _t.database.multi_lang = multiLang; + }); + + QUnit.test('html field translatable', async function (assert) { + assert.expect(6); + + this.data.partner.fields.foo.translate = true; + + var multiLang = _t.database.multi_lang; + _t.database.multi_lang = true; + + var form = await createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + res_id: 1, + session: { + user_context: {lang: 'en_US'}, + }, + mockRPC: function (route, args) { + if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { + assert.deepEqual(args.args, ["partner",1,"foo"], 'should call "call_button" route'); + return Promise.resolve({ + domain: [], + context: { + search_default_name: 'partner,foo', + translation_type: 'char', + translation_show_src: true, + }, + }); + } + if (route === "/web/dataset/call_kw/res.lang/get_installed") { + return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]); + } + if (args.method === "search_read" && args.model == "ir.translation") { + return Promise.resolve([ + {lang: 'en_US', src: 'first paragraph', value: 'first paragraph', id: 42}, + {lang: 'en_US', src: 'second paragraph', value: 'second paragraph', id: 43}, + {lang: 'fr_BE', src: 'first paragraph', value: 'premier paragraphe', id: 44}, + {lang: 'fr_BE', src: 'second paragraph', value: 'deuxième paragraphe', id: 45}, + ]); + } + if (args.method === "write" && args.model == "ir.translation") { + assert.deepEqual(args.args[1], {value: "first paragraph modified"}, + "Wrong update on translation"); + return Promise.resolve(); + } + return this._super.apply(this, arguments); + }, + }); + await testUtils.form.clickEdit(form); + var $foo = form.$('input[type="text"].o_field_char'); + + // this will not affect the translate_fields effect until the record is + // saved but is set for consistency of the test + await testUtils.fields.editInput($foo, "

first paragraph

second paragraph

"); + + var $button = form.$('input[type="text"].o_field_char + .o_field_translate'); + await testUtils.dom.click($button); + await testUtils.nextTick(); + + assert.containsOnce($(document), '.modal', 'a translate modal should be visible'); + assert.containsN($('.modal .o_translation_dialog'), '.translation', 4, + 'four rows should be visible'); + + var $enField = $('.modal .o_translation_dialog .translation:first() input'); + assert.strictEqual($enField.val(), 'first paragraph', + 'first part of english translation should be filled'); + + await testUtils.fields.editInput($enField, "first paragraph modified"); + await testUtils.dom.click($('.modal button.btn-primary')); // save + await testUtils.nextTick(); + + assert.strictEqual($foo.val(), "

first paragraph

second paragraph

", + "the new partial translation should not be transfered"); form.destroy(); -- cgit v1.2.3 From e06117bd85c6b39dd62d4979b0c1c437b92dbafa Mon Sep 17 00:00:00 2001 From: Vincent Schippefilt Date: Fri, 27 Sep 2019 13:47:52 +0000 Subject: [FIX] web: multi-hell-ditable list only activate multi-edit on list with specific attribute. When the attribute multi_edit="1" is set on the tree view the user can select a/some records and it will activate the multi-edit with confirmation dialog (even for a single record). Also, on-change are not applied when using multi-edit closes odoo/odoo#37525 Signed-off-by: VincentSchippefilt --- addons/web/static/tests/fields/basic_fields_tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'addons/web/static/tests/fields/basic_fields_tests.js') diff --git a/addons/web/static/tests/fields/basic_fields_tests.js b/addons/web/static/tests/fields/basic_fields_tests.js index 5b5b5196e90..c4bfc8a4341 100644 --- a/addons/web/static/tests/fields/basic_fields_tests.js +++ b/addons/web/static/tests/fields/basic_fields_tests.js @@ -3711,7 +3711,7 @@ QUnit.module('basic_fields', { View: ListView, model: 'partner', data: this.data, - arch: '' + + arch: '' + '' + '', translateParameters: { // Avoid issues due to localization formats -- cgit v1.2.3