[PATCH] Upstream patch - 06082022

This commit is contained in:
Parthiv Patel
2022-08-06 08:34:21 +00:00
parent 960d6cadae
commit 84ee04817c
24 changed files with 251 additions and 64 deletions
+1 -1
View File
@@ -294,7 +294,7 @@ class AccountMove(models.Model):
default=_get_default_invoice_incoterm,
help='International Commercial Terms are a series of predefined commercial terms used in international transactions.')
display_qr_code = fields.Boolean(string="Display QR-code", related='company_id.qr_code')
qr_code_method = fields.Selection(string="Payment QR-code",
qr_code_method = fields.Selection(string="Payment QR-code", copy=False,
selection=lambda self: self.env['res.partner.bank'].get_available_qr_methods_in_sequence(),
help="Type of QR-code to be generated for the payment of this invoice, when printing it. If left blank, the first available and usable method will be used.")
@@ -3,7 +3,7 @@
from flectra.exceptions import UserError
from flectra.addons.account.tests.common import AccountTestInvoicingCommon
from flectra.tests import tagged
from flectra import fields
@tagged('post_install', '-at_install')
class TestSEPAQRCode(AccountTestInvoicingCommon):
@@ -62,3 +62,16 @@ class TestSEPAQRCode(AccountTestInvoicingCommon):
"""
self.sepa_qr_invoice.generate_qr_code()
self.assertEqual(self.sepa_qr_invoice.qr_code_method, 'sct_qr', "SEPA QR-code generator should have been chosen for this invoice.")
def test_out_invoice_create_refund_qr_code(self):
self.sepa_qr_invoice.generate_qr_code()
self.sepa_qr_invoice.action_post()
move_reversal = self.env['account.move.reversal'].with_context(active_model="account.move", active_ids=self.sepa_qr_invoice.ids).create({
'date': fields.Date.from_string('2019-02-01'),
'reason': 'no reason',
'refund_method': 'refund',
})
reversal = move_reversal.reverse_moves()
reverse_move = self.env['account.move'].browse(reversal['res_id'])
self.assertFalse(reverse_move.qr_code_method, "qr_code_method for credit note should be None")
+17 -17
View File
@@ -1,24 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<flectra>
<data noupdate="1">
<!-- Account Tax Group -->
<record id="tax_group_tva_21" model="account.tax.group">
<field name="name">TVA 21%</field>
</record>
<data noupdate="1">
<record id="tax_group_tva_12" model="account.tax.group">
<field name="name">TVA 12%</field>
</record>
<!-- Account Tax Group -->
<record id="tax_group_tva_21" model="account.tax.group">
<field name="name">TVA 21%</field>
</record>
<record id="tax_group_tva_6" model="account.tax.group">
<field name="name">TVA 6%</field>
</record>
<record id="tax_group_tva_12" model="account.tax.group">
<field name="name">TVA 12%</field>
</record>
<record id="tax_group_tva_0" model="account.tax.group">
<field name="name">TVA 0%</field>
</record>
<record id="tax_group_tva_6" model="account.tax.group">
<field name="name">TVA 6%</field>
</record>
</data>
</flectra>
<record id="tax_group_tva_0" model="account.tax.group">
<field name="name">TVA 0%</field>
</record>
</data>
</flectra>
@@ -12,6 +12,10 @@
<field name="zip">12345</field>
</record>
<record id="l10n_it.partner_demo_company_it" model="res.partner">
<field name="l10n_it_pa_index">0803HR0</field>
</record>
<record id="partner_demo_it" model="res.partner">
<field name="name">Palazzo dell'Arte</field>
<field name="vat">IT00000010215</field>
@@ -3,6 +3,12 @@
<data>
<template id="account_invoice_line_it_FatturaPA">
<t t-if="rc_refund">
<t t-set="price_subtotal" t-value="-line.price_subtotal"/>
</t>
<t t-else="">
<t t-set="price_subtotal" t-value="line.price_subtotal"/>
</t>
<DettaglioLinee>
<NumeroLinea t-esc="line_counter"/>
<CodiceArticolo t-if="line.product_id.barcode">
@@ -19,12 +25,12 @@
</Descrizione>
<Quantita t-esc="format_numbers(line.quantity)"/>
<UnitaMisura t-if="line.product_uom_id.category_id != env.ref('uom.product_uom_categ_unit')" t-esc="format_alphanumeric(line.product_uom_id.name)"/>
<PrezzoUnitario t-esc="'%.06f' % (line.price_subtotal / (( 1 - (line.discount or 0.0) / 100.0) * line.quantity) if line.quantity and line.discount != 100.0 else line.price_unit)"/>
<PrezzoUnitario t-esc="'%.06f' % (price_subtotal / (( 1 - (line.discount or 0.0) / 100.0) * line.quantity) if line.quantity and line.discount != 100.0 else line.price_unit)"/>
<ScontoMaggiorazione t-if="line.discount != 0">
<Tipo t-esc="discount_type(line.discount)"/>
<Percentuale t-esc="format_numbers(abs(line.discount))"/>
</ScontoMaggiorazione>
<PrezzoTotale t-esc="format_monetary(line.price_subtotal, currency)"/>
<PrezzoTotale t-esc="format_monetary(price_subtotal, currency)"/>
<AliquotaIVA t-if="line.tax_ids.amount_type == 'percent'" t-esc="format_numbers(line.tax_ids.amount)"/>
<AliquotaIVA t-elif="line.tax_ids.amount_type != 'percent'" t-esc="'0.00'"/>
<Natura t-if="line.tax_ids.l10n_it_has_exoneration" t-esc="line.tax_ids.l10n_it_kind_exoneration"/>
@@ -158,8 +164,14 @@
<AliquotaIVA t-esc="format_numbers(tax.amount)"/>
<Natura t-if="has_exoneration" t-esc="kind_exoneration"/>
<Arrotondamento t-if="tax_dict.get('rounding')" t-esc="format_numbers(tax_dict['rounding'])"/>
<ImponibileImporto t-esc="format_monetary(abs(tax_dict['base_amount_currency']), currency)"/>
<Imposta t-esc="format_monetary(abs(tax_dict['tax_amount_currency']), currency)"/>
<t t-if="rc_refund">
<ImponibileImporto t-esc="format_monetary(tax_dict['base_amount_currency'], currency)"/>
<Imposta t-esc="format_monetary(tax_dict['tax_amount_currency'], currency)"/>
</t>
<t t-else="">
<ImponibileImporto t-esc="format_monetary(abs(tax_dict['base_amount_currency']), currency)"/>
<Imposta t-esc="format_monetary(abs(tax_dict['tax_amount_currency']), currency)"/>
</t>
<EsigibilitaIVA t-if="not has_exoneration or kind_exoneration == 'N6'" t-esc="tax.l10n_it_vat_due_date"/>
<RiferimentoNormativo t-if="has_exoneration" t-esc="format_alphanumeric(tax.l10n_it_law_reference[:100])"/>
</DatiRiepilogo>
@@ -212,14 +212,14 @@ class AccountEdiFormat(models.Model):
def _l10n_it_document_type_mapping(self):
return {
'TD01': dict(move_type='out_invoice', import_type='in_invoice'),
'TD04': dict(move_type='out_refund', import_type='in_refund'),
'TD07': dict(move_type='out_invoice', import_type='in_invoice', simplified=True),
'TD08': dict(move_type='out_refund', import_type='in_refund', simplified=True),
'TD09': dict(move_type='out_invoice', import_type='in_invoice', simplified=True),
'TD17': dict(move_type='in_invoice', import_type='out_invoice', self_invoice=True, services_or_goods="service"),
'TD18': dict(move_type='in_invoice', import_type='out_invoice', self_invoice=True, services_or_goods="consu", partner_in_eu=True),
'TD19': dict(move_type='in_invoice', import_type='out_invoice', self_invoice=True, services_or_goods="consu", goods_in_italy=True),
'TD01': dict(move_types=['out_invoice'], import_type='in_invoice'),
'TD04': dict(move_types=['out_refund'], import_type='in_refund'),
'TD07': dict(move_types=['out_invoice'], import_type='in_invoice', simplified=True),
'TD08': dict(move_types=['out_refund'], import_type='in_refund', simplified=True),
'TD09': dict(move_types=['out_invoice'], import_type='in_invoice', simplified=True),
'TD17': dict(move_types=['in_invoice', 'in_refund'], import_type='out_invoice', self_invoice=True, services_or_goods="service"),
'TD18': dict(move_types=['in_invoice', 'in_refund'], import_type='out_invoice', self_invoice=True, services_or_goods="consu", partner_in_eu=True),
'TD19': dict(move_types=['in_invoice', 'in_refund'], import_type='out_invoice', self_invoice=True, services_or_goods="consu", goods_in_italy=True),
}
def _l10n_it_get_document_type(self, invoice):
@@ -232,7 +232,7 @@ class AccountEdiFormat(models.Model):
info_services_or_goods = infos.get('services_or_goods', "both")
info_partner_in_eu = infos.get('partner_in_eu', False)
if all([
invoice.move_type == infos.get('move_type', False),
invoice.move_type in infos.get('move_types', False),
is_self_invoice == infos.get('self_invoice', False),
is_simplified == infos.get('simplified', False),
info_services_or_goods in ("both", services_or_goods),
+8 -2
View File
@@ -183,11 +183,16 @@ class AccountMove(models.Model):
or (partner.country_id.code == 'IT' and '0000000')
or 'XXXXXXX')
# Represent if the document is a reverse charge refund in a single variable
rc_refund = self.move_type == 'in_refund' and document_type in ['TD16', 'TD17', 'TD18']
# Self-invoices are technically -100%/+100% repartitioned
# but functionally need to be exported as 100%
document_total = self.amount_total
if is_self_invoice:
document_total += sum([v['tax_amount_currency'] for k, v in tax_details['tax_details'].items()])
document_total += sum([abs(v['tax_amount_currency']) for k, v in tax_details['tax_details'].items()])
if rc_refund:
document_total = -abs(document_total)
# Create file content.
template_values = {
@@ -225,7 +230,8 @@ class AccountMove(models.Model):
'normalize_codice_fiscale': partner._l10n_it_normalize_codice_fiscale,
'get_vat_number': get_vat_number,
'get_vat_country': get_vat_country,
'in_eu': in_eu
'in_eu': in_eu,
'rc_refund': rc_refund,
}
return template_values
+3 -3
View File
@@ -1,5 +1,6 @@
# -*- coding:utf-8 -*-
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
from stdnum.it import codicefiscale, iva
from flectra import api, fields, models, _
from flectra.exceptions import UserError, ValidationError
@@ -45,7 +46,6 @@ class ResPartner(models.Model):
@api.constrains('l10n_it_codice_fiscale')
def validate_codice_fiscale(self):
p = re.compile(r'^([A-Za-z]{6}[0-9]{2}[A-Za-z]{1}[0-9]{2}[A-Za-z]{1}[0-9]{3}[A-Za-z]{1}$)|([0-9]{11})|(IT[0-9]{11})$')
for record in self:
if record.l10n_it_codice_fiscale and not p.match(record.l10n_it_codice_fiscale):
raise UserError(_("Invalid Codice Fiscale '%s': should be like 'MRTMTT91D08F205J' for physical person and '12345678901' or 'IT12345678901' for businesses.", record.l10n_it_codice_fiscale))
if record.l10n_it_codice_fiscale and (not codicefiscale.is_valid(record.l10n_it_codice_fiscale) and not iva.is_valid(record.l10n_it_codice_fiscale)):
raise UserError(_("Invalid Codice Fiscale '%s': should be like 'MRTMTT91D08F205J' for physical person and '12345670546' or 'IT12345670546' for businesses.", record.l10n_it_codice_fiscale))
+1
View File
@@ -2,3 +2,4 @@
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
from . import test_ir_mail_server
from . import test_res_partner
@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
from flectra.exceptions import UserError
from flectra.tests.common import TransactionCase, tagged
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestResPartner(TransactionCase):
def test_validate_fiscal_code(self):
valid_codes = [
"AORTHV05P30V295L",
"SPDTHB43S93F42VH",
"MDRTUV99H14X2MNU",
"XPTDRX73R64YPLUD",
"LOLXDR40T3MZRTSV",
"GJTIUG55DLQZRTSS",
"CDEOTG5PBLQZRTSE",
"PERTLELPALQZRTSN",
"IT12345678887",
"IT12345670546",
"IT95286931217",
"IT95867361206",
"IT94567689990",
"12345670546",
"95286931217",
"95867361206",
"94567689990",
]
invalid_codes = [
"AORTHV05P34V295U",
"SPDTHB43O93F42VH",
"MDRTUVV9H14X2MNU",
"XPTDRX73RS4YPLUD",
"LOLXDRQ0T3QZRTSJ",
"GJTIUGR5DLQZRTSS",
"CDEOTG5PBLQZRTSS",
"PERTLEZPALQZRTSN",
"IT12345678901",
"IT12345678885",
"IT45689349992",
"IT78239131204",
"IT45692151219",
"12345678901",
"12345678885",
"45689349992",
"78239131204",
"45692151219",
]
partners = self.env['res.partner']
for i, code in enumerate(invalid_codes):
with self.assertRaises(UserError):
partners += self.env['res.partner'].create({'name': f'partner_{i}', 'l10n_it_codice_fiscale': code})
for i, code in enumerate(valid_codes):
partners += self.env['res.partner'].create({'name': f'partner_{i}', 'l10n_it_codice_fiscale': code})
self.assertEqual(len(partners), len(valid_codes))
@@ -55,13 +55,13 @@ class TestItEdiReverseCharge(TestItEdi):
'amount_type': 'percent',
'type_tax_use': 'purchase',
'invoice_repartition_line_ids': repartition_lines(
RepartitionLine(100, 'base', ('+03',)),
RepartitionLine(100, 'tax', ('+vj9',)),
RepartitionLine(-100, 'tax', False)),
RepartitionLine(100, 'base', ('+03', '+vj9')),
RepartitionLine(100, 'tax', ('+5v',)),
RepartitionLine(-100, 'tax', ('-4v',))),
'refund_repartition_line_ids': repartition_lines(
RepartitionLine(100, 'base', False),
RepartitionLine(100, 'base', ('-03', '-vj9')),
RepartitionLine(100, 'tax', False),
RepartitionLine(-100, 'tax', False))
RepartitionLine(-100, 'tax', False)),
}
# Purchase tax 4% with Reverse Charge
cls.purchase_tax_4p = cls.env['account.tax'].with_company(cls.company).create(tax_data)
@@ -74,10 +74,15 @@ class TestItEdiReverseCharge(TestItEdi):
**tax_data,
'name': 'Tax 4% purchase Reverse Charge, in Italy',
'invoice_repartition_line_ids': repartition_lines(
RepartitionLine(100, 'base', ('+03',)),
RepartitionLine(100, 'tax', ('+vj3',)),
RepartitionLine(100, 'base', ('+03', '+vj3')),
RepartitionLine(100, 'tax', ('+5v',)),
RepartitionLine(-100, 'tax', ('-4v',))),
'refund_repartition_line_ids': repartition_lines(
RepartitionLine(100, 'base', ('-03', '-vj3')),
RepartitionLine(100, 'tax', False),
RepartitionLine(-100, 'tax', False)),
}
cls.purchase_tax_4p_already_in_italy = cls.env['account.tax'].with_company(cls.company).create(tax_data_4p_already_in_italy)
cls.line_tax_4p_already_in_italy = cls.standard_line.copy()
cls.line_tax_4p_already_in_italy['tax_ids'] = [(6, 0, cls.purchase_tax_4p_already_in_italy.ids)]
@@ -151,11 +156,15 @@ class TestItEdiReverseCharge(TestItEdi):
),
}
cls.reverse_charge_bill_2 = cls.env['account.move'].with_company(cls.company).create(bill_data_2)
cls.reverse_charge_refund = cls.reverse_charge_bill.with_company(cls.company)._reverse_moves([{
'invoice_date': fields.Date.from_string('2022-03-24'),
}])
# Posting moves -----------
cls.reverse_charge_invoice._post()
cls.reverse_charge_bill._post()
cls.reverse_charge_bill_2._post()
cls.reverse_charge_refund._post()
def _cleanup_etree(self, content, xpaths=None):
xpaths = {
@@ -193,3 +202,26 @@ class TestItEdiReverseCharge(TestItEdi):
"(//DettaglioLinee/Descrizione)[2]": "<Descrizione/>",
}
)
def test_reverse_charge_refund(self):
self._test_invoice_with_sample_file(
self.reverse_charge_refund,
"reverse_charge_bill.xml",
xpaths_result={
"//DatiGeneraliDocumento/Numero": "<Numero/>",
"//DatiPagamento/DettaglioPagamento/DataScadenzaPagamento": "<DataScadenzaPagamento/>",
},
xpaths_file={
"//DatiGeneraliDocumento/Numero": "<Numero/>",
"//DatiGeneraliDocumento/ImportoTotaleDocumento": "<ImportoTotaleDocumento>-1808.91</ImportoTotaleDocumento>",
"//DatiPagamento/DettaglioPagamento/DataScadenzaPagamento": "<DataScadenzaPagamento/>",
"(//DettaglioLinee/PrezzoUnitario)[1]": "<PrezzoUnitario>-800.400000</PrezzoUnitario>",
"(//DettaglioLinee/PrezzoUnitario)[2]": "<PrezzoUnitario>-800.400000</PrezzoUnitario>",
"(//DettaglioLinee/PrezzoTotale)[1]": "<PrezzoTotale>-800.40</PrezzoTotale>",
"(//DettaglioLinee/PrezzoTotale)[2]": "<PrezzoTotale>-800.40</PrezzoTotale>",
"(//DatiRiepilogo/ImponibileImporto)[1]": "<ImponibileImporto>-800.40</ImponibileImporto>",
"(//DatiRiepilogo/ImponibileImporto)[2]": "<ImponibileImporto>-800.40</ImponibileImporto>",
"(//DatiRiepilogo/Imposta)[1]": "<Imposta>-176.09</Imposta>",
"(//DatiRiepilogo/Imposta)[2]": "<Imposta>-32.02</Imposta>",
}
)
@@ -347,7 +347,7 @@ var MassMailingFieldHtml = FieldHtml.extend({
var $snippets_menu = $snippetsSideBar.find("#snippets_menu");
for (const button of $snippets_menu.get(0).children) {
if (button.textContent === 'Select a template') {
if (!button.hasAttribute('tabindex') && !button.hasAttribute('accesskey')) {
button.style.display = 'none';
}
}
+1 -1
View File
@@ -531,7 +531,7 @@
<field name="view_id" eval="False"/>
<field name="search_view_id" ref="view_mrp_production_filter"/>
<field name="domain">[('picking_type_id', '=', active_id)]</field>
<field name="context">{}</field>
<field name="context">{'default_picking_type_id': active_id}</field>
</record>
<record id="mrp_production_action_unreserve_tree" model="ir.actions.server">
@@ -2268,6 +2268,16 @@ exports.Orderline = Backbone.Model.extend({
return this.get_base_price();
}
},
get_taxed_lst_unit_price: function(){
var lst_price = this.get_lst_price();
if (this.pos.config.iface_tax_included === 'total') {
var product = this.get_product();
var taxes_ids = product.taxes_id;
var product_taxes = this.get_taxes_after_fp(taxes_ids);
return this.compute_all(product_taxes, lst_price, 1, this.pos.currency.rounding).total_included;
}
return lst_price;
},
get_price_without_tax: function(){
return this.get_all_prices().priceWithoutTax;
},
@@ -37,9 +37,9 @@
<span> </span><t t-esc="props.line.get_unit().name" />
at
<t t-if="props.line.display_discount_policy() == 'without_discount' and
props.line.get_unit_display_price() &lt; props.line.get_lst_price()">
props.line.get_unit_display_price() &lt; props.line.get_taxed_lst_unit_price()">
<s>
<t t-esc="env.pos.format_currency(props.line.get_fixed_lst_price(),'Product Price')" />
<t t-esc="env.pos.format_currency(props.line.get_taxed_lst_unit_price(),'Product Price')" />
</s>
<t t-esc="env.pos.format_currency(props.line.get_unit_display_price(),'Product Price')" />
</t>
+2 -1
View File
@@ -335,7 +335,8 @@ class PurchaseOrderLine(models.Model):
result = super(PurchaseOrderLine, self).write(values)
if 'price_unit' in values:
for line in lines:
moves = line.move_ids.filtered(lambda s: s.state not in ('cancel', 'done'))
# Avoid updating kit components' stock.move
moves = line.move_ids.filtered(lambda s: s.state not in ('cancel', 'done') and s.product_id == line.product_id)
moves.write({'price_unit': line._get_stock_move_price_unit()})
if 'product_qty' in values:
lines.with_context(previous_product_qty=previous_product_qty)._create_or_update_picking()
+1
View File
@@ -1188,6 +1188,7 @@ class SaleOrderLine(models.Model):
elif not float_is_zero(line.qty_to_invoice, precision_digits=precision):
line.invoice_status = 'to invoice'
elif line.state == 'sale' and line.product_id.invoice_policy == 'order' and\
line.product_uom_qty >= 0.0 and\
float_compare(line.qty_delivered, line.product_uom_qty, precision_digits=precision) == 1:
line.invoice_status = 'upselling'
elif float_compare(line.qty_invoiced, line.product_uom_qty, precision_digits=precision) >= 0:
+14
View File
@@ -172,6 +172,20 @@ class TestSaleOrder(TestSaleCommon):
self.assertEqual(mail_message.author_id, mail_message.partner_ids, 'Sale: author should be in composer recipients thanks to "partner_to" field set on template')
self.assertEqual(mail_message.partner_ids, mail_message.sudo().mail_ids.recipient_ids, 'Sale: author should receive mail due to presence in composer recipients')
def test_invoice_state_when_ordered_quantity_is_negative(self):
"""When you invoice a SO line with a product that is invoiced on ordered quantities and has negative ordered quantity,
this test ensures that the invoicing status of the SO line is 'invoiced' (and not 'upselling')."""
sale_order = self.env['sale.order'].create({
'partner_id': self.partner_a.id,
'order_line': [(0, 0, {
'product_id': self.company_data['product_order_no'].id,
'product_uom_qty': -1,
})]
})
sale_order.action_confirm()
sale_order._create_invoices(final=True)
self.assertTrue(sale_order.invoice_status == 'invoiced', 'Sale: The invoicing status of the SO should be "invoiced"')
def test_sale_sequence(self):
self.env['ir.sequence'].search([
('code', '=', 'sale.order'),
@@ -90,6 +90,8 @@ class ResCompany(models.Model):
('default_location_src_id.usage', '=', 'supplier'),
('default_location_dest_id.usage', '=', 'customer'),
], limit=1, order='sequence')
if not dropship_picking_type:
continue
dropship_vals.append({
'name': '%s%s' % (supplier_location.name, customer_location.name),
'action': 'buy',
@@ -1,6 +1,7 @@
flectra.define('web.DatePickerOwl', function (require) {
"use strict";
const config = require('web.config');
const field_utils = require('web.field_utils');
const time = require('web.time');
const { useAutofocus } = require('web.custom_hooks');
@@ -167,7 +168,7 @@ flectra.define('web.DatePickerOwl', function (require) {
* @private
*/
_onWindowScroll(ev) {
if (ev.target !== this.inputRef.el) {
if (!config.device.isIOS && ev.target !== this.inputRef.el) {
this._datetimepicker('hide');
}
}
+13 -9
View File
@@ -615,16 +615,9 @@ function parseFloat(value) {
* @throws {Error} if no float is found or if parameter does not respect monetary condition
*/
function parseMonetary(value, field, options) {
var values = value.split('&nbsp;');
if (values.length === 1) {
values = value.split(NBSP);
}
if (values.length === 1) {
if (!value.includes(NBSP) && !value.includes('&nbsp;')) {
return parseFloat(value);
}
else if (values.length !== 2) {
throw new Error(_.str.sprintf(core._t("'%s' is not a correct monetary field"), value));
}
options = options || {};
var currency = options.currency;
if (!currency) {
@@ -635,7 +628,18 @@ function parseMonetary(value, field, options) {
}
currency = session.get_currency(currency_id);
}
return parseFloat(values[0] === currency.symbol ? values[1] : values[0]);
if (!value.includes(currency.symbol)) {
throw new Error(_.str.sprintf(core._t("'%s' is not a correct monetary field"), value));
}
if (currency.position === 'before') {
return parseFloat(value
.replace(`${ currency.symbol }${ NBSP }`, '')
.replace(`${ currency.symbol }&nbsp;`, ''));
} else {
return parseFloat(value
.replace(`${ NBSP }${ currency.symbol }`, '')
.replace(`&nbsp;${ currency.symbol }`, ''));
}
}
/**
@@ -261,8 +261,9 @@ QUnit.test('parse integer', function(assert) {
});
QUnit.test('parse monetary', function(assert) {
assert.expect(11);
assert.expect(13);
var originalCurrencies = session.currencies;
const originalParameters = _.clone(core._t.database.parameters);
session.currencies = {
1: {
digits: [69, 2],
@@ -288,7 +289,18 @@ QUnit.test('parse monetary', function(assert) {
assert.throws(function() {fieldUtils.parse.monetary("$&nbsp;12.00", {}, {currency_id: 1})}, /is not a correct/);
assert.throws(function() {fieldUtils.parse.monetary("$&nbsp;12.00&nbsp;34", {}, {currency_id: 3})}, /is not a correct/);
// In some languages, the non-breaking space character is used as thousands separator.
const nbsp = '\u00a0';
_.extend(core._t.database.parameters, {
grouping: [3, 0],
decimal_point: ',',
thousands_sep: nbsp,
});
assert.strictEqual(fieldUtils.parse.monetary(`1${nbsp}000.00${nbsp}`, {}, {currency_id: 1}), 1000);
assert.strictEqual(fieldUtils.parse.monetary(`$${nbsp}1${nbsp}000.00`, {}, {currency_id: 3}), 1000);
session.currencies = originalCurrencies;
core._t.database.parameters = originalParameters;
});
QUnit.test('parse percentage', function(assert) {
@@ -77,7 +77,16 @@ function _buildElement(tagName, title, options) {
*/
function _buildTitleElement(title) {
const titleEl = document.createElement('we-title');
titleEl.textContent = title;
// As a stable fix, to not touch XML templates and break existing
// translations, the ⌙ character is automatically replaced by └ which makes
// more sense for the usecase and should work properly in all browsers. The
// ⌙ character is actually rendered mirrored on Windows 11 Chrome (and
// others) as the font used for those unicode characters is left to the
// browser. We could force a font of our own but it's probably not worth it.
// TODO a better solution with a SVG or CSS solution has to be done in
// master. That would unify the look of the symbol across all browsers and
// also prevent special characters to be placed in translations.
titleEl.textContent = title.replace(/⌙/g, '└');
return titleEl;
}
/**
+7 -3
View File
@@ -1286,10 +1286,14 @@ header {
}
// Language selector
.js_language_selector {
.dropdown-menu {
min-width: 0;
#wrapwrap:not(.o_rtl) {
.js_language_selector {
.dropdown-menu {
min-width: 0;
}
}
}
.js_language_selector {
a.list-inline-item {
padding: 3px 0;