# coding: utf-8 from odoo.addons.account_edi.tests.common import AccountEdiTestCommon from odoo.tools import misc from odoo import Command from unittest.mock import patch, Mock from freezegun import freeze_time import datetime from contextlib import contextmanager from pytz import timezone class TestCoEdiCommon(AccountEdiTestCommon): @contextmanager def mock_carvajal(self): return_value_upload = { 'message': 'mocked success', 'transactionId': 'mocked_success', } return_value_check = { 'filename': 'mock_signed_file', 'xml_file': b'file_content', 'attachments': None, 'l10n_co_edi_cufe_cude_ref': 'cufe_cude ref', 'message': 'successfully mocked' } try: with freeze_time(self.frozen_today), \ patch('odoo.addons.l10n_co_edi.models.carvajal_request.CarvajalRequest.upload', new=Mock(return_value=return_value_upload)), \ patch('odoo.addons.l10n_co_edi.models.carvajal_request.CarvajalRequest.check_status', new=Mock(return_value=return_value_check)), \ patch('odoo.addons.l10n_co_edi.models.carvajal_request.CarvajalRequest.client', new=Mock(return_value=None)): yield finally: pass @classmethod def setUpClass(cls, chart_template_ref='co', edi_format_ref='l10n_co_edi.edi_carvajal'): super().setUpClass(chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref) cls.frozen_today = datetime.datetime(year=2020, month=8, day=27, hour=0, minute=0, second=0, tzinfo=timezone('utc')) cls.salesperson = cls.env.ref('base.user_admin') cls.salesperson.function = 'Funcionario de ventas y trato al cliente final' report_text = 'GRANDES CONTRIBUYENTES SHD Res. DDI-042065 13-10-17' cls.company_data['company'].write({ 'country_id': cls.env.ref('base.co').id, 'l10n_co_edi_header_gran_contribuyente': report_text, 'l10n_co_edi_header_tipo_de_regimen': report_text, 'l10n_co_edi_header_retenedores_de_iva': report_text, 'l10n_co_edi_header_autorretenedores': report_text, 'l10n_co_edi_header_resolucion_aplicable': report_text, 'l10n_co_edi_header_actividad_economica': report_text, 'l10n_co_edi_header_bank_information': report_text, 'l10n_co_edi_username': 'test', 'l10n_co_edi_password': 'test', 'l10n_co_edi_company': 'test', 'l10n_co_edi_account': 'test', 'vat': '213123432-1', 'phone': '+57 123 4567890 123', # Colombian telephone numbers are 10 digit numbers (January 2024); longer here to test shortening 'website': 'http://www.example.com', 'email': 'info@yourcompany.example.com', 'street': 'Route de Ramilies', 'zip': '1234', 'city': 'Bogota', 'state_id': cls.env.ref('base.state_co_01').id, 'tax_calculation_rounding_method': 'round_globally', }) cls.company_data['company'].partner_id.write({ 'l10n_latam_identification_type_id': cls.env.ref('l10n_co.rut').id, 'l10n_co_edi_obligation_type_ids': [(6, 0, [cls.env.ref('l10n_co_edi.obligation_type_1').id])], 'l10n_co_edi_large_taxpayer': True, }) cls.company_data['default_journal_sale'].write({ 'l10n_co_edi_dian_authorization_end_date': cls.frozen_today, 'l10n_co_edi_dian_authorization_number': 42, 'l10n_co_edi_dian_authorization_date': cls.frozen_today, }) cls.company_data['default_journal_purchase'].write({ 'l10n_co_edi_dian_authorization_end_date': cls.frozen_today, 'l10n_co_edi_dian_authorization_number': 42, 'l10n_co_edi_dian_authorization_date': cls.frozen_today, }) cls.company_data_2['company'].write({ 'country_id': cls.env.ref('base.co').id, 'phone': '(870)-931-0505', 'website': 'http://wwww.company_2.com', 'email': 'company_2@example.com', 'street': 'Route de Eghezée', 'zip': '4567', 'city': 'Medellín', 'state_id': cls.env.ref('base.state_co_02').id, 'vat': '213.123.432-1', }) cls.company_data_2['company'].partner_id.write({ 'l10n_latam_identification_type_id': cls.env.ref('l10n_co.rut').id, 'l10n_co_edi_obligation_type_ids': [(6, 0, [cls.env.ref('l10n_co_edi.obligation_type_1').id])], 'l10n_co_edi_large_taxpayer': True, }) cls.tax = cls.company_data['default_tax_sale'] cls.tax.write({ 'amount': 15, 'l10n_co_edi_type': cls.env.ref('l10n_co_edi.tax_type_0').id }) cls.retention_tax = cls.tax.copy({ 'name': 'retention_tax', 'l10n_co_edi_type': cls.env.ref('l10n_co_edi.tax_type_9').id }) cls.tax_group = cls.env['account.tax'].create({ 'name': 'tax_group', 'amount_type': 'group', 'amount': 0.0, 'type_tax_use': 'sale', 'l10n_co_edi_type': cls.env.ref('l10n_co_edi.tax_type_0').id, 'children_tax_ids': [(6, 0, (cls.tax + cls.retention_tax).ids)], }) uom = cls.env.ref('uom.product_uom_unit') uom.l10n_co_edi_ubl = 'S7' cls.product_a.write({ 'default_code': 'P0000', 'uom_id': uom, 'volume': 500.0, }) invoice_data = { 'partner_id': cls.company_data_2['company'].partner_id.id, 'move_type': 'out_invoice', 'ref': 'reference', 'invoice_user_id': cls.salesperson.id, 'invoice_payment_term_id': cls.env.ref('account.account_payment_term_end_following_month').id, 'invoice_line_ids': [ Command.create({ 'product_id': cls.product_a.id, 'quantity': 150, 'price_unit': 250, 'discount': 10, 'name': 'Line 1', 'tax_ids': [Command.set((cls.tax + cls.retention_tax).ids)], }), ] } cls.invoice = cls.env['account.move'].create(invoice_data) # Invoice in non-company currency cls.currency_usd = cls.env.ref('base.USD') cls.currency_usd.active = True cls.env['res.currency.rate'].create({ 'name': cls.frozen_today, 'company_id': cls.company_data['company'].id, 'currency_id': cls.currency_usd.id, 'rate': 1 / 3919.109578}) cls.invoice_multicurrency = cls.env['account.move'].create({ **invoice_data, 'invoice_line_ids': [ Command.create({ 'product_id': cls.product_a.id, 'quantity': 1, 'price_unit': 252.2, 'discount': 10, 'name': 'Line 1', 'tax_ids': [Command.set((cls.tax + cls.retention_tax).ids)], }), ], 'currency_id': cls.currency_usd.id, }) cls.sugar_tax_1 = cls.env['account.tax'].create({ 'name': "IBUA >10gr 50ml", # arbitrary values 'amount_type': 'fixed', 'amount': 17.5, # actual rate of the tax = 35 (for a product with >10gr of sugar per 100ml) 'l10n_co_edi_type': cls.env.ref('l10n_co_edi.tax_type_20').id, }) cls.sugar_tax_2 = cls.sugar_tax_1.copy({ 'name': "IBUA >6gr & <10gr 50ml", 'amount': 18, # actual rate of the tax = 36 (for a product with >10gr of sugar per 100ml) }) # an IBUA tax group to display the total IBUA tax amount in the tax totals section on the invoice cls.env['account.tax'].create({ 'name': 'IBUA', 'amount_type': 'group', 'amount': 0.0, 'type_tax_use': 'sale', 'children_tax_ids': [Command.set([cls.sugar_tax_1.id, cls.sugar_tax_2.id])], }) cls.product_sugar = cls.env['product.product'].create({ 'name': 'Ice Cream', 'uom_id': uom.id, 'volume': 50.0, # this should be the volume in milliliters 'property_account_income_id': cls.company_data['default_account_revenue'].id, 'property_account_expense_id': cls.company_data['default_account_expense'].id, 'default_code': 'P0000', }) # Sugar Invoice invoice_data['invoice_line_ids'] = [ # Sugar taxes should not be grouped together since they have different rates Command.create({ 'product_id': cls.product_sugar.id, 'quantity': 1, 'price_unit': 100, 'tax_ids': [Command.set([cls.tax.id, cls.sugar_tax_1.id])], }), Command.create({ 'product_id': cls.product_sugar.id, 'quantity': 10, 'price_unit': 200, 'tax_ids': [Command.set([cls.tax.id, cls.sugar_tax_2.id])], }), # The following taxes should be grouped together (same CO tax code, amount, and amount_type) Command.create({ 'product_id': cls.product_a.id, 'quantity': 10, 'price_unit': 100, 'tax_ids': [Command.set([cls.tax.id, cls.retention_tax.id])], }), Command.create({ 'product_id': cls.product_a.id, 'quantity': 5, 'price_unit': 100, 'tax_ids': [Command.set([cls.tax.id, cls.retention_tax.id])], }), ] cls.sugar_tax_invoice = cls.env['account.move'].create(invoice_data) cls.tax_iva_19 = cls.env['account.tax'].create({ 'name': "IVA Ventas 19%", 'amount': 19, 'l10n_co_edi_type': cls.env.ref('l10n_co_edi.tax_type_0').id, }) cls.tax_iva_5 = cls.env['account.tax'].create({ 'name': "IVA Ventas 5%", 'amount': 5, 'l10n_co_edi_type': cls.env.ref('l10n_co_edi.tax_type_0').id, }) cls.tax_iva_excento_0 = cls.env['account.tax'].create({ 'name': "IVA Excento", 'amount': 0, 'l10n_co_edi_type': cls.env.ref('l10n_co_edi.tax_type_0').id, }) cls.tax_iva_excluido_0 = cls.env['account.tax'].create({ 'name': "IVA Excluido", 'amount': 0, 'l10n_co_edi_type': cls.env.ref('l10n_co_edi.tax_type_0').id, }) # Testing the grouping inside the TIM sections: invoice with 2 IVA taxes with different rates and 1 Bolsas invoice_data['invoice_line_ids'] = [ # IVA 5% and IVA 19% should be grouped inside the same TIM section Command.create({ 'product_id': cls.product_a.id, 'quantity': 1, 'price_unit': 100, 'tax_ids': [Command.set([cls.tax_iva_19.id])], }), Command.create({ 'product_id': cls.product_a.id, 'quantity': 1, 'price_unit': 200, 'tax_ids': [Command.set([cls.tax_iva_5.id])], }), # The Bolsas tax should be in another TIM section Command.create({ 'product_id': cls.product_a.id, 'quantity': 1, 'price_unit': 200, 'tax_ids': [Command.set([cls.tax_iva_19.id, cls.retention_tax.id])], }), # IVA Excento (IVA, 0%) and IVA Excluido (IVA, 0%): both should be grouped together (same rate and CO type) Command.create({ 'product_id': cls.product_a.id, 'quantity': 1, 'price_unit': 400, 'tax_ids': [Command.set([cls.tax_iva_excento_0.id])], }), Command.create({ 'product_id': cls.product_a.id, 'quantity': 1, 'price_unit': 500, 'tax_ids': [Command.set([cls.tax_iva_excluido_0.id])], }), Command.create({'display_type': 'line_section', 'name': 'Section'}), Command.create({'display_type': 'line_note', 'name': 'Note'}), ] cls.invoice_tim = cls.env['account.move'].create(invoice_data) cls.in_invoice = cls.env['account.move'].create({ **invoice_data, 'move_type': 'in_invoice', 'invoice_date': cls.frozen_today, }) cls.expected_invoice_xml = misc.file_open('l10n_co_edi/tests/accepted_invoice.xml', 'rb').read() cls.expected_invoice_multicurrency_xml = misc.file_open('l10n_co_edi/tests/accepted_invoice_multicurrency.xml', 'rb').read() cls.expected_sugar_tax_invoice_xml = misc.file_open('l10n_co_edi/tests/accepted_sugar_tax_invoice.xml', 'rb').read() cls.expected_credit_note_xml = misc.file_open('l10n_co_edi/tests/accepted_credit_note.xml', 'rb').read() cls.expected_invoice_tim_xml = misc.file_open('l10n_co_edi/tests/accepted_invoice_tim.xml', 'rb').read() cls.expected_in_invoice_xml = misc.file_open('l10n_co_edi/tests/accepted_in_invoice.xml', 'rb').read()