Odoo18-Base/addons/l10n_it_edi/tests/test_edi_export.py
2025-03-10 11:12:23 +07:00

760 lines
34 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import datetime
from lxml import etree
from odoo.tests import tagged
from odoo.addons.l10n_it_edi.tests.common import TestItEdi
from odoo.exceptions import UserError
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestItEdiExport(TestItEdi):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.price_included_tax = cls.env['account.tax'].create({
'name': '22% price included tax',
'amount': 22.0,
'amount_type': 'percent',
'price_include': True,
'include_base_amount': True,
'company_id': cls.company.id,
})
cls.tax_10 = cls.env['account.tax'].create({
'name': '10% tax',
'amount': 10.0,
'amount_type': 'percent',
'company_id': cls.company.id,
})
cls.tax_zero_percent_hundred_percent_repartition = cls.env['account.tax'].create({
'name': 'all of nothing',
'amount': 0,
'amount_type': 'percent',
'company_id': cls.company.id,
'invoice_repartition_line_ids': [
(0, 0, {'factor_percent': 100, 'repartition_type': 'base'}),
(0, 0, {'factor_percent': 100, 'repartition_type': 'tax'}),
],
'refund_repartition_line_ids': [
(0, 0, {'factor_percent': 100, 'repartition_type': 'base'}),
(0, 0, {'factor_percent': 100, 'repartition_type': 'tax'}),
],
})
cls.tax_zero_percent_zero_percent_repartition = cls.env['account.tax'].create({
'name': 'none of nothing',
'amount': 0,
'amount_type': 'percent',
'company_id': cls.company.id,
'invoice_repartition_line_ids': [
(0, 0, {'factor_percent': 100, 'repartition_type': 'base'}),
(0, 0, {'factor_percent': 0, 'repartition_type': 'tax'}),
],
'refund_repartition_line_ids': [
(0, 0, {'factor_percent': 100, 'repartition_type': 'base'}),
(0, 0, {'factor_percent': 0, 'repartition_type': 'tax'}),
],
})
cls.italian_partner_b = cls.env['res.partner'].create({
'name': 'pa partner',
'vat': 'IT06655971007',
'l10n_it_codice_fiscale': '06655971007',
'l10n_it_pa_index': '123456',
'country_id': cls.env.ref('base.it').id,
'street': 'Via Test PA',
'zip': '32121',
'city': 'PA Town',
'is_company': True
})
cls.italian_partner_no_address_codice = cls.env['res.partner'].create({
'name': 'Alessi',
'l10n_it_codice_fiscale': '00465840031',
'is_company': True,
})
cls.italian_partner_no_address_VAT = cls.env['res.partner'].create({
'name': 'Alessi',
'vat': 'IT00465840031',
'is_company': True,
})
cls.american_partner = cls.env['res.partner'].create({
'name': 'Alessi',
'vat': '00465840031',
'country_id': cls.env.ref('base.us').id,
'is_company': True,
})
cls.standard_line_below_400 = {
'name': 'cheap_line',
'quantity': 1,
'price_unit': 100.00,
'tax_ids': [(6, 0, [cls.company.account_sale_tax_id.id])]
}
cls.standard_line_400 = {
'name': '400_line',
'quantity': 1,
'price_unit': 327.87,
'tax_ids': [(6, 0, [cls.company.account_sale_tax_id.id])]
}
cls.price_included_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_a.id,
'partner_bank_id': cls.test_bank.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line,
'name': 'something price included',
'tax_ids': [(6, 0, [cls.price_included_tax.id])]
}),
(0, 0, {
**cls.standard_line,
'name': 'something else price included',
'tax_ids': [(6, 0, [cls.price_included_tax.id])]
}),
(0, 0, {
**cls.standard_line,
'name': 'something not price included',
}),
],
})
cls.partial_discount_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_a.id,
'partner_bank_id': cls.test_bank.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line,
'name': 'no discount',
}),
(0, 0, {
**cls.standard_line,
'name': 'special discount',
'discount': 50,
}),
(0, 0, {
**cls.standard_line,
'name': "an offer you can't refuse",
'discount': 100,
}),
],
})
cls.full_discount_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_a.id,
'partner_bank_id': cls.test_bank.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line,
'name': 'nothing shady just a gift for my friend',
'discount': 100,
}),
],
})
cls.non_latin_and_latin_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_a.id,
'partner_bank_id': cls.test_bank.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line,
'name': 'ʢ◉ᴥ◉ʡ',
}),
(0, 0, {
**cls.standard_line,
'name': '-',
}),
(0, 0, {
**cls.standard_line,
'name': 'this should be the same as it was',
}),
],
})
cls.below_400_codice_simplified_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_no_address_codice.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line_below_400,
}),
(0, 0, {
**cls.standard_line_below_400,
'name': 'cheap_line_2',
'quantity': 2,
'price_unit': 10.0,
}),
],
})
cls.total_400_VAT_simplified_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_no_address_VAT.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line_400,
}),
],
})
cls.more_400_simplified_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_no_address_codice.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line,
}),
],
})
cls.non_domestic_simplified_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.american_partner.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line_below_400,
}),
],
})
cls.zero_tax_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_a.id,
'partner_bank_id': cls.test_bank.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line,
'name': 'line with tax of 0% with repartition line of 100% ',
'tax_ids': [(6, 0, [cls.tax_zero_percent_hundred_percent_repartition.id])],
}),
(0, 0, {
**cls.standard_line,
'name': 'line with tax of 0% with repartition line of 0% ',
'tax_ids': [(6, 0, [cls.tax_zero_percent_zero_percent_repartition.id])],
}),
],
})
cls.negative_price_invoice = cls.env['account.move'].with_company(cls.company).create({
'move_type': 'out_invoice',
'invoice_date': datetime.date(2022, 3, 24),
'invoice_date_due': datetime.date(2022, 3, 24),
'partner_id': cls.italian_partner_a.id,
'partner_bank_id': cls.test_bank.id,
'invoice_line_ids': [
(0, 0, {
**cls.standard_line,
}),
(0, 0, {
**cls.standard_line,
'name': 'negative_line',
'price_unit': -100.0,
}),
(0, 0, {
**cls.standard_line,
'name': 'negative_line_different_tax',
'price_unit': -50.0,
'tax_ids': [(6, 0, [cls.tax_10.id])]
}),
],
})
cls.negative_price_credit_note = cls.negative_price_invoice.with_company(cls.company)._reverse_moves([{
'invoice_date': datetime.date(2022, 3, 24),
}])
# post the invoices
cls.price_included_invoice._post()
cls.partial_discount_invoice._post()
cls.full_discount_invoice._post()
cls.non_latin_and_latin_invoice._post()
cls.below_400_codice_simplified_invoice._post()
cls.total_400_VAT_simplified_invoice._post()
cls.zero_tax_invoice._post()
cls.negative_price_invoice._post()
cls.negative_price_credit_note._post()
def test_price_included_taxes(self):
""" When the tax is price included, there should be a rounding value added to the xml, if the sum(subtotals) * tax_rate is not
equal to taxable base * tax rate (there is a constraint in the edi where taxable base * tax rate = tax amount, but also
taxable base = sum(subtotals) + rounding amount)
"""
# In this case, the first two lines use a price_include tax the
# subtotals should be 800.40 / (100 + 22.0) * 100 = 656.065564..,
# where 22.0 is the tax rate.
#
# Since the subtotals are rounded we actually have 656.07
lines = self.price_included_invoice.line_ids
price_included_lines = lines.filtered(lambda line: line.tax_ids == self.price_included_tax)
self.assertEqual([line.price_subtotal for line in price_included_lines], [656.07, 656.07])
# So the taxable a base the edi expects (for this tax) is actually 1312.14
price_included_tax_line = lines.filtered(lambda line: line.tax_line_id == self.price_included_tax)
self.assertEqual(price_included_tax_line.tax_base_amount, 1312.14)
# The tax amount of the price included tax should be:
# per line: 800.40 - (800.40 / (100 + 22) * 100) = 144.33
# tax amount: 144.33 * 2 = 288.66
self.assertEqual(price_included_tax_line.amount_currency, -288.66)
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
<xpath expr="//FatturaElettronicaBody//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<DettaglioLinee>
<NumeroLinea>1</NumeroLinea>
<Descrizione>something price included</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>656.070000</PrezzoUnitario>
<PrezzoTotale>656.07</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>2</NumeroLinea>
<Descrizione>something else price included</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>656.070000</PrezzoUnitario>
<PrezzoTotale>656.07</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>3</NumeroLinea>
<Descrizione>something not price included</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
<AliquotaIVA>22.00</AliquotaIVA>
<Arrotondamento>-0.04909091</Arrotondamento>
<ImponibileImporto>1312.09</ImponibileImporto>
<Imposta>288.66</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
<DatiRiepilogo>
<AliquotaIVA>22.00</AliquotaIVA>
<ImponibileImporto>800.40</ImponibileImporto>
<Imposta>176.09</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
</DatiBeniServizi>
</xpath>
<xpath expr="//DettaglioPagamento//ImportoPagamento" position="inside">
2577.29
</xpath>
<xpath expr="//DatiGeneraliDocumento//ImportoTotaleDocumento" position="inside">
2577.29
</xpath>
''')
invoice_etree = etree.fromstring(self.edi_format._l10n_it_edi_export_invoice_as_xml(self.price_included_invoice))
# Remove the attachment and its details
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_partially_discounted_invoice(self):
# The EDI can account for discounts, but a line with, for example, a 100% discount should still have
# a corresponding tax with a base amount of 0
invoice_etree = etree.fromstring(self.edi_format._l10n_it_edi_export_invoice_as_xml(self.partial_discount_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
<xpath expr="//FatturaElettronicaBody//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<DettaglioLinee>
<NumeroLinea>1</NumeroLinea>
<Descrizione>no discount</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>2</NumeroLinea>
<Descrizione>special discount</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<ScontoMaggiorazione>
<Tipo>SC</Tipo>
<Percentuale>50.00</Percentuale>
</ScontoMaggiorazione>
<PrezzoTotale>400.20</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>3</NumeroLinea>
<Descrizione>an offer you can't refuse</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<ScontoMaggiorazione>
<Tipo>SC</Tipo>
<Percentuale>100.00</Percentuale>
</ScontoMaggiorazione>
<PrezzoTotale>0.00</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
<AliquotaIVA>22.00</AliquotaIVA>
<ImponibileImporto>1200.60</ImponibileImporto>
<Imposta>264.13</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
</DatiBeniServizi>
</xpath>
<xpath expr="//DettaglioPagamento//ImportoPagamento" position="inside">
1464.73
</xpath>
<xpath expr="//DatiGeneraliDocumento//ImportoTotaleDocumento" position="inside">
1464.73
</xpath>
''')
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_fully_discounted_inovice(self):
invoice_etree = etree.fromstring(self.edi_format._l10n_it_edi_export_invoice_as_xml(self.full_discount_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
<xpath expr="//FatturaElettronicaBody//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<DettaglioLinee>
<NumeroLinea>1</NumeroLinea>
<Descrizione>nothing shady just a gift for my friend</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<ScontoMaggiorazione>
<Tipo>SC</Tipo>
<Percentuale>100.00</Percentuale>
</ScontoMaggiorazione>
<PrezzoTotale>0.00</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
<AliquotaIVA>22.00</AliquotaIVA>
<ImponibileImporto>0.00</ImponibileImporto>
<Imposta>0.00</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
</DatiBeniServizi>
</xpath>
<xpath expr="//DettaglioPagamento//ImportoPagamento" position="inside">
0.00
</xpath>
<xpath expr="//DatiGeneraliDocumento//ImportoTotaleDocumento" position="inside">
0.00
</xpath>
''')
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_non_latin_and_latin_invoice(self):
invoice_etree = etree.fromstring(self.edi_format._l10n_it_edi_export_invoice_as_xml(self.non_latin_and_latin_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
<xpath expr="//FatturaElettronicaBody//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<DettaglioLinee>
<NumeroLinea>1</NumeroLinea>
<Descrizione>?????</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>2</NumeroLinea>
<Descrizione>?-</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>3</NumeroLinea>
<Descrizione>this should be the same as it was</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
<AliquotaIVA>22.00</AliquotaIVA>
<ImponibileImporto>2401.20</ImponibileImporto>
<Imposta>528.26</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
</DatiBeniServizi>
</xpath>
<xpath expr="//DettaglioPagamento//ImportoPagamento" position="inside">
2929.46
</xpath>
<xpath expr="//DatiGeneraliDocumento//ImportoTotaleDocumento" position="inside">
2929.46
</xpath>
''')
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_below_400_codice_simplified_invoice(self):
invoice_etree = etree.fromstring(self.edi_format._l10n_it_edi_export_invoice_as_xml(self.below_400_codice_simplified_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_simplified_basis_xml),
'''
<xpath expr="//FatturaElettronicaHeader//CessionarioCommittente" position="inside">
<IdentificativiFiscali>
<CodiceFiscale>00465840031</CodiceFiscale>
</IdentificativiFiscali>
</xpath>
<xpath expr="//FatturaElettronicaBody//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<Descrizione>cheap_line</Descrizione>
<Importo>122.00</Importo>
<DatiIVA>
<Imposta>22.00</Imposta>
</DatiIVA>
</DatiBeniServizi>
<DatiBeniServizi>
<Descrizione>cheap_line_2</Descrizione>
<Importo>24.40</Importo>
<DatiIVA>
<Imposta>4.40</Imposta>
</DatiIVA>
</DatiBeniServizi>
</xpath>
''')
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_total_400_VAT_simplified_invoice(self):
invoice_etree = etree.fromstring(self.edi_format._l10n_it_edi_export_invoice_as_xml(self.total_400_VAT_simplified_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_simplified_basis_xml),
'''
<xpath expr="//FatturaElettronicaHeader//CessionarioCommittente" position="inside">
<IdentificativiFiscali>
<IdFiscaleIVA>
<IdPaese>IT</IdPaese>
<IdCodice>00465840031</IdCodice>
</IdFiscaleIVA>
</IdentificativiFiscali>
</xpath>
<xpath expr="//FatturaElettronicaBody//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<Descrizione>400_line</Descrizione>
<Importo>400.00</Importo>
<DatiIVA>
<Imposta>72.13</Imposta>
</DatiIVA>
</DatiBeniServizi>
</xpath>
''')
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_more_400_simplified_invoice(self):
with self.assertRaises(UserError):
self.more_400_simplified_invoice._post()
def test_non_domestic_simplified_invoice(self):
with self.assertRaises(UserError):
self.non_domestic_simplified_invoice._post()
def test_zero_percent_taxes(self):
invoice_etree = etree.fromstring(self.edi_format._l10n_it_edi_export_invoice_as_xml(self.zero_tax_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
<xpath expr="//FatturaElettronicaBody//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<DettaglioLinee>
<NumeroLinea>1</NumeroLinea>
<Descrizione>line with tax of 0% with repartition line of 100%</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>0.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>2</NumeroLinea>
<Descrizione>line with tax of 0% with repartition line of 0%</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>0.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
<AliquotaIVA>0.00</AliquotaIVA>
<ImponibileImporto>800.40</ImponibileImporto>
<Imposta>0.00</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
<DatiRiepilogo>
<AliquotaIVA>0.00</AliquotaIVA>
<ImponibileImporto>800.40</ImponibileImporto>
<Imposta>0.00</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
</DatiBeniServizi>
</xpath>
<xpath expr="//DettaglioPagamento//ImportoPagamento" position="inside">
1600.80
</xpath>
<xpath expr="//DatiGeneraliDocumento//ImportoTotaleDocumento" position="inside">
1600.80
</xpath>
'''
)
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_negative_price_invoice(self):
invoice_etree = etree.fromstring(self.edi_format._l10n_it_edi_export_invoice_as_xml(self.negative_price_invoice))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
'''
<xpath expr="//FatturaElettronicaBody//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<DettaglioLinee>
<NumeroLinea>1</NumeroLinea>
<Descrizione>standard_line</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>2</NumeroLinea>
<Descrizione>negative_line</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>-100.000000</PrezzoUnitario>
<PrezzoTotale>-100.00</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>3</NumeroLinea>
<Descrizione>negative_line_different_tax</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>-50.000000</PrezzoUnitario>
<PrezzoTotale>-50.00</PrezzoTotale>
<AliquotaIVA>10.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
<AliquotaIVA>22.00</AliquotaIVA>
<ImponibileImporto>700.40</ImponibileImporto>
<Imposta>154.09</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
<DatiRiepilogo>
<AliquotaIVA>10.00</AliquotaIVA>
<ImponibileImporto>-50.00</ImponibileImporto>
<Imposta>-5.00</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
</DatiBeniServizi>
</xpath>
<xpath expr="//DettaglioPagamento//ImportoPagamento" position="inside">
799.49
</xpath>
<xpath expr="//DatiGeneraliDocumento//ImportoTotaleDocumento" position="inside">
799.49
</xpath>
''')
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)
def test_negative_price_credit_note(self):
invoice_etree = etree.fromstring(self.edi_format._l10n_it_edi_export_invoice_as_xml(self.negative_price_credit_note))
expected_etree = self.with_applied_xpath(
etree.fromstring(self.edi_basis_xml),
f'''
<xpath expr="//DatiGeneraliDocumento/TipoDocumento" position="replace">
<TipoDocumento>TD04</TipoDocumento>
</xpath>
<xpath expr="//DatiGeneraliDocumento//ImportoTotaleDocumento" position="inside">
799.49
</xpath>
<xpath expr="//DatiGeneraliDocumento" position="after">
<DatiFattureCollegate>
<IdDocumento>{self.negative_price_invoice.name}</IdDocumento>
<Data>{self.negative_price_credit_note.invoice_date}</Data>
</DatiFattureCollegate>
</xpath>
<xpath expr="//DatiBeniServizi" position="replace">
<DatiBeniServizi>
<DettaglioLinee>
<NumeroLinea>1</NumeroLinea>
<Descrizione>standard_line</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>800.400000</PrezzoUnitario>
<PrezzoTotale>800.40</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>2</NumeroLinea>
<Descrizione>negative_line</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>-100.000000</PrezzoUnitario>
<PrezzoTotale>-100.00</PrezzoTotale>
<AliquotaIVA>22.00</AliquotaIVA>
</DettaglioLinee>
<DettaglioLinee>
<NumeroLinea>3</NumeroLinea>
<Descrizione>negative_line_different_tax</Descrizione>
<Quantita>1.00</Quantita>
<PrezzoUnitario>-50.000000</PrezzoUnitario>
<PrezzoTotale>-50.00</PrezzoTotale>
<AliquotaIVA>10.00</AliquotaIVA>
</DettaglioLinee>
<DatiRiepilogo>
<AliquotaIVA>22.00</AliquotaIVA>
<ImponibileImporto>700.40</ImponibileImporto>
<Imposta>154.09</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
<DatiRiepilogo>
<AliquotaIVA>10.00</AliquotaIVA>
<ImponibileImporto>-50.00</ImponibileImporto>
<Imposta>-5.00</Imposta>
<EsigibilitaIVA>I</EsigibilitaIVA>
</DatiRiepilogo>
</DatiBeniServizi>
</xpath>
<xpath expr="//DatiPagamento" position="replace"/>
''')
invoice_etree = self.with_applied_xpath(invoice_etree, "<xpath expr='.//Allegati' position='replace'/>")
self.assertXmlTreeEqual(invoice_etree, expected_etree)