odoo18/addons/l10n_gr_edi/tests/test_mydata_invoice.py

263 lines
12 KiB
Python

from lxml import etree
from odoo import Command
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.tests import tagged
from odoo.tools import misc
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestMyDATAInvoice(AccountTestInvoicingCommon):
@classmethod
@AccountTestInvoicingCommon.setup_country('gr')
def setUpClass(cls):
super().setUpClass()
cls.env.company.write({
'name': 'My Greece Company',
'vat': '047747270',
'l10n_gr_edi_test_env': True,
'l10n_gr_edi_aade_id': 'odoodev',
'l10n_gr_edi_aade_key': '20ea658627fd8c7d90594fe4601d3327',
})
cls.partner_a.write({
'country_id': cls.env.ref('base.gr').id,
'vat': '047747210',
})
cls.env['res.company'].create({
'name': 'Greece Partner A',
'partner_id': cls.partner_a.id,
'l10n_gr_edi_test_env': True,
})
cls.tax_24 = cls.env.ref("account.%s_l10n_gr_tax_s24_G" % cls.env.company.id)
cls.tax_13 = cls.env.ref("account.%s_l10n_gr_tax_s13_G" % cls.env.company.id)
cls.tax_0 = cls.env.ref("account.%s_l10n_gr_tax_s0_exempt" % cls.env.company.id)
def _create_mydata_invoice(
self,
inv_type='1.1',
tax_ids=False,
cls_category='category1_1',
cls_type='E3_561_001',
post=True,
paid=True,
**kwargs,
):
invoice = self.env['account.move'].create({
'move_type': 'out_invoice',
'partner_id': self.partner_a.id,
'invoice_date': '2024-01-01',
'date': '2024-01-01',
'l10n_gr_edi_inv_type': inv_type,
'invoice_line_ids': [Command.create({
'product_id': self.product_a.id,
'tax_ids': tax_ids or [Command.set(self.tax_24.ids)],
'l10n_gr_edi_cls_category': cls_category,
'l10n_gr_edi_cls_type': cls_type,
})],
**kwargs,
})
if post:
invoice.action_post()
if paid:
self.env['account.payment.register'] \
.with_context(active_ids=invoice.ids, active_model='account.move') \
.create({'payment_date': invoice.date}) \
._create_payments()
return invoice
def _create_mydata_bill(
self,
mydata_mark='400001924190891',
inv_type='13.1',
invoice_line_ids=False,
**kwargs,
):
if not invoice_line_ids:
invoice_line_ids = [
Command.create({
'product_id': self.product_a.id,
'tax_ids': [Command.set(self.tax_24.ids)],
'l10n_gr_edi_cls_category': 'category2_1',
'l10n_gr_edi_cls_type': 'E3_102_001',
}),
Command.create({
'product_id': self.product_b.id,
'tax_ids': [Command.set(self.tax_13.ids)],
'l10n_gr_edi_cls_category': 'category2_10',
'l10n_gr_edi_cls_type': 'E3_313_004',
}),
]
bill = self._create_mydata_invoice(
move_type='in_invoice',
inv_type=inv_type,
invoice_line_ids=invoice_line_ids,
**kwargs,
)
bill.l10n_gr_edi_document_ids = self.env['l10n_gr_edi.document'].create([{
'move_id': bill.id,
'mydata_mark': mydata_mark,
'state': 'bill_fetched',
}])
return bill
@staticmethod
def _add_address(partner):
partner.write({'zip': '10431', 'city': 'Athens'})
def assert_mydata_xml_tree(self, invoice, expected_file_path, send_classification=False):
if send_classification:
xml_template = 'l10n_gr_edi.mydata_expense_classification'
xml_vals = invoice._l10n_gr_edi_get_expense_classification_xml_vals()
else:
xml_template = 'l10n_gr_edi.mydata_invoice'
xml_vals = invoice._l10n_gr_edi_get_invoices_xml_vals()
xml_content = self.env['account.move']._l10n_gr_edi_generate_xml_content(xml_template, xml_vals)
xml_etree = self.get_xml_tree_from_string(xml_content)
expected_file_full_path = misc.file_path(f'{self.test_module}/tests/test_files/{expected_file_path}')
expected_etree = etree.parse(expected_file_full_path).getroot()
self.assertXmlTreeEqual(xml_etree, expected_etree)
def assert_mydata_error(self, invoice, expected_error_message):
"""
:param account.move invoice:
:param str expected_error_message:
"""
document = invoice.l10n_gr_edi_document_ids.sorted()[0]
self.assertRecordValues(document, [{
'state': 'invoice_error' if invoice.is_sale_document(include_receipts=True) else 'bill_error',
'message': expected_error_message,
}])
####################################################################################################
# Test: assert available classification value for dynamic selection fields
####################################################################################################
def test_mydata_available_inv_type_values(self):
invoice = self._create_mydata_invoice(inv_type='1.1', cls_category='', cls_type='')
self.assertRecordValues(invoice, [{
'l10n_gr_edi_available_inv_type': '1.1,1.2,1.3,1.4,1.5,1.6,2.1,2.2,2.3,2.4,3.1,3.2,5.1,5.2,'
'6.1,6.2,7.1,8.1,8.2,11.1,11.2,11.3,11.4,11.5,17.3,17.4',
}])
self.assertRecordValues(invoice.invoice_line_ids, [{
'l10n_gr_edi_available_cls_category': 'category1_1,category1_2,category1_3,category1_4,category1_5,'
'category1_7,category1_8,category1_9,category1_95',
'l10n_gr_edi_available_cls_type': False,
'l10n_gr_edi_available_cls_vat': False,
}])
invoice.invoice_line_ids.l10n_gr_edi_cls_category = 'category1_1'
self.assertRecordValues(invoice.invoice_line_ids, [{
'l10n_gr_edi_available_cls_type': 'E3_561_001,E3_561_002,E3_561_007',
}])
invoice.invoice_line_ids.l10n_gr_edi_cls_category = 'category1_8'
# In some cases, the order of available types are jumbled
self.assertEqual(sorted(invoice.invoice_line_ids.l10n_gr_edi_available_cls_type.split(',')), [
'E3_561_001', 'E3_561_002', 'E3_561_007', 'E3_562', 'E3_563', 'E3_564', 'E3_565', 'E3_566', 'E3_567',
'E3_568', 'E3_570', 'E3_596', 'E3_597', 'E3_880_001', 'E3_881_001', 'E3_881_003', 'E3_881_004'])
####################################################################################################
# Test: assert XML tree to file
####################################################################################################
def test_mydata_send_invoice(self):
invoice = self._create_mydata_invoice(invoice_line_ids=[
Command.create({
'product_id': self.product_a.id,
'tax_ids': [Command.set(self.tax_24.ids)],
'l10n_gr_edi_cls_category': 'category1_1',
'l10n_gr_edi_cls_type': 'E3_561_001',
}),
Command.create({
'product_id': self.product_b.id,
'tax_ids': [Command.set(self.tax_13.ids)],
'l10n_gr_edi_cls_category': 'category1_3',
'l10n_gr_edi_cls_type': 'E3_561_002',
}),
])
self.assert_mydata_xml_tree(invoice, expected_file_path='from_odoo/mydata_invoice.xml')
def test_mydata_send_multi_invoices(self):
invoice_1 = self._create_mydata_invoice(inv_type='2.1', cls_category='category1_3', cls_type='E3_561_002')
invoice_2 = self._create_mydata_invoice(inv_type='11.1', cls_category='category1_95', cls_type='')
self.assert_mydata_xml_tree(invoice_1 + invoice_2, expected_file_path='from_odoo/mydata_multi_invoices.xml')
def test_mydata_send_bill_cls_expense(self):
bill = self._create_mydata_bill()
self.assert_mydata_xml_tree(bill, expected_file_path='from_odoo/mydata_cls_expense.xml', send_classification=True)
####################################################################################################
# Test: assert built-in constraints
####################################################################################################
def test_l10n_gr_edi_try_send_invoices_no_credentials_and_vat(self):
self.company_data['company'].write({
'l10n_gr_edi_aade_id': False,
'l10n_gr_edi_aade_key': False,
})
invoice = self._create_mydata_invoice()
invoice.l10n_gr_edi_try_send_invoices()
self.assert_mydata_error(invoice, "You need to set AADE ID and Key in the company settings.")
def test_l10n_gr_edi_try_send_invoices_no_classification(self):
# No invoice type
invoice = self._create_mydata_invoice()
invoice.l10n_gr_edi_inv_type = False
invoice.l10n_gr_edi_try_send_invoices()
self.assert_mydata_error(invoice, 'Missing myDATA Invoice Type.')
# No classification category
invoice = self._create_mydata_invoice(cls_category='')
invoice.l10n_gr_edi_try_send_invoices()
self.assert_mydata_error(invoice, 'Missing myDATA classification category on line 1.')
# No classification type, and inv_type + cls_category combination doesn't allow empty cls_type
invoice = self._create_mydata_invoice(cls_type='')
invoice.l10n_gr_edi_try_send_invoices()
self.assert_mydata_error(invoice, 'Missing myDATA classification type on line 1.')
def test_l10n_gr_edi_try_send_invoices_allowed_no_cls_type(self):
"""Allow no cls_type on some combinations with available cls_type"""
allowed_inv_type_category = (('1.1', 'category1_95'), ('3.2', 'category1_95'), ('5.1', 'category1_95'))
for inv_type, category in allowed_inv_type_category:
invoice = self._create_mydata_invoice(inv_type=inv_type, cls_category=category, cls_type='')
self.assertFalse(invoice._l10n_gr_edi_get_pre_error_string())
def test_l10n_gr_edi_try_send_invoices_invalid_tax_amount(self):
""" Invalid tax amount should raises error. """
invalid_tax = self.env['account.tax'].create([{
'name': 'Bad 12%',
'type_tax_use': 'sale',
'amount': 12.0,
'company_id': self.env.company.id,
}])
invoice = self._create_mydata_invoice(tax_ids=[Command.set(invalid_tax.ids)])
invoice.l10n_gr_edi_try_send_invoices()
self.assert_mydata_error(invoice, 'Invalid tax amount for line 1. The valid values are 24, 13, 6, 17, 9, 4, 0.')
def test_l10n_gr_edi_try_send_invoices_invalid_tax_multi(self):
""" Multiple tax should raises error. """
invoice = self._create_mydata_invoice(tax_ids=[Command.set((self.tax_24 + self.tax_0).ids)])
invoice.l10n_gr_edi_try_send_invoices()
self.assert_mydata_error(invoice, 'myDATA does not support multiple taxes on line 1.')
def test_l10n_gr_edi_try_send_invoices_invalid_tax_nonexistent(self):
""" No tax should raises error. """
invoice = self._create_mydata_invoice(post=False)
invoice.invoice_line_ids.tax_ids = False
invoice.action_post()
invoice.l10n_gr_edi_try_send_invoices()
self.assert_mydata_error(invoice, 'Missing tax on line 1.')
def test_l10n_gr_edi_try_send_invoices_invalid_tax_exempt_no_category(self):
""" Tax 0% and no tax exemption category should raises error. """
invoice = self._create_mydata_invoice(tax_ids=[Command.set(self.tax_0.ids)])
invoice.with_context(skip_readonly_check=True).invoice_line_ids.l10n_gr_edi_tax_exemption_category = False
invoice.l10n_gr_edi_try_send_invoices()
self.assert_mydata_error(invoice, 'Missing myDATA Tax Exemption Category for line 1.')