diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-04-09 20:48:01 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-04-09 20:48:01 +0200 |
| commit | c9e1deb496e97fca22991b788ad36f753ecb1fc5 (patch) | |
| tree | 78689bb9e5e029c99a2f1690e7e9d8e425734a26 | |
| parent | 6c8e30cae6ca5e4fe12e287ebd29d76544be57a3 (diff) | |
| parent | af313858b4bfcaa726c4d08057a1ff1db1ade480 (diff) | |
[MERGE] forward port branch saas-12.1 up to af313858b4b
19 files changed, 245 insertions, 28 deletions
diff --git a/addons/account/security/account_security.xml b/addons/account/security/account_security.xml index 69f3fca8bc8..b7ec99e56c0 100644 --- a/addons/account/security/account_security.xml +++ b/addons/account/security/account_security.xml @@ -20,18 +20,6 @@ <field name="category_id" ref="base.module_category_hidden"/> </record> - <record id="base.group_public" model="res.groups"> - <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/> - </record> - - <record id="base.group_portal" model="res.groups"> - <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/> - </record> - - <record id="base.group_user" model="res.groups"> - <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/> - </record> - <record id="group_account_user" model="res.groups"> <field name="name">Show Full Accounting Features</field> <field name="category_id" ref="base.module_category_hidden"/> @@ -75,6 +63,16 @@ <data noupdate="1"> + <record id="base.group_public" model="res.groups"> + <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/> + </record> + <record id="base.group_portal" model="res.groups"> + <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/> + </record> + <record id="base.group_user" model="res.groups"> + <field name="implied_ids" eval="[(4, ref('account.group_show_line_subtotals_tax_excluded'))]"/> + </record> + <record id="base.default_user" model="res.users"> <field name="groups_id" eval="[(4,ref('account.group_account_manager'))]"/> </record> diff --git a/addons/account/views/report_invoice.xml b/addons/account/views/report_invoice.xml index 043c5a3cc47..8c3da3ae101 100644 --- a/addons/account/views/report_invoice.xml +++ b/addons/account/views/report_invoice.xml @@ -51,12 +51,16 @@ <table class="table table-sm o_main_table" name="invoice_line_table"> <thead> <tr> + <!-- TODO: remove in master --> + <t t-set="colspan" t-value="6"/> <th class="text-left"><span>Description</span></th> <th class="d-none text-left"><span>Source Document</span></th> <th class="text-right"><span>Quantity</span></th> <th t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span>Unit Price</span></th> <th t-if="display_discount" t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"> <span>Disc.(%)</span> + <!-- TODO: remove in master --> + <t t-set="colspan" t-value="colspan+1"/> </th> <th t-attf-class="text-left {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}"><span>Taxes</span></th> <th class="text-right"> diff --git a/addons/board/static/src/scss/dashboard.scss b/addons/board/static/src/scss/dashboard.scss index d5d95fa9f59..518849cde0a 100644 --- a/addons/board/static/src/scss/dashboard.scss +++ b/addons/board/static/src/scss/dashboard.scss @@ -123,8 +123,8 @@ } // Override height of graph. min-height doesn't do the trick - .o_graph_controller { - height: 500px; + .o_graph_svg_container { + height: 250px; } } } diff --git a/addons/calendar/models/res_partner.py b/addons/calendar/models/res_partner.py index 403d70b18fc..261b955fc8f 100644 --- a/addons/calendar/models/res_partner.py +++ b/addons/calendar/models/res_partner.py @@ -11,7 +11,7 @@ from odoo.addons.calendar.models.calendar import get_real_ids class Partner(models.Model): _inherit = 'res.partner' - calendar_last_notif_ack = fields.Datetime('Last notification marked as read from base Calendar') + calendar_last_notif_ack = fields.Datetime('Last notification marked as read from base Calendar', default=fields.Datetime.now) @api.multi def get_attendee_detail(self, meeting_id): diff --git a/addons/mail/models/ir_attachment.py b/addons/mail/models/ir_attachment.py index 4330059c495..527b22409d6 100644 --- a/addons/mail/models/ir_attachment.py +++ b/addons/mail/models/ir_attachment.py @@ -13,9 +13,22 @@ class IrAttachment(models.Model): """ super(IrAttachment, self)._post_add_create() for record in self: - record.register_as_main_attachment() + record.register_as_main_attachment(force=False) - def register_as_main_attachment(self): + @api.multi + def unlink(self): + self.remove_as_main_attachment() + super(IrAttachment, self).unlink() + + @api.multi + def remove_as_main_attachment(self): + for attachment in self: + related_record = self.env[attachment.res_model].browse(attachment.res_id) + if related_record and hasattr(related_record, 'message_main_attachment_id'): + if related_record.message_main_attachment_id == attachment: + related_record.message_main_attachment_id = False + + def register_as_main_attachment(self, force=True): """ Registers this attachment as the main one of the model it is attached to. """ @@ -24,4 +37,5 @@ class IrAttachment(models.Model): # message_main_attachment_id field can be empty, that's why we compare to False; # we are just checking that it exists on the model before writing it if related_record and hasattr(related_record, 'message_main_attachment_id'): - related_record.message_main_attachment_id = self + if force or not related_record.message_main_attachment_id: + related_record.message_main_attachment_id = self diff --git a/addons/mail/static/src/js/chatter.js b/addons/mail/static/src/js/chatter.js index 72971fbeee9..5f9b11198e3 100644 --- a/addons/mail/static/src/js/chatter.js +++ b/addons/mail/static/src/js/chatter.js @@ -81,6 +81,7 @@ var Chatter = Widget.extend({ var nodeOptions = fieldsInfo[mailFields.mail_thread].options || {}; this.hasLogButton = options.display_log_button || nodeOptions.display_log_button; this.postRefresh = nodeOptions.post_refresh || 'never'; + this.reloadOnUploadAttachment = this.postRefresh === 'always'; } }, /** @@ -572,6 +573,9 @@ var Chatter = Widget.extend({ * @private */ _onReloadAttachmentBox: function () { + if (this.reloadOnUploadAttachment) { + this.trigger_up('reload'); + } this._reloadAttachmentBox(); }, /** diff --git a/addons/product/models/product_template.py b/addons/product/models/product_template.py index fbdf5f37b33..5fc9bed772a 100644 --- a/addons/product/models/product_template.py +++ b/addons/product/models/product_template.py @@ -518,6 +518,7 @@ class ProductTemplate(models.Model): variants_to_create.append({ 'product_tmpl_id': tmpl_id.id, 'attribute_value_ids': [(6, 0, list(value_ids))], + 'active': tmpl_id.active, }) if len(variants_to_create) > 1000: raise UserError(_( @@ -957,8 +958,10 @@ class ProductTemplate(models.Model): if not self.active: return _("The product template is archived so no combination is possible.") - ptal_stack = [self._get_valid_product_template_attribute_lines()] - combination_stack = [self.env['product.template.attribute.value']] + necessary_values = necessary_values or self.env['product.template.attribute.value'] + necessary_attributes = necessary_values.mapped('attribute_id') + ptal_stack = [self.valid_product_template_attribute_line_ids.filtered(lambda ptal: ptal.attribute_id not in necessary_attributes)] + combination_stack = [necessary_values] # keep going while we have attribute lines to test while len(ptal_stack): @@ -967,7 +970,7 @@ class ProductTemplate(models.Model): if not attribute_lines: # full combination, if it's possible return it, otherwise skip it - if self._is_combination_possible(combination, parent_combination) and all(v in combination for v in (necessary_values or [])): + if self._is_combination_possible(combination, parent_combination): yield(combination) else: # we have remaining attribute lines to consider diff --git a/addons/product/tests/test_product_attribute_value_config.py b/addons/product/tests/test_product_attribute_value_config.py index aa1de67d16b..e966adbdbdc 100644 --- a/addons/product/tests/test_product_attribute_value_config.py +++ b/addons/product/tests/test_product_attribute_value_config.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import time + from odoo.tests import tagged from odoo.tests.common import TransactionCase @@ -423,6 +425,47 @@ class TestProductAttributeValueConfig(TestProductAttributeValueSetup): self.assertEqual(self.computer._get_closest_possible_combination(computer_ssd_256 + computer_ram_8 + computer_hdd_4 + computer_hdd_2), computer_ssd_256 + computer_ram_8 + computer_hdd_4) + # Make sure this is not extremely slow: + product_template = self.env['product.template'].create({ + 'name': 'many combinations', + }) + + for i in range(10): + # create the attributes + product_attribute = self.env['product.attribute'].create({ + 'name': "att %s" % i, + 'create_variant': 'dynamic', + 'sequence': i, + }) + + for j in range(10): + # create the attribute values + self.env['product.attribute.value'].create([{ + 'name': "val %s/%s" % (i, j), + 'attribute_id': product_attribute.id, + 'sequence': j, + }]) + + # set attribute and attribute values on the template + self.env['product.template.attribute.line'].create([{ + 'attribute_id': product_attribute.id, + 'product_tmpl_id': product_template.id, + 'value_ids': [(6, 0, product_attribute.value_ids.ids)] + }]) + + # Get a value in the middle for each attribute to make sure it would + # take time to reach it (if looping one by one like before the fix). + combination = self.env['product.template.attribute.value'] + for ptal in product_template.attribute_line_ids: + combination += ptal.product_template_value_ids[5] + + started_at = time.time() + self.assertEqual(product_template._get_closest_possible_combination(combination), combination) + elapsed = time.time() - started_at + # It should take around 10ms, but to avoid false positives we check an + # higher value. Before the fix it would take hours. + self.assertLess(elapsed, 0.5) + def test_clear_caches(self): """The goal of this test is to make sure the cache is invalidated when it should be.""" diff --git a/addons/sale/report/sale_report_templates.xml b/addons/sale/report/sale_report_templates.xml index 380596eb181..c8c9612326a 100644 --- a/addons/sale/report/sale_report_templates.xml +++ b/addons/sale/report/sale_report_templates.xml @@ -70,11 +70,15 @@ <table class="table table-sm o_main_table"> <thead> <tr> + <!-- TODO: remove in master --> + <t t-set="colspan" t-value="5"/> <th class="text-left">Description</th> <th class="text-right">Quantity</th> <th class="text-right">Unit Price</th> <th t-if="display_discount" class="text-right" groups="sale.group_discount_per_so_line"> <span>Disc.(%)</span> + <!-- TODO: remove in master --> + <t t-set="colspan" t-value="colspan+1"/> </th> <th class="text-right">Taxes</th> <th class="text-right"> diff --git a/addons/sale/views/sale_portal_templates.xml b/addons/sale/views/sale_portal_templates.xml index c2a12f037d2..1f85b2e7e32 100644 --- a/addons/sale/views/sale_portal_templates.xml +++ b/addons/sale/views/sale_portal_templates.xml @@ -423,11 +423,15 @@ <table class="table table-sm" id="sales_order_table"> <thead class="bg-100"> <tr> + <!-- TODO: remove in master --> + <t t-set="colspan" t-value="6"/> <th class="text-left">Products</th> <th class="text-right">Quantity</th> <th t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}">Unit Price</th> <th t-if="display_discount" t-attf-class="text-right {{ 'd-none d-sm-table-cell' if report_type == 'html' else '' }}"> <span>Discount</span> + <!-- TODO: remove in master --> + <t t-set="colspan" t-value="colspan+1"/> </th> <th t-attf-class="text-right {{ 'd-none d-md-table-cell' if report_type == 'html' else '' }}">Taxes</th> <th class="text-right" > diff --git a/addons/sale_management/models/sale_order.py b/addons/sale_management/models/sale_order.py index 6ebed05a2bc..6a95dfeaeeb 100644 --- a/addons/sale_management/models/sale_order.py +++ b/addons/sale_management/models/sale_order.py @@ -31,7 +31,8 @@ class SaleOrder(models.Model): @api.onchange('partner_id') def onchange_partner_id(self): super(SaleOrder, self).onchange_partner_id() - self.note = self.sale_order_template_id.note or self.note + template = self.sale_order_template_id.with_context(lang=self.partner_id.lang) + self.note = template.note or self.note def _compute_line_data_for_template_change(self, line): return { diff --git a/addons/sale_management/models/sale_order_template.py b/addons/sale_management/models/sale_order_template.py index 8eef6efd405..493386f33c7 100644 --- a/addons/sale_management/models/sale_order_template.py +++ b/addons/sale_management/models/sale_order_template.py @@ -18,7 +18,7 @@ class SaleOrderTemplate(models.Model): name = fields.Char('Quotation Template', required=True) sale_order_template_line_ids = fields.One2many('sale.order.template.line', 'sale_order_template_id', 'Lines', copy=True) - note = fields.Text('Terms and conditions') + note = fields.Text('Terms and conditions', translate=True) sale_order_template_option_ids = fields.One2many('sale.order.template.option', 'sale_order_template_id', 'Optional Products', copy=True) number_of_days = fields.Integer('Quotation Duration', help='Number of days for the validity date computation of the quotation') diff --git a/addons/sale_management/views/sale_portal_templates.xml b/addons/sale_management/views/sale_portal_templates.xml index 90d2100ba4a..f29a538dd0e 100644 --- a/addons/sale_management/views/sale_portal_templates.xml +++ b/addons/sale_management/views/sale_portal_templates.xml @@ -15,6 +15,7 @@ <xpath expr="//section[@id='details']//table[@id='sales_order_table']/thead/tr" position="inside"> <th t-if="display_remove"> + <!-- TODO: remove in master --> <t t-set="colspan" t-value="colspan+1"/> </th> </xpath> diff --git a/addons/stock/report/report_stockinventory.xml b/addons/stock/report/report_stockinventory.xml index 90abd6b4109..19a988dfe59 100644 --- a/addons/stock/report/report_stockinventory.xml +++ b/addons/stock/report/report_stockinventory.xml @@ -6,6 +6,7 @@ <t t-foreach="docs" t-as="o"> <t t-call="web.external_layout"> <div class="page"> + <br/> <h2>Stock Inventory</h2> <table class="table table-sm"> diff --git a/addons/stock_account/views/product_views.xml b/addons/stock_account/views/product_views.xml index d4920effef1..1a42d5fcb58 100644 --- a/addons/stock_account/views/product_views.xml +++ b/addons/stock_account/views/product_views.xml @@ -67,7 +67,7 @@ <div name="standard_price_div" class="row ml-0"> <field name="standard_price" attrs="{'readonly':[('valuation','=','real_time')]}" - widget='monetary' options="{'currency_field': 'currency_id', 'field_digits': True}" class="float-left"/> + widget='monetary' options="{'currency_field': 'cost_currency_id', 'field_digits': True}" class="float-left"/> <div name="update_cost_price"> <button string="Update Cost" type="action" name="%(action_view_change_standard_price)d" diff --git a/addons/survey/static/src/js/survey.js b/addons/survey/static/src/js/survey.js index b4b47cb3d2f..0bcea263df4 100644 --- a/addons/survey/static/src/js/survey.js +++ b/addons/survey/static/src/js/survey.js @@ -98,6 +98,9 @@ if(!the_form.length) { } // checkboxes and radios + the_form.find("input[name^='" + key + "_'][type='checkbox']").each(function(){ + $(this).val(value); + }); the_form.find("input[name=" + key + "][type!='text']").each(function(){ $(this).val(value); }); diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js index 185a385a812..04da1bc4983 100644 --- a/addons/web/static/src/js/views/list/list_editable_renderer.js +++ b/addons/web/static/src/js/views/list/list_editable_renderer.js @@ -428,6 +428,18 @@ ListRenderer.include({ }); } }, + /** + * @override + */ + updateState: function (state, params) { + if (params.noRender) { + // the state changed, but we won't do a re-rendering right now, so + // remove computed modifiers data (as they are obsolete) to force + // them to be recomputed at next (sub-)rendering + this.allModifiersData = []; + } + return this._super.apply(this, arguments); + }, //-------------------------------------------------------------------------- // Private @@ -478,7 +490,8 @@ ListRenderer.include({ */ _moveToPreviousLine: function () { if (this.currentRow > 0) { - this._selectCell(this.currentRow - 1, this.columns.length - 1); + // FORWARD-PORT THIS COMMIT UP TO SAAS-12.2 + this._selectCell(this.currentRow - 1, this.columns.length - 1, {inc: -1}); } else { this.unselectRow().then(this.trigger_up.bind(this, 'add_record')); } @@ -692,6 +705,8 @@ ListRenderer.include({ * @param {boolean} [options.force=false] if true, force selecting the cell * even if seems to be already the selected one (useful after a re- * rendering, to reset the focus on the correct field) + * @param {integer} [options.inc=1] the increment to use when searching for + * the "next" possible cell (if the cell to select can't be selected) * @return {Deferred} fails if no cell could be selected */ _selectCell: function (rowIndex, fieldIndex, options) { @@ -715,7 +730,7 @@ ListRenderer.include({ var oldFieldIndex = self.currentFieldIndex; self.currentFieldIndex = fieldIndex; fieldIndex = self._activateFieldWidget(record, fieldIndex, { - inc: 1, + inc: options.inc || 1, wrap: wrap, event: options && options.event, }); @@ -868,7 +883,7 @@ ListRenderer.include({ switch (ev.data.direction) { case 'previous': if (this.currentFieldIndex > 0) { - this._selectCell(this.currentRow, this.currentFieldIndex - 1, {wrap: false}) + this._selectCell(this.currentRow, this.currentFieldIndex - 1, {inc: -1, wrap: false}) .fail(this._moveToPreviousLine.bind(this)); } else { this._moveToPreviousLine(); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index 347d0c23a53..9be3e427d98 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -2879,6 +2879,87 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('pressing SHIFT-TAB in editable list with a readonly field [REQUIRE FOCUS]', function (assert) { + assert.expect(4); + + var list = createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree editable="bottom">' + + '<field name="foo"/>' + + '<field name="int_field" readonly="1"/>' + + '<field name="qux"/>' + + '</tree>', + }); + + // start on 'qux', line 3 + testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:nth(2)')); + assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row'); + assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=qux]')[0]); + + // Press 'shift-Tab' -> should go to first cell (same line) + $(document.activeElement).trigger({type: 'keydown', which: $.ui.keyCode.TAB, shiftKey: true}); + assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row'); + assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=foo]')[0]); + + list.destroy(); + }); + + QUnit.test('pressing SHIFT-TAB in editable list with a readonly field in first column [REQUIRE FOCUS]', function (assert) { + assert.expect(4); + + var list = createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree editable="bottom">' + + '<field name="int_field" readonly="1"/>' + + '<field name="foo"/>' + + '<field name="qux"/>' + + '</tree>', + }); + + // start on 'foo', line 3 + testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:nth(1)')); + assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row'); + assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=foo]')[0]); + + // Press 'shift-Tab' -> should go to previous line (last cell) + $(document.activeElement).trigger({type: 'keydown', which: $.ui.keyCode.TAB, shiftKey: true}); + assert.hasClass(list.$('.o_data_row:nth(1)'), 'o_selected_row'); + assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(1) .o_data_cell input[name=qux]')[0]); + + list.destroy(); + }); + + QUnit.test('pressing SHIFT-TAB in editable list with a readonly field in last column [REQUIRE FOCUS]', function (assert) { + assert.expect(4); + + var list = createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree editable="bottom">' + + '<field name="int_field"/>' + + '<field name="foo"/>' + + '<field name="qux" readonly="1"/>' + + '</tree>', + }); + + // start on 'int_field', line 3 + testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:first')); + assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row'); + assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=int_field]')[0]); + + // Press 'shift-Tab' -> should go to previous line ('foo' field) + $(document.activeElement).trigger({type: 'keydown', which: $.ui.keyCode.TAB, shiftKey: true}); + assert.hasClass(list.$('.o_data_row:nth(1)'), 'o_selected_row'); + assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(1) .o_data_cell input[name=foo]')[0]); + + list.destroy(); + }); + QUnit.test('skip invisible fields when navigating list view with TAB', function (assert) { assert.expect(2); @@ -4097,6 +4178,47 @@ QUnit.module('Views', { list.destroy(); }); + + QUnit.test('create record on list with modifiers depending on id', function (assert) { + assert.expect(8); + + var list = createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree editable="top">' + + '<field name="id" invisible="1"/>' + + '<field name="foo" attrs="{\'readonly\': [[\'id\',\'!=\',False]]}"/>' + + '<field name="int_field" attrs="{\'invisible\': [[\'id\',\'!=\',False]]}"/>' + + '</tree>', + }); + + // add a new record + testUtils.dom.click(list.$buttons.find('.o_list_button_add')); + + // modifiers should be evaluted to false + assert.containsOnce(list, '.o_selected_row'); + assert.doesNotHaveClass(list.$('.o_selected_row .o_data_cell:first'), 'o_readonly_modifier'); + assert.doesNotHaveClass(list.$('.o_selected_row .o_data_cell:nth(1)'), 'o_invisible_modifier'); + + // set a value and save + testUtils.fields.editInput(list.$('.o_selected_row input[name=foo]'), 'some value'); + testUtils.dom.click(list.$buttons.find('.o_list_button_save')); + + // modifiers should be evaluted to true + assert.hasClass(list.$('.o_data_row:first .o_data_cell:first'), 'o_readonly_modifier'); + assert.hasClass(list.$('.o_data_row:first .o_data_cell:nth(1)'), 'o_invisible_modifier'); + + // edit again the just created record + testUtils.dom.click(list.$('.o_data_row:first .o_data_cell:first')); + + // modifiers should be evaluted to true + assert.containsOnce(list, '.o_selected_row'); + assert.hasClass(list.$('.o_selected_row .o_data_cell:first'), 'o_readonly_modifier'); + assert.hasClass(list.$('.o_selected_row .o_data_cell:nth(1)'), 'o_invisible_modifier'); + + list.destroy(); + }); }); }); diff --git a/odoo/addons/base/models/res_currency.py b/odoo/addons/base/models/res_currency.py index c75ddfe0021..09a6d019f38 100644 --- a/odoo/addons/base/models/res_currency.py +++ b/odoo/addons/base/models/res_currency.py @@ -115,7 +115,7 @@ class Currency(models.Model): fractional_value = int(parts[2] or 0) lang_code = self.env.context.get('lang') or self.env.user.lang - lang = self.env['res.lang'].search([('code', '=', lang_code)]) + lang = self.env['res.lang'].with_context(active_test=False).search([('code', '=', lang_code)]) amount_words = tools.ustr('{amt_value} {amt_word}').format( amt_value=_num2words(integer_value, lang=lang.iso_code), amt_word=self.currency_unit_label, |
