diff --git a/l10n_it_fatturapa_out/data/invoice_it_template.xml b/l10n_it_fatturapa_out/data/invoice_it_template.xml
index 2a4ef2bd1922..54e3138e782e 100644
--- a/l10n_it_fatturapa_out/data/invoice_it_template.xml
+++ b/l10n_it_fatturapa_out/data/invoice_it_template.xml
@@ -170,7 +170,9 @@ e 'line' per riga di fattura (a seconda del livello in cui sono chiamati)
-
+ INVCONT
+
+ Valuta
+
+
+
+
@@ -217,9 +229,11 @@ e 'line' per riga di fattura (a seconda del livello in cui sono chiamati)
+
-
+
-
+
+ -->
+
@@ -600,10 +627,10 @@ e 'line' per riga di fattura (a seconda del livello in cui sono chiamati)
+
+
diff --git a/l10n_it_fatturapa_out/models/company.py b/l10n_it_fatturapa_out/models/company.py
index fcbf2fe3c24c..3ba9073a0dbc 100644
--- a/l10n_it_fatturapa_out/models/company.py
+++ b/l10n_it_fatturapa_out/models/company.py
@@ -1,9 +1,12 @@
# Copyright 2019 Roberto Fichera
+# Copyright 2022 Marco Colombo
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo import _, api, fields, models
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):
"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:
@@ -33,12 +45,20 @@ class AccountConfigSettings(models.TransientModel):
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..0aab977ff77b
--- /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/12/0001
+ 14.00
+ SI
+
+
+
+
+ 1
+ Cabinet with Doors
+ 1.000
+ Unit(s)
+ 14.00000
+ 14.00
+ 0.00
+ N3.2
+
+ Valuta
+ USD
+ 16.38000
+ 2021-12-16
+
+
+
+ 0.00
+ N3.2
+ 14.00
+ 0.00
+ Not subject to VAT law
+
+
+
+ TP02
+
+ MP05
+ 2022-01-31
+ 14.00
+
+
+
+
diff --git a/l10n_it_fatturapa_out/tests/data/IT06363391001_00016.xml b/l10n_it_fatturapa_out/tests/data/IT06363391001_00016.xml
new file mode 100644
index 000000000000..ca62e8138b71
--- /dev/null
+++ b/l10n_it_fatturapa_out/tests/data/IT06363391001_00016.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+ IT
+ 06363391001
+
+ 00016
+ 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/12/0001
+ 16.38
+ SI
+
+
+
+
+ 1
+ Cabinet with Doors
+ 1.000
+ Unit(s)
+ 14.00000
+ 14.00
+ 0.00
+ N3.2
+
+ Valuta
+ USD
+ 16.38000
+ 2021-12-16
+
+
+
+ 0.00
+ N3.2
+ 14.00
+ 0.00
+ Not subject to VAT law
+
+
+
+ TP02
+
+ MP05
+ 2022-01-31
+ 16.38
+
+
+
+
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 7cf3b108c125..efcfdb971039 100644
--- a/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py
+++ b/l10n_it_fatturapa_out/tests/test_fatturapa_xml_validation.py
@@ -452,62 +452,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(
- {
- "name": "INV/2018/0018",
- "invoice_date": "2018-01-07",
- "partner_id": partner.id,
- "journal_id": self.sales_journal.id,
- # "account_id": self.a_recv.id,
- "invoice_payment_term_id": self.account_payment_term.id,
- "user_id": self.user_demo.id,
- "move_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,
- "product_uom_id": self.product_uom_unit.id,
- "price_unit": 10,
- "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,
- "product_uom_id": self.product_uom_unit.id,
- "price_unit": 4,
- "tax_ids": [(6, 0, {self.tax_22.id})],
- },
- ),
- ],
- }
- )
- invoice._post()
- 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")
@@ -750,6 +694,61 @@ def test_14_xml_export(self):
xml_content = base64.decodebytes(attachment.datas)
self.check_content(xml_content, "IT06363391001_00014.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": fields.Date.from_string("2021-12-16"),
+ "rate": 1.17,
+ "currency_id": usd.id,
+ "company_id": self.env.company.id,
+ }
+ )
+
+ invoice_form = Form(
+ self.env["account.move"].with_context({"default_move_type": "out_invoice"})
+ )
+ invoice_form.currency_id = usd
+ invoice_form.partner_id = self.res_partner_fatturapa_0
+ invoice_form.name = "INV/2021/12/0001"
+ invoice_form.date = fields.Date.from_string("2021-12-16")
+ invoice_form.invoice_date = fields.Date.from_string("2021-12-16")
+ invoice_form.invoice_payment_term_id = self.account_payment_term
+
+ self.tax_00_ns.kind_id = self.env.ref("l10n_it_account_tax_kind.n3_2")
+
+ with invoice_form.line_ids.new() as line_form:
+ line_form.product_id = self.product_product_10
+ line_form.account_id = self.a_sale
+ line_form.tax_ids.clear()
+ line_form.tax_ids.add(self.tax_00_ns)
+ invoice = invoice_form.save()
+ invoice.action_post()
+
+ 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)
+ 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_00016.xml")
+
+ xml_content = base64.decodebytes(attachment.datas)
+ self.check_content(xml_content, "IT06363391001_00016.xml")
+
def test_no_tax_fail(self):
"""
- create an invoice with a product line without taxes
diff --git a/l10n_it_fatturapa_out/views/company_view.xml b/l10n_it_fatturapa_out/views/company_view.xml
index f50771ba8ac1..4af49e26c90a 100644
--- a/l10n_it_fatturapa_out/views/company_view.xml
+++ b/l10n_it_fatturapa_out/views/company_view.xml
@@ -15,10 +15,20 @@
position="after"
>
-
+
+
+
+
+
diff --git a/l10n_it_fatturapa_out/wizard/efattura.py b/l10n_it_fatturapa_out/wizard/efattura.py
index fa5e7d0f696d..8cb2a07a7ae0 100644
--- a/l10n_it_fatturapa_out/wizard/efattura.py
+++ b/l10n_it_fatturapa_out/wizard/efattura.py
@@ -99,7 +99,7 @@ def format_phone(number):
return number
return False
- def format_price(line, sign=1):
+ def format_price(line, sign=1, original_currency=False):
res = line.price_unit
if line.tax_ids and line.tax_ids[0].price_include:
res = line.price_unit / (1 + (line.tax_ids[0].amount / 100))
@@ -114,6 +114,10 @@ def format_price(line, sign=1):
if line.quantity < 0:
res = -res
+ # force EUR unless we want the original currency
+ if not original_currency:
+ res = fpa_to_eur(res, line.move_id)
+
# XXX arrotondamento?
res = "{prezzo:.{precision}f}".format(
prezzo=sign * res, precision=price_precision
@@ -260,6 +264,20 @@ def get_importo(line):
return False
return line.price_unit * line.discount / 100
+ def get_importo_totale(invoice):
+ # wrapper to a method in wizard (for better overriding)
+ wiz = self.env["wizard.export.fatturapa"]
+ return wiz.getImportoTotale(invoice)
+
+ def fpa_to_eur(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, False
+ )
+
if self.partner_id.commercial_partner_id.is_pa:
# check value code
code = self.partner_id.ipa_code
@@ -291,9 +309,11 @@ def get_importo(line):
"unidecode": unidecode,
"wizard": self.wizard,
"get_importo": get_importo,
+ "get_importo_totale": get_importo_totale,
"all_taxes": {
invoice.id: get_all_taxes(invoice) for invoice in self.invoices
},
+ "fpa_to_eur": fpa_to_eur,
}
return template_values
diff --git a/l10n_it_fatturapa_out/wizard/wizard_export_fatturapa.py b/l10n_it_fatturapa_out/wizard/wizard_export_fatturapa.py
index 056d09df3c53..a2b56ed95395 100644
--- a/l10n_it_fatturapa_out/wizard/wizard_export_fatturapa.py
+++ b/l10n_it_fatturapa_out/wizard/wizard_export_fatturapa.py
@@ -74,6 +74,19 @@ def getPartnerId(self, invoice_ids):
return partner
+ @api.model
+ def getImportoTotale(self, invoice):
+ """Entry point for other modules to override computation of
+ ImportoTotaleDocumento"""
+ # this requires a better refactoring. We SHOULD be able to use
+ # amount_total as is, w/o further computations.
+ # At the moment, some modules store the total amount to be paid
+ # by the customer, and handle the printed total in the print
+ # report (there are cases in which the partner does not have to
+ # paid the VAT, yet its amount has to be printed out and included
+ # in the total amount of the invoice)
+ return invoice.amount_total
+
def group_invoices_by_partner(self):
def split_list(my_list, size):
it = iter(my_list)