From 3580017676969dd48afa927cfba30dcd73e07912 Mon Sep 17 00:00:00 2001 From: Marco Colombo Date: Thu, 14 Jan 2021 14:04:49 +0100 Subject: [PATCH] [ADD] supporto per la trasmissione degli importi in EUR per fatture in valuta estera --- l10n_it_fatturapa_out/models/company.py | 18 +++ .../tests/data/IT06363391001_00009.xml | 104 ----------------- .../tests/data/IT06363391001_00015.xml | 105 +++++++++++++++++ .../tests/data/IT06363391001_00015a.xml | 105 +++++++++++++++++ .../tests/test_fatturapa_xml_validation.py | 106 ++++++++++-------- l10n_it_fatturapa_out/views/company_view.xml | 10 +- .../wizard/wizard_export_fatturapa.py | 46 ++++++-- 7 files changed, 336 insertions(+), 158 deletions(-) delete mode 100644 l10n_it_fatturapa_out/tests/data/IT06363391001_00009.xml create mode 100644 l10n_it_fatturapa_out/tests/data/IT06363391001_00015.xml create mode 100644 l10n_it_fatturapa_out/tests/data/IT06363391001_00015a.xml diff --git a/l10n_it_fatturapa_out/models/company.py b/l10n_it_fatturapa_out/models/company.py index d36ffbbdef24..72aef1ba7f24 100644 --- a/l10n_it_fatturapa_out/models/company.py +++ b/l10n_it_fatturapa_out/models/company.py @@ -1,10 +1,13 @@ # -*- coding: utf-8 -*- # Copyright 2019 Roberto Fichera +# Copytight 2022 Marco Colombo # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). from odoo import fields, models, api, _ from odoo.exceptions import ValidationError +_DEFAULT_XML_DIVISA_VALUE = "force_eur" + class ResCompany(models.Model): _inherit = 'res.company' @@ -15,6 +18,15 @@ class ResCompany(models.Model): help="Customer default for maximum number of invoices to group " "in a single XML file. 0=Unlimited") + xml_divisa_value = fields.Selection( + [ + ("keep_orig", "Keep original"), + ("force_eur", "Force euro"), + ], + string="XML Divisa value", + default=_DEFAULT_XML_DIVISA_VALUE, + ) + @api.constrains('max_invoice_in_xml') def _validate_max_invoice_in_xml(self): if self.max_invoice_in_xml < 0: @@ -29,12 +41,18 @@ class AccountConfigSettings(models.TransientModel): max_invoice_in_xml = fields.Integer( related='company_id.max_invoice_in_xml', readonly=False) + xml_divisa_value = fields.Selection( + related="company_id.xml_divisa_value", readonly=False) + @api.onchange('company_id') def onchange_company_id(self): res = super(AccountConfigSettings, self).onchange_company_id() if self.company_id: company = self.company_id self.max_invoice_in_xml = (company.max_invoice_in_xml or 0) + self.xml_divisa_value = (company.xml_divisa_value or + _DEFAULT_XML_DIVISA_VALUE) else: self.max_invoice_in_xml = 0 + self.xml_divisa_value = _DEFAULT_XML_DIVISA_VALUE return res diff --git a/l10n_it_fatturapa_out/tests/data/IT06363391001_00009.xml b/l10n_it_fatturapa_out/tests/data/IT06363391001_00009.xml deleted file mode 100644 index 94ccc4e7d2ab..000000000000 --- a/l10n_it_fatturapa_out/tests/data/IT06363391001_00009.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - IT - 06363391001 - - 00009 - FPR12 - XXXXXXX - - 06543534343 - info@yourcompany.example.com - - - - - - IT - 06363391001 - - - YourCompany - - RF01 - - - Via Milano, 1 - 00100 - Roma - AK - IT - - - 06543534343 - info@yourcompany.example.com - - - - - - AE - 99999999999 - - - Foreign Customer - - - - 29-17 Al Thanya St - 00000 - Dubai - EE - AE - - - - - - - TD01 - AED - 2018-01-07 - INV/2018/0018 - 14.00 - SI - - - - - 1 - Mouse Optical - 1.000 - Unit(s) - 8.19672 - 8.20 - 22.00 - - - 2 - Zed+ Antivirus - 1.000 - Unit(s) - 3.27869 - 3.28 - 22.00 - - - 22.00 - 11.48 - 2.52 - - - - TP02 - - MP05 - 2018-02-28 - 14.00 - - - - diff --git a/l10n_it_fatturapa_out/tests/data/IT06363391001_00015.xml b/l10n_it_fatturapa_out/tests/data/IT06363391001_00015.xml new file mode 100644 index 000000000000..7cdd9caff0d3 --- /dev/null +++ b/l10n_it_fatturapa_out/tests/data/IT06363391001_00015.xml @@ -0,0 +1,105 @@ + + + + + + IT + 06363391001 + + 00015 + FPA12 + UFPQ1O + + 06543534343 + info@yourcompany.example.com + + + + + + IT + 06363391001 + + + YourCompany + + RF01 + + + Via Milano, 1 + 00100 + Roma + AK + IT + + + 06543534343 + info@yourcompany.example.com + + + + + + IT + 06363391001 + + 06363391001 + + Public Administration + + + + Via Roma, 1 + 10100 + Torino + AK + IT + + + + + + + TD01 + EUR + 2021-12-16 + INV/2021/0001 + 11.97 + SI + + + + + 1 + Cabinet with Doors + 1.000 + Unit(s) + 11.96581 + 11.97 + 0.00 + N3.2 + + Valuta + USD + 14.00000 + 2021-12-16 + + + + 0.00 + N3.2 + 11.97 + 0.00 + Not subject to VAT law + + + + TP02 + + MP05 + 2022-01-31 + 11.97 + + + + diff --git a/l10n_it_fatturapa_out/tests/data/IT06363391001_00015a.xml b/l10n_it_fatturapa_out/tests/data/IT06363391001_00015a.xml new file mode 100644 index 000000000000..be5ab76d5ef6 --- /dev/null +++ b/l10n_it_fatturapa_out/tests/data/IT06363391001_00015a.xml @@ -0,0 +1,105 @@ + + + + + + IT + 06363391001 + + 00015a + FPA12 + UFPQ1O + + 06543534343 + info@yourcompany.example.com + + + + + + IT + 06363391001 + + + YourCompany + + RF01 + + + Via Milano, 1 + 00100 + Roma + AK + IT + + + 06543534343 + info@yourcompany.example.com + + + + + + IT + 06363391001 + + 06363391001 + + Public Administration + + + + Via Roma, 1 + 10100 + Torino + AK + IT + + + + + + + TD01 + USD + 2021-12-16 + INV/2021/0001 + 14.00 + SI + + + + + 1 + Cabinet with Doors + 1.000 + Unit(s) + 11.96581 + 11.97 + 0.00 + N3.2 + + Valuta + USD + 14.00000 + 2021-12-16 + + + + 0.00 + N3.2 + 11.97 + 0.00 + Not subject to VAT law + + + + TP02 + + MP05 + 2022-01-31 + 14.00 + + + + diff --git a/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py b/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py index 459d6bb481f1..6db8104f0768 100644 --- a/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py +++ b/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py @@ -385,52 +385,6 @@ def test_8_xml_export(self): xml_content = base64.decodebytes(attachment.datas) self.check_content(xml_content, 'IT06363391001_00008.xml') - def test_9_xml_export(self): - self.tax_22.price_include = True - self.set_sequences(18, '2018-01-07') - partner = self.res_partner_fatturapa_4 - partner.onchange_country_id_e_inv() - partner.write(partner._convert_to_write(partner._cache)) - self.assertEqual(partner.codice_destinatario, 'XXXXXXX') - invoice = self.invoice_model.create({ - 'date_invoice': '2018-01-07', - 'partner_id': partner.id, - 'journal_id': self.sales_journal.id, - 'account_id': self.a_recv.id, - 'payment_term_id': self.account_payment_term.id, - 'user_id': self.user_demo.id, - 'type': 'out_invoice', - 'currency_id': self.AED.id, - 'invoice_line_ids': [ - (0, 0, { - 'account_id': self.a_sale.id, - 'product_id': self.product_product_10.id, - 'name': 'Mouse Optical', - 'quantity': 1, - 'uom_id': self.product_uom_unit.id, - 'price_unit': 10, - 'invoice_line_tax_ids': [(6, 0, { - self.tax_22.id})] - }), - (0, 0, { - 'account_id': self.a_sale.id, - 'product_id': self.product_order_01.id, - 'name': 'Zed+ Antivirus', - 'quantity': 1, - 'uom_id': self.product_uom_unit.id, - 'price_unit': 4, - 'invoice_line_tax_ids': [(6, 0, { - self.tax_22.id})] - })], - }) - invoice.action_invoice_open() - res = self.run_wizard(invoice.id) - attachment = self.attach_model.browse(res['res_id']) - self.set_e_invoice_file_id(attachment, 'IT06363391001_00009.xml') - - xml_content = base64.decodebytes(attachment.datas) - self.check_content(xml_content, 'IT06363391001_00009.xml') - def test_10_xml_export(self): # invoice with descriptive line self.set_sequences(10, '2019-08-07') @@ -599,6 +553,66 @@ def test_13_xml_export(self): xml_content = base64.decodebytes(attachment.datas) self.check_content(xml_content, 'IT06363391001_00013.xml') + def test_15_xml_export(self): + """ + create an invoice in USD + + expect an XML with values in EUR + """ + + usd = self.env.ref("base.USD") + self.env["res.currency.rate"].create( + { + "name": "2021-12-16", + "rate": 1.17, + "currency_id": usd.id, + "company_id": self.env.user.company_id.id, + } + ) + self.set_sequences(1, '2021-12-16') + self.tax_00_ns.kind_id = self.env.ref("l10n_it_account_tax_kind.n3_2") + invoice = self.invoice_model.create({ + 'type': 'out_invoice', + 'currency_id': usd.id, + 'date_invoice': '2021-12-16', + 'partner_id': self.res_partner_fatturapa_0.id, + 'payment_term_id': self.account_payment_term.id, + 'journal_id': self.sales_journal.id, + 'account_id': self.a_recv.id, + 'user_id': self.user_demo.id, + 'invoice_line_ids': [ + (0, 0, { + 'product_id': self.product_product_10.id, + 'account_id': self.a_sale.id, + 'name': 'Cabinet with Doors', + 'quantity': 1, + 'price_unit': 14.00, + 'uom_id': self.product_uom_unit.id, + 'invoice_line_tax_ids': [(6, 0, { + self.tax_00_ns.id})] + })], + }) + invoice.action_invoice_open() + self.assertEqual(invoice.invoice_line_ids[0].price_unit, 14.00) + + invoice.company_id.xml_divisa_value = "force_eur" + res = self.run_wizard(invoice.id) + attachment = self.attach_model.browse(res['res_id']) + self.set_e_invoice_file_id(attachment, 'IT06363391001_00015.xml') + xml_content = base64.decodebytes(attachment.datas) + with open("/tmp/IT06363391001_00015.xml", "wb") as o: + o.write(xml_content) + self.check_content(xml_content, 'IT06363391001_00015.xml') + attachment.unlink() + + invoice.company_id.xml_divisa_value = "keep_orig" + res = self.run_wizard(invoice.id) + attachment = self.attach_model.browse(res['res_id']) + self.set_e_invoice_file_id(attachment, 'IT06363391001_00015a.xml') + xml_content = base64.decodebytes(attachment.datas) + self.check_content(xml_content, 'IT06363391001_00015a.xml') + attachment.unlink() + def test_unlink(self): e_invoice = self._create_e_invoice() e_invoice.unlink() diff --git a/l10n_it_fatturapa_out/views/company_view.xml b/l10n_it_fatturapa_out/views/company_view.xml index 2141410417ba..cef0a60b0b96 100644 --- a/l10n_it_fatturapa_out/views/company_view.xml +++ b/l10n_it_fatturapa_out/views/company_view.xml @@ -10,7 +10,15 @@
diff --git a/l10n_it_fatturapa_out/wizard/wizard_export_fatturapa.py b/l10n_it_fatturapa_out/wizard/wizard_export_fatturapa.py index 00eacdc25cda..3eb005144ec2 100644 --- a/l10n_it_fatturapa_out/wizard/wizard_export_fatturapa.py +++ b/l10n_it_fatturapa_out/wizard/wizard_export_fatturapa.py @@ -91,6 +91,20 @@ class WizardExportFatturapa(models.TransientModel): _name = "wizard.export.fatturapa" _description = "Export E-invoice" + @api.model + def _to_EUR(self, amount, invoice): + currency = invoice.currency_id + euro = self.env.ref('base.EUR') + if currency == euro: + return amount + return currency._convert( + amount, + euro, + invoice.company_id, + invoice.date_invoice, + False + ) + @api.model def _domain_ir_values(self): model_name = self.env.context.get('active_model', False) @@ -545,9 +559,13 @@ def setDatiGeneraliDocumento(self, invoice, body): ImportoTotaleDocumento = invoice.amount_total if invoice.split_payment: ImportoTotaleDocumento += invoice.amount_sp + Divisa = invoice.currency_id.name + if not invoice.company_id.xml_divisa_value == 'keep_orig': + Divisa = self.env.ref('base.EUR').name + ImportoTotaleDocumento = self._to_EUR(ImportoTotaleDocumento, invoice) body.DatiGenerali.DatiGeneraliDocumento = DatiGeneraliDocumentoType( TipoDocumento=TipoDocumento, - Divisa=invoice.currency_id.name, + Divisa=Divisa, Data=invoice.date_invoice, Numero=invoice.number, ImportoTotaleDocumento='%.2f' % float_round(ImportoTotaleDocumento, 2)) @@ -662,7 +680,7 @@ def setDettaglioLinea( aliquota = line.invoice_line_tax_ids[0].amount AliquotaIVA = '%.2f' % float_round(aliquota, 2) line.ftpa_line_number = line_no - prezzo_unitario = self._get_prezzo_unitario(line) + prezzo_unitario = self._to_EUR(self._get_prezzo_unitario(line), line.invoice_id) DettaglioLinea = DettaglioLineeType( NumeroLinea=str(line_no), Descrizione=encode_for_export(line.name, 1000), @@ -672,8 +690,19 @@ def setDettaglioLinea( qta=line.quantity, precision=uom_precision), UnitaMisura=line.uom_id and ( unidecode(line.uom_id.name)) or None, - PrezzoTotale='%.2f' % float_round(line.price_subtotal, 2), + PrezzoTotale='%.2f' % float_round( + self._to_EUR(line.price_subtotal, line.invoice_id), 2), AliquotaIVA=AliquotaIVA) + if line.currency_id != self.env.ref('base.EUR'): + AltriDatiGestionali = AltriDatiGestionaliType( + TipoDato="Valuta", + RiferimentoTesto=line.currency_id.name, + RiferimentoNumero='{prezzo:.{precision}f}'.format( + prezzo=self._get_prezzo_unitario(line), precision=price_precision), + RiferimentoData=line.invoice_id.date_invoice + ) + DettaglioLinea.AltriDatiGestionali.append(AltriDatiGestionali) + DettaglioLinea.ScontoMaggiorazione.extend( self.setScontoMaggiorazione(line)) if aliquota == 0.0: @@ -734,8 +763,9 @@ def setDatiRiepilogo(self, invoice, body): tax = tax_line.tax_id riepilogo = DatiRiepilogoType( AliquotaIVA='%.2f' % float_round(tax.amount, 2), - ImponibileImporto='%.2f' % float_round(tax_line.base, 2), - Imposta='%.2f' % float_round(tax_line.amount, 2) + ImponibileImporto='%.2f' % float_round( + self._to_EUR(tax_line.base, invoice), 2), + Imposta='%.2f' % float_round(self._to_EUR(tax_line.amount, invoice), 2) ) if tax.amount == 0.0: if not tax.kind_id: @@ -777,8 +807,10 @@ def setDatiPagamento(self, invoice, body): move_line_pool = self.env['account.move.line'] for move_line_id in payment_line_ids: move_line = move_line_pool.browse(move_line_id) - ImportoPagamento = '%.2f' % float_round( - move_line.amount_currency or move_line.debit, 2) + ImportoPagamento = move_line.amount_currency or move_line.debit + if not invoice.company_id.xml_divisa_value == 'keep_orig': + ImportoPagamento = self._to_EUR(ImportoPagamento, invoice) + ImportoPagamento = '%.2f' % float_round(ImportoPagamento, 2) # Create with only mandatory fields DettaglioPagamento = DettaglioPagamentoType( ModalitaPagamento=(