Odoo18-Base/addons/l10n_it_edi/tests/test_edi_export.py
2025-01-06 10:57:38 +07:00

498 lines
20 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
from odoo.tests import tagged
from odoo.addons.l10n_it_edi.tests.common import TestItEdi
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestItEdiExport(TestItEdi):
@classmethod
def setUpClass(cls):
super().setUpClass()
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,
})
def test_vat_not_equals_codice(self):
self.company.partner_id.vat = '01698911003'
self.company.l10n_it_codice_fiscale = '07149930583'
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_a.id,
'invoice_line_ids': [
Command.create({
'name': 'line1',
'price_unit': 800.40,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'invoice_vat_not_equals_codice.xml')
def test_export_invoice_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).
"""
tax_included = self.env['account.tax'].with_company(self.company).create({
'name': "22% price included tax",
'amount': 22.0,
'amount_type': 'percent',
'price_include_override': 'tax_included',
})
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_a.id,
'invoice_line_ids': [
Command.create({
'name': "something price included",
'price_unit': 800.40,
'tax_ids': [Command.set(tax_included.ids)],
}),
Command.create({
'name': "something else price included",
'price_unit': 800.40,
'tax_ids': [Command.set(tax_included.ids)],
}),
Command.create({
'name': "something not price included",
'price_unit': 800.40,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'invoice_price_included_taxes.xml')
def test_export_invoice_partially_discounted(self):
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_a.id,
'invoice_line_ids': [
Command.create({
'name': 'no discount',
'price_unit': 800.40,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
Command.create({
'name': 'special discount',
'price_unit': 800.40,
'discount': 50,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
Command.create({
'name': "an offer you can't refuse",
'price_unit': 800.40,
'discount': 100,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'invoice_partially_discounted.xml')
def test_invoice_fully_discounted(self):
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_a.id,
'invoice_line_ids': [
Command.create({
'name': 'nothing shady just a gift for my friend',
'price_unit': 800.40,
'discount': 100,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'invoice_fully_discounted.xml')
def test_invoice_non_latin_and_latin(self):
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_a.id,
'invoice_line_ids': [
Command.create({
'name': 'ʢ◉ᴥ◉ʡ',
'price_unit': 800.40,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
Command.create({
'name': '--',
'price_unit': 800.40,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
Command.create({
'name': 'this should be the same as it was',
'price_unit': 800.40,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'invoice_non_latin_and_latin.xml')
def test_invoice_below_400_codice_simplified(self):
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_no_address_codice.id,
'invoice_line_ids': [
Command.create({
'name': 'cheap_line',
'price_unit': 100.00,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
Command.create({
'name': 'cheap_line_2',
'quantity': 2,
'price_unit': 10.0,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'invoice_below_400_codice_simplified.xml')
def test_invoice_total_400_VAT_simplified(self):
self.company.l10n_it_codice_fiscale = '07149930583'
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_no_address_VAT.id,
'invoice_line_ids': [
Command.create({
'name': '400_line',
'price_unit': 327.87,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'invoice_total_400_VAT_simplified.xml')
def test_invoice_more_400_simplified(self):
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_no_address_codice.id,
'invoice_line_ids': [
Command.create({
'name': 'standard_line',
'price_unit': 800.40,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
self.assertEqual(['l10n_it_edi_partner_address_missing'], list(invoice._l10n_it_edi_export_data_check().keys()))
def test_invoice_non_domestic_simplified(self):
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.american_partner.id,
'invoice_line_ids': [
Command.create({
'name': 'cheap_line',
'price_unit': 100.00,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
self.assertEqual(['l10n_it_edi_partner_address_missing'], list(invoice._l10n_it_edi_export_data_check().keys()))
def test_bill_refund_no_reconcile(self):
Move = self.env['account.move'].with_company(self.company)
purchase_tax = self.env['account.tax'].with_company(self.company).create({
'name': 'Tax 4%',
'amount': 4.0,
'amount_type': 'percent',
'type_tax_use': 'purchase',
'invoice_repartition_line_ids': self.repartition_lines(
self.RepartitionLine(100, 'base', ('+03', )),
self.RepartitionLine(100, 'tax', ('+5v', ))),
'refund_repartition_line_ids': self.repartition_lines(
self.RepartitionLine(100, 'base', ('-03', )),
self.RepartitionLine(100, 'tax', False))
})
values = {
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_a.id,
'partner_bank_id': self.test_bank.id,
'invoice_line_ids': [
Command.create({
'name': "Product A",
'price_unit': 800.40,
'tax_ids': [Command.set(purchase_tax.ids)],
})
]
}
bill = Move.create({'move_type': 'in_invoice', **values})
credit_note = Move.create({'move_type': 'in_refund', **values})
(bill + credit_note).action_post()
credit_note.reversed_entry_id = bill
self._assert_export_invoice(credit_note, 'credit_note_refund_no_reconcile.xml')
def test_invoice_negative_price(self):
tax_10 = self.env['account.tax'].create({
'name': '10% tax',
'amount': 10.0,
'amount_type': 'percent',
'company_id': self.company.id,
})
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_a.id,
'partner_bank_id': self.test_bank.id,
'invoice_line_ids': [
Command.create({
'name': 'standard_line',
'quantity': 2,
'price_unit': 800.40,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
Command.create({
'name': 'negative_line',
'price_unit': -100.0,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
with self.subTest('invoice'):
self._assert_export_invoice(invoice, 'invoice_negative_price.xml')
credit_note = invoice._reverse_moves([{
'invoice_date': '2022-03-24',
}])
credit_note.action_post()
with self.subTest('credit note'):
self._assert_export_invoice(credit_note, 'credit_note_negative_price.xml')
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_a.id,
'partner_bank_id': self.test_bank.id,
'invoice_line_ids': [
Command.create({
'name': 'standard_line',
'price_unit': 800.40,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
Command.create({
'name': 'negative_line',
'price_unit': -100.0,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
# This negative line can't be dispatched, rejeted.
Command.create({
'name': 'negative_line_different_tax',
'price_unit': -50.0,
'tax_ids': [Command.set(tax_10.ids)],
}),
],
})
invoice.action_post()
with self.subTest('invoice_different_taxes'):
self._assert_export_invoice(invoice, 'invoice_negative_price_different_taxes.xml')
def test_invoice_more_decimal_price_unit(self):
decimal_precision_name = self.env['account.move.line']._fields['price_unit']._digits
decimal_precision = self.env['decimal.precision'].search([('name', '=', decimal_precision_name)])
decimal_precision.digits = 4
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2022-03-24',
'invoice_date_due': '2022-03-24',
'partner_id': self.italian_partner_a.id,
'partner_bank_id': self.test_bank.id,
'invoice_line_ids': [
Command.create({
'name': 'standard_line',
'price_unit': 3.156,
'quantity': 10,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'invoice_decimal_precision_product.xml')
def test_send_and_print_invoice_with_fallback_pdf(self):
self.italian_partner_a.zip = False # invalid configuration for partner -> proforma pdf
invoice = self.env['account.move'].with_company(self.company).create({
'partner_id': self.italian_partner_a.id,
'move_type': 'out_invoice',
'invoice_line_ids': [
Command.create({
'name': 'Example Product',
'price_unit': 500,
'tax_ids': [Command.set(self.default_tax.ids)],
}),
],
})
invoice.action_post()
invoice._generate_and_send()
self.assertIn(
'INV_2024_00001_proforma.pdf',
invoice.attachment_ids.mapped('name'),
"The proforma PDF should have been generated.",
)
def test_export_foreign_currency(self):
tax_zero_percent_us = self.env['account.tax'].with_company(self.company).create({
'name': '0 % US',
'amount': 0.0,
'amount_type': 'percent',
'l10n_it_exempt_reason': 'N3.1',
'l10n_it_law_reference': 'Art. 8, c.1, lett.a - D.P.R. 633/1972',
})
american_partner_b = self.env['res.partner'].create({
'name': 'US Partner',
'city': 'Test city',
'country_id': self.env.ref('base.us').id,
'zip': '12345',
'street': '123 Rainbow Road',
'is_company': True,
})
# =============== create invoices ===============
usd = self.env.ref('base.USD')
self.env['res.currency.rate'].create({
'name': '2024-08-06',
'rate': 1.0789,
'currency_id': usd.id,
'company_id': self.company.id,
})
# usd simple discount % on the product
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2024-08-07',
'invoice_date_due': '2024-08-07',
'partner_id': american_partner_b.id,
'currency_id': usd.id,
'invoice_line_ids': [
Command.create({
'name': 'A productive product',
'price_unit': 1068.11,
'quantity': 1,
'tax_ids': [Command.set(tax_zero_percent_us.ids)],
'discount': 15,
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'export_foreign_currency_simple_discount.xml')
# usd discount both on product in % + a global one (negative aml)
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2024-08-06',
'invoice_date_due': '2024-08-06',
'partner_id': american_partner_b.id,
'currency_id': usd.id,
'invoice_line_ids': [
Command.create({
'name': 'A productive product',
'price_unit': 712.07,
'quantity': 1,
'tax_ids': [Command.set(tax_zero_percent_us.ids)],
'discount': 15,
}),
Command.create({
'name': 'A global discount',
'price_unit': -100,
'quantity': 1,
'tax_ids': [Command.set(tax_zero_percent_us.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'export_foreign_currency_global_simple_discount.xml')
# usd discount global (negative aml)
invoice = self.env['account.move'].with_company(self.company).create({
'move_type': 'out_invoice',
'invoice_date': '2024-08-07',
'invoice_date_due': '2024-08-07',
'partner_id': american_partner_b.id,
'currency_id': usd.id,
'invoice_line_ids': [
Command.create({
'name': 'A productive product',
'price_unit': 712.07,
'quantity': 1,
'tax_ids': [Command.set(tax_zero_percent_us.ids)],
}),
Command.create({
'name': 'A global discount',
'price_unit': -200,
'quantity': 1,
'tax_ids': [Command.set(tax_zero_percent_us.ids)],
}),
],
})
invoice.action_post()
self._assert_export_invoice(invoice, 'export_foreign_currency_global_discount.xml')