1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/l10n_br_edi/tests/test_l10n_br_edi.py
2024-12-10 09:04:09 +07:00

258 lines
12 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from contextlib import contextmanager
from unittest.mock import patch, DEFAULT
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.addons.iap import InsufficientCreditError
from odoo.addons.l10n_br_edi.tests.data_invoice_1 import (
invoice_1_request,
invoice_1_submit_success_response,
invoice_1_submit_fail_response,
invoice_1_cancel_success_response,
invoice_1_correct_success_response,
invoice_1_correct_fail_response, invoice_1_cancel_fail_response,
)
from odoo.exceptions import UserError, ValidationError, AccessError
from odoo.tests import tagged
from odoo.tools import json
@tagged("post_install_l10n", "post_install", "-at_install")
class TestL10nBREDICommon(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls, chart_template_ref="br"):
super().setUpClass(chart_template_ref)
company = cls.company_data["company"]
company.partner_id.write(
{
"street": "Rua Marechal Deodoro",
"street2": "Centro",
"city": "Curitaba",
"state_id": cls.env.ref("base.state_br_pr").id,
"zip": "80010-010",
"country_id": cls.env.ref("base.br").id,
"vat": "49233848000150",
"l10n_br_ie_code": "9102799558",
}
)
cls.partner_customer = cls.env["res.partner"].create(
{
"name": "BR Company Customer",
"street": "Av. Presidente Vargas 592",
"street2": "Centros",
"city": "Rio de Janeiro",
"state_id": cls.env.ref("base.state_br_rj").id,
"zip": "20071-001",
"vat": "51494569013170",
}
)
cls.product_screens = cls.env["product.template"].create(
{
"name": "Acoustic Bloc Screens",
"list_price": 295.00,
"default_code": "FURN_6666",
"l10n_br_ncm_code_id": cls.env.ref("l10n_br_avatax.49011000").id,
"l10n_br_source_origin": "0",
"l10n_br_sped_type": "FOR PRODUCT",
"l10n_br_use_type": "use or consumption",
}
)
cls.product_cabinet = cls.env["product.template"].create(
{
"name": "Cabinet with Doors",
"list_price": 140.00,
"default_code": "E-COM11",
"l10n_br_ncm_code_id": cls.env.ref("l10n_br_avatax.49011000").id,
"l10n_br_source_origin": "0",
"l10n_br_sped_type": "FOR PRODUCT",
"l10n_br_use_type": "use or consumption",
}
)
cls.avatax_fp = cls.env["account.fiscal.position"].create({"name": "Avatax Brazil", "l10n_br_is_avatax": True})
cls.invoice = cls.env["account.move"].create(
{
"partner_id": cls.partner_customer.id,
"move_type": "out_invoice",
"invoice_date": "2023-10-05",
"currency_id": cls.env.ref("base.BRL").id,
"fiscal_position_id": cls.avatax_fp.id,
"l10n_br_edi_avatax_data": json.dumps(
{
"header": invoice_1_request["header"],
"lines": invoice_1_request["lines"],
"summary": invoice_1_request["summary"],
}
),
"invoice_line_ids": [
(
0,
0,
{"product_id": cls.product_screens.product_variant_id.id},
),
(
0,
0,
{"product_id": cls.product_cabinet.product_variant_id.id},
),
],
}
)
cls.wizard = cls.env["account.move.send"].create({"move_ids": cls.invoice.ids})
@contextmanager
def with_patched_account_move(self, method_name, mocked_response=None):
with patch.object(
type(self.env["account.move"]),
method_name,
new=(lambda *args, **kwargs: mocked_response) if mocked_response is not None else DEFAULT,
autospec=not mocked_response,
) as patched_fn:
yield patched_fn
@tagged("post_install_l10n", "post_install", "-at_install")
class TestL10nBREDI(TestL10nBREDICommon):
def test_l10n_br_edi_is_enabled_checkbox(self):
self.wizard.l10n_br_edi_is_enabled = False
self.wizard.checkbox_download = False
with self.with_patched_account_move("_l10n_br_iap_submit_invoice_goods") as patched_submit:
self.wizard.action_send_and_print()
self.assertFalse(patched_submit.called, "EDI should not have been called when the checkbox isn't checked.")
def test_invoice_1_success(self):
with self.with_patched_account_move("_l10n_br_iap_submit_invoice_goods", invoice_1_submit_success_response):
self.wizard.action_send_and_print()
self.assertEqual(self.invoice.l10n_br_last_edi_status, "accepted", "The EDI document should be accepted.")
self.assertEqual(
self.invoice.invoice_pdf_report_file.decode(),
invoice_1_submit_success_response["pdf"]["base64"],
"PDF data should have been saved on the EDI document.",
)
self.assertEqual(
self.invoice.l10n_br_edi_xml_attachment_id.datas.decode(),
invoice_1_submit_success_response["xml"]["base64"],
"XML data should have been saved on the EDI document.",
)
self.assertEqual(
self.invoice.l10n_br_access_key,
invoice_1_submit_success_response["key"],
"The access key should have been saved.",
)
self.assertFalse(self.invoice.l10n_br_edi_avatax_data, "Saved Avatax tax data should have been removed.")
def test_invoice_1_fail(self):
with self.with_patched_account_move(
"_l10n_br_iap_submit_invoice_goods", invoice_1_submit_fail_response
), self.assertRaisesRegex(UserError, re.escape(invoice_1_submit_fail_response["error"]["message"])):
self.wizard.action_send_and_print()
self.assertEqual(self.invoice.l10n_br_last_edi_status, "error", "The EDI document should be in error.")
self.assertFalse(
self.invoice.invoice_pdf_report_file,
"There should be no PDF data on the EDI document.",
)
self.assertFalse(
self.invoice.l10n_br_edi_xml_attachment_id,
"There should be no XML data on the EDI document.",
)
self.assertTrue(self.invoice.l10n_br_edi_avatax_data, "Saved Avatax tax data should have been kept.")
def test_invoice_1_iap_errors(self):
"""Test that we properly catch errors from IAP. We should never raise, because it would break the asynchronous
invoice_multi mode. We can't test the asynchronous mode easily, but we can test if we catch IAP errors by looking
at their format."""
@contextmanager
def wrap_iap(exception):
# If the error is prefixed with "Errors when..." we know we caught and handled it.
with patch(
"odoo.addons.l10n_br_avatax.models.account_external_tax_mixin.iap_jsonrpc", side_effect=exception
), self.assertRaisesRegex(UserError, "Errors when submitting the e-invoice:"):
yield
for Exc in (UserError, AccessError, InsufficientCreditError):
with wrap_iap(Exc("test")):
try:
self.wizard.action_send_and_print()
finally:
self.wizard.move_ids.l10n_br_last_edi_status = False
def test_prepare_tax_data(self):
to_include, header = self.invoice._l10n_br_edi_get_tax_data()
for line in to_include["lines"]:
for detail in line["taxDetails"]:
self.assertFalse("ruleId" in detail and detail["ruleId"] is None, "ruleId shouldn't be sent when null.")
self.assertEqual(header["goods"]["class"], "TEST CLASS VALUE", "Test class value should be included in header.")
def test_update_cancel(self):
wizard = self.env["l10n_br_edi.invoice.update"].create(
{"move_id": self.invoice.id, "mode": "cancel", "reason": "test reason with at least 15 characters"}
)
with self.with_patched_account_move("_l10n_br_iap_cancel_invoice_goods", invoice_1_cancel_success_response):
wizard.action_submit()
self.assertEqual(self.invoice.state, "cancel", "Invoice should be cancelled.")
self.assertEqual(self.invoice.l10n_br_last_edi_status, "cancelled", "Invoice should be EDI cancelled.")
def test_update_cancel_error(self):
wizard = self.env["l10n_br_edi.invoice.update"].create(
{"move_id": self.invoice.id, "mode": "cancel", "reason": "test reason with at least 15 characters"}
)
with self.with_patched_account_move("_l10n_br_iap_cancel_invoice_goods", invoice_1_cancel_fail_response), \
self.assertRaisesRegex(UserError, "Rejei\u00e7\u00e3o: Evento n\u00e3o atende o Schema XML espec\u00edfico"):
wizard.action_submit()
def test_update_correction(self):
wizard = self.env["l10n_br_edi.invoice.update"].create(
{"move_id": self.invoice.id, "mode": "correct", "reason": "Reason to make this correction."}
)
self.assertFalse(
self.invoice.l10n_br_edi_last_correction_number, "Invoice shouldn't have a correction number yet (is 0)."
)
original_state = self.invoice.state
original_edi_state = self.invoice.l10n_br_last_edi_status
with self.with_patched_account_move("_l10n_br_iap_correct_invoice_goods", invoice_1_correct_success_response):
wizard.action_submit()
self.assertEqual(self.invoice.state, original_state, "Invoice state shouldn't have changed.")
self.assertEqual(
self.invoice.l10n_br_last_edi_status, original_edi_state, "Invoice EDI state shouldn't have changed."
)
self.assertEqual(self.invoice.l10n_br_edi_last_correction_number, 1, "Latest correction number should be 1")
def test_update_correction_error(self):
# This will fail because the reason must be >=15 characters according to Avalara.
wizard = self.env["l10n_br_edi.invoice.update"].create(
{"move_id": self.invoice.id, "mode": "correct", "reason": "Too short."}
)
with self.with_patched_account_move(
"_l10n_br_iap_correct_invoice_goods", invoice_1_correct_fail_response
), self.assertRaisesRegex(ValidationError, re.escape(invoice_1_correct_fail_response["status"]["desc"])):
wizard.action_submit()
def test_new_invoice_attachments(self):
"""Test that newly set invoice PDFs or XMLs are reflected in the fields."""
def set_new_attachments(invoice, response):
new_attachments = invoice._l10n_br_edi_attachments_from_response(response)
new_pdf_attachment = new_attachments.filtered(lambda attachment: ".pdf" in attachment.name)
new_xml_attachment = new_attachments.filtered(lambda attachment: ".xml" in attachment.name)
self.assertEqual(invoice.invoice_pdf_report_id, new_pdf_attachment)
self.assertEqual(invoice.l10n_br_edi_xml_attachment_id, new_xml_attachment)
self.assertEqual(invoice.message_main_attachment_id, new_pdf_attachment)
set_new_attachments(self.invoice, invoice_1_submit_success_response)
set_new_attachments(self.invoice, invoice_1_submit_success_response)