263 lines
12 KiB
Python
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.')
|