606 lines
30 KiB
Python
606 lines
30 KiB
Python
from unittest.mock import patch
|
|
|
|
from odoo import Command, fields
|
|
from odoo.exceptions import UserError
|
|
from odoo.tests.common import tagged
|
|
|
|
from . import common
|
|
|
|
|
|
@tagged("-at_install", "post_install", "post_install_l10n", "manual")
|
|
class TestManual(common.TestUyEdi):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.company_uy.l10n_uy_edi_ucfe_env = "demo"
|
|
|
|
def test_10_post_invoice(self):
|
|
""" Post EDI UY invoice
|
|
* default invoice is e-ticket,
|
|
* default taxes auto applied on lines is 22% vat tax
|
|
* name after post (should have * ID name)
|
|
"""
|
|
invoice = self._create_move()
|
|
self.assertEqual(invoice.company_id, self.company_uy, "created with wrong company")
|
|
self.assertEqual(invoice.journal_id.l10n_uy_edi_type, "electronic", "Invoice is not created on EDI journal")
|
|
self.assertEqual(invoice.amount_tax, 22, "invoice taxes are not properly set")
|
|
self.assertEqual(invoice.amount_total, 122.0, "invoice taxes has not been applied to the total")
|
|
invoice.action_post()
|
|
self.assertEqual(invoice.state, "posted", "invoice has not been validate in Odoo")
|
|
self.assertEqual(invoice.name, "* %s" % invoice.id, "Not expected name")
|
|
|
|
def test_20_e_ticket_xml(self):
|
|
""" Create/post/send an e-ticket and check that the pre-generated XML is the same as the one expected """
|
|
invoice = self._create_move()
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "101", "Not e-ticket")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-TK", "20_e_ticket")
|
|
|
|
def test_30_e_invoice_xml(self):
|
|
""" Create e-Invoice, and check that the pre-generated xml is the same as the one expected """
|
|
invoice = self._create_move(
|
|
partner_id=self.partner_local.id,
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv").id
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "111", "Not an e-invoice")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FC", "30_e_invoice")
|
|
|
|
def test_40_e_expo_invoice(self):
|
|
""" Create an Expo e-invoice, and check that the pre-generated xml is the same as the one expected """
|
|
invoice = self._create_move(
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv_exp").id,
|
|
partner_id=self.foreign_partner.id,
|
|
invoice_incoterm_id=self.env.ref("account.incoterm_FOB").id,
|
|
l10n_uy_edi_cfe_sale_mode="1",
|
|
l10n_uy_edi_cfe_transport_route="1",
|
|
invoice_line_ids=[Command.create({"product_id": self.product_vat_22.id, "price_unit": 100.0})],
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "121", "Not Expo e-invoice")
|
|
invoice.action_post()
|
|
|
|
# IndFact lo cambié de 3 a 10
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FCE", "40_e_expo_invoice")
|
|
|
|
def test_50_e_ticket_multi_tax_xml(self):
|
|
""" Create/post/send an e-ticket with multi tax and check that the pre-generated XML is the same as the one expected """
|
|
invoice = self._create_move(
|
|
invoice_line_ids=[
|
|
Command.create({
|
|
"product_id": self.service_vat_22.id,
|
|
"price_unit": 100.0,
|
|
}),
|
|
Command.create({
|
|
"product_id": self.service_vat_10.id,
|
|
"quantity": 2,
|
|
"price_unit": 150,
|
|
}),
|
|
]
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "101", "Not e-ticket")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-TK", "50_e_ticket_multi_tax")
|
|
|
|
def test_60_another_currency(self):
|
|
""" create an invoice with different currency, also test that Incoterm/Transport is properly set for services """
|
|
invoice = self._create_move(
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv_exp").id,
|
|
partner_id=self.foreign_partner.id,
|
|
l10n_uy_edi_cfe_sale_mode="1",
|
|
l10n_uy_edi_cfe_transport_route="1",
|
|
currency_id=self.env.ref("base.USD").id,
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "121", "Not Expo e-invoice")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FCE", "60_e_invoice_another_currency")
|
|
|
|
def test_70_tax_included(self):
|
|
tax_22_included = self.tax_22.copy({"price_include_override": "tax_included", "name": "22% VAT (included)"})
|
|
tax_10_included = self.tax_10.copy({"price_include_override": "tax_included", "name": "10% VAT (included)"})
|
|
tax_0_included = self.tax_0.copy({"price_include_override": "tax_included", "name": "0% VAT (included)"})
|
|
invoice = self._create_move(
|
|
partner_id=self.partner_local.id,
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv").id,
|
|
invoice_line_ids=[
|
|
Command.create({
|
|
"product_id": self.service_vat_22.id,
|
|
"price_unit": 1000,
|
|
"tax_ids": tax_22_included,
|
|
}),
|
|
Command.create({
|
|
"product_id": self.service_vat_22.id,
|
|
"quantity": 2,
|
|
"price_unit": 150,
|
|
"tax_ids": tax_10_included,
|
|
}),
|
|
Command.create({
|
|
"product_id": self.service_vat_22.id,
|
|
"quantity": 2,
|
|
"price_unit": 150,
|
|
"tax_ids": tax_0_included,
|
|
}),
|
|
],
|
|
)
|
|
|
|
invoice.action_post()
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "111", "Not e-invoice")
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FC", "70_e_invoice_tax_included")
|
|
|
|
def test_80_e_ticket_credit_note(self):
|
|
""" Create a credit note, validate it, check that we do not get any error. """
|
|
invoice = self._create_move()
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-TK", "20_e_ticket")
|
|
|
|
refund = self._create_credit_note(invoice)
|
|
refund.action_post()
|
|
self.assertEqual(refund.l10n_latam_document_type_id.code, "102", "Not Credit not document type.")
|
|
self._send_and_print(refund)
|
|
self._check_cfe(refund, "e-NCTK", "80_e_ticket_credit_note")
|
|
|
|
def test_81_e_ticket_credit_note_zero_amount(self):
|
|
""" Ensure that credit note of a 0.00 e-Ticket must keep the mandatory MntCFEref reference node (DGI CODE 31). """
|
|
invoice = self._create_move(invoice_line_ids=[Command.create({
|
|
"product_id": self.service_vat_22.id,
|
|
"price_unit": 0,
|
|
})])
|
|
invoice.action_post()
|
|
self.assertEqual(invoice.amount_total, 0.0, "Original e-Ticket should have a 0.00 total.")
|
|
self._send_and_print(invoice)
|
|
|
|
refund = self._create_credit_note(invoice)
|
|
refund.action_post()
|
|
self._send_and_print(refund)
|
|
|
|
cfe = self.get_xml_tree_from_attachment(refund.l10n_uy_edi_document_id.attachment_id)
|
|
namespace = {"cfe": "http://cfe.dgi.gub.uy"}
|
|
reference = cfe.find(".//cfe:Referencia/cfe:Referencia", namespace)
|
|
mnt_cfe_ref = reference.find("cfe:MntCFEref", namespace)
|
|
self.assertIsNotNone(mnt_cfe_ref, "MntCFEref must be present even when the referenced amount is 0.00.")
|
|
self.assertEqual(mnt_cfe_ref.text, "0.00")
|
|
self.assertIsNotNone(reference.find("cfe:TpoMonedaRef", namespace), "TpoMonedaRef must be present.")
|
|
|
|
def test_90_e_ticket_debit_note(self):
|
|
""" Create a credit note, validate it, check that we do not get any error. """
|
|
invoice = self._create_move()
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-TK", "20_e_ticket")
|
|
refund = self._create_debit_note(invoice)
|
|
refund.action_post()
|
|
self.assertEqual(refund.l10n_latam_document_type_id.code, "103", "Not Debit not document type.")
|
|
self._send_and_print(refund)
|
|
self._check_cfe(refund, "e-NDTK", "90_e_ticket_debit_note")
|
|
|
|
def test_100_e_ticket_with_disclosures(self):
|
|
""" Create/post/send an e-ticket with disclosures and check that the pre-generated XML is the same as the one
|
|
expected """
|
|
item_legend = self.env['l10n_uy_edi.addenda'].create({
|
|
"type": 'item',
|
|
"is_legend": True,
|
|
"name": 'Leyenda Product/Service Detail',
|
|
"content": 'Leyenda Product/Service Detail',
|
|
"company_id": self.env.company.id
|
|
})
|
|
l10n_uy_edi_addenda_ids = self.env['l10n_uy_edi.addenda'].create({
|
|
"type": 'cfe_doc',
|
|
"is_legend": True,
|
|
"name": 'CFE Legend',
|
|
"content": 'CFE Legend',
|
|
"company_id": self.env.company.id
|
|
})
|
|
invoice = self._create_move(
|
|
l10n_uy_edi_addenda_ids=l10n_uy_edi_addenda_ids.ids,
|
|
invoice_line_ids=[Command.create({
|
|
"product_id": self.service_vat_22.id,
|
|
"price_unit": 100.0,
|
|
"l10n_uy_edi_addenda_ids": item_legend.ids,
|
|
})],
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "101", "Not e-ticket")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-TK", "100_e_ticket_disclosures")
|
|
|
|
def test_110_account_move_line_nom_and_desc(self):
|
|
"""Test account move with varied products and descriptions"""
|
|
long_name = "Product name for testing purposes that intentionally contains more than eighty chars"
|
|
|
|
products = [
|
|
self.env["product.product"].create({"name": "Product Without Description"}),
|
|
self.env["product.product"].create({"name": "Product With Description"}),
|
|
self.env["product.product"].create({"name": long_name}),
|
|
]
|
|
|
|
addenda = self.env["l10n_uy_edi.addenda"].create(
|
|
{
|
|
"content": "Addenda Content",
|
|
"is_legend": False,
|
|
}
|
|
)
|
|
|
|
invoice = self._create_move(
|
|
partner_id=self.partner_local.id,
|
|
invoice_line_ids=[
|
|
Command.create({'product_id': products[0].id}), # Without description
|
|
Command.create({'product_id': products[1].id, 'name': 'Custom Description'}), # With description
|
|
Command.create({'product_id': products[2].id}), # +80 chars without description
|
|
Command.create({'product_id': products[2].id, 'name': 'Custom Description'}), # +80 chars with description
|
|
Command.create({'product_id': products[2].id, 'l10n_uy_edi_addenda_ids': [(6, 0, [addenda.id])]}), # +80 chars with addenda
|
|
Command.create({'product_id': products[2].id, 'name': 'Custom Description', 'l10n_uy_edi_addenda_ids': [(6, 0, [addenda.id])]}), # +80 chars with desc and addenda
|
|
Command.create({'product_id': products[0].id, 'name': False}), # With description deleted by user
|
|
Command.create({'product_id': products[0].id, 'name': ''}), # With a empty string as description
|
|
],
|
|
)
|
|
|
|
for idx, line in enumerate(invoice.invoice_line_ids):
|
|
nom_item, description = invoice._l10n_uy_edi_get_line_nom_and_desc(line)
|
|
if idx == 0: # Without description
|
|
self.assertEqual(
|
|
nom_item, "Product Without Description"[:80], "NomItem mismatch for line without description"
|
|
)
|
|
self.assertEqual(description, "", "Description mismatch for line without description")
|
|
elif idx == 1: # With description
|
|
self.assertEqual(
|
|
nom_item, "Product With Description"[:80], "NomItem mismatch for line with description"
|
|
)
|
|
self.assertEqual(description, "Custom Description", "Description mismatch for line with description")
|
|
elif idx == 2: # +80 chars without description
|
|
self.assertEqual(
|
|
nom_item, long_name[:80], "NomItem mismatch for line with long name without description"
|
|
)
|
|
self.assertEqual(
|
|
description, long_name[80:], "Description mismatch for line with long name without description"
|
|
)
|
|
elif idx == 3: # +80 chars with description
|
|
self.assertEqual(nom_item, long_name[:80], "NomItem mismatch for line with long name and description")
|
|
self.assertEqual(
|
|
description,
|
|
f"{long_name[80:]}Custom Description",
|
|
"Description mismatch for line with long name and description",
|
|
)
|
|
elif idx == 4: # +80 chars with addenda
|
|
self.assertEqual(nom_item, long_name[:80], "NomItem mismatch for line with long name and addenda")
|
|
self.assertIn(
|
|
"Addenda Content", description, "Addenda content missing in description for line with addenda"
|
|
)
|
|
elif idx == 5: # +80 chars with desc and addenda
|
|
self.assertEqual(
|
|
nom_item, long_name[:80], "NomItem mismatch for line with long name, description, and addenda"
|
|
)
|
|
self.assertIn(
|
|
"Custom Description",
|
|
description,
|
|
"Custom description missing in description for line with desc and addenda",
|
|
)
|
|
self.assertIn(
|
|
"Addenda Content",
|
|
description,
|
|
"Addenda content missing in description for line with desc and addenda",
|
|
)
|
|
elif idx == 6 or idx == 7: # With description deleted by user or ""
|
|
self.assertEqual(
|
|
nom_item, "Product Without Description"[:80], "NomItem mismatch for line with deleted description"
|
|
)
|
|
self.assertEqual(description, "", "Description mismatch for line with deleted description")
|
|
|
|
def test_120_e_ticket_final_consumer(self):
|
|
""" Create/post/send an e-ticket and check that the pre-generated XML is the same as the one expected """
|
|
invoice = self._create_move(partner_id=self.env.ref("l10n_uy.partner_cfu").id)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "101", "Not e-ticket")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-TK", "120_e_ticket_final_consumer")
|
|
|
|
def test_default_doc_type_by_id(self):
|
|
dc_e_inv = self.env.ref('l10n_uy.dc_e_inv')
|
|
move = self._create_move(partner_id=self.env.ref("l10n_uy.partner_cfu").id)
|
|
self.assertEqual(move.l10n_latam_document_type_id, self.env.ref('l10n_uy.dc_e_ticket'), "The document type is not being set correctly.")
|
|
move.partner_id = self.env.ref('l10n_uy.partner_dgi').id
|
|
self.assertEqual(move.l10n_latam_document_type_id, dc_e_inv, "The expected document should be e-invoice")
|
|
|
|
def test_130_entrega_gratuita(self):
|
|
""" Create e-Invoice with line with discount 100% and it should work """
|
|
invoice = self._create_move(
|
|
partner_id=self.partner_local.id,
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv").id,
|
|
invoice_line_ids=[Command.create({
|
|
"product_id": self.service_vat_22.id,
|
|
"price_unit": 100.0,
|
|
"discount": 100.0,
|
|
})],
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "111", "Not an e-invoice")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FC", "130_entrega_gratuita")
|
|
|
|
def test_135_entrega_gratuita_zero(self):
|
|
""" Create e-Invoice with line quantity 1.0 and price_unit/price_total = 0.0 (not need discount) """
|
|
invoice = self._create_move(
|
|
partner_id=self.partner_local.id,
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv").id,
|
|
invoice_line_ids=[Command.create({
|
|
"product_id": self.service_vat_22.id,
|
|
"price_unit": 0,
|
|
})],
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "111", "Not an e-invoice")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FC", "135_entrega_gratuita_zero")
|
|
|
|
def test_140_global_discount(self):
|
|
""" Create e-Invoice with line with discount 100% and it should work """
|
|
discount = self.env["product.product"].create({
|
|
"name": "Discount Product",
|
|
"list_price": 10,
|
|
"standard_price": 10,
|
|
"type": "service",
|
|
})
|
|
discount_list = [
|
|
{"product_id": discount.id, "price_unit": -5.0, "tax_ids": self.tax_22}, # w/product
|
|
{"product_id": discount.id, "name": "", "price_unit": -2.0, "tax_ids": self.tax_22}, # w/product & wo/label
|
|
{"name": "Discount only Label", "price_unit": -20.0, "tax_ids": self.tax_0}, # wo product but w/label
|
|
{"name": "", "price_unit": -10.0, "tax_ids": self.tax_10}, # wo product or label
|
|
]
|
|
invoice = self._create_move(
|
|
ref="test_140_global_discount",
|
|
partner_id=self.partner_local.id,
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv").id,
|
|
invoice_line_ids=[
|
|
Command.create({"product_id": self.service_vat_22.id, "price_unit": 100.0, "tax_ids": self.tax_22}),
|
|
Command.create({"product_id": self.service_vat_22.id, "price_unit": 100.0, "tax_ids": self.tax_10}),
|
|
Command.create({"product_id": self.service_vat_22.id, "price_unit": 100.0, "tax_ids": self.tax_0}),
|
|
] + [Command.create(item) for item in discount_list] # Discounts
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "111", "Not an e-invoice")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FC", "140_global_discount")
|
|
|
|
def test_150_global_donwpayment(self):
|
|
""" Create e-Invoice that is a down payment
|
|
invoice_ind = 6 if self._is_downpayment() else invoice_ind
|
|
"""
|
|
down_payment = self.env["product.product"].create({
|
|
"name": "Down Payment",
|
|
"list_price": 10,
|
|
"standard_price": 10,
|
|
"type": "service",
|
|
})
|
|
invoice = self._create_move(
|
|
ref="test_150_global_donwpayment",
|
|
partner_id=self.partner_local.id,
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv").id,
|
|
invoice_line_ids=[
|
|
Command.create({"product_id": down_payment.id, "price_unit": 2000.0, "tax_ids": []}),
|
|
])
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "111", "Not an e-invoice")
|
|
invoice.action_post()
|
|
|
|
with patch("odoo.addons.account.models.account_move.AccountMove._is_downpayment", return_value=True), \
|
|
patch("odoo.addons.sale.models.account_move.AccountMove._is_downpayment", return_value=True), \
|
|
patch("odoo.addons.pos_sale.models.account_move.AccountMove._is_downpayment", return_value=True):
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FC", "150_global_donwpayment")
|
|
|
|
def test_160_deduct_global_donwpayment(self):
|
|
""" Create e-Invoice that is a down payment
|
|
invoice_ind = 7 if line.quantity < 0 else invoice_ind
|
|
"""
|
|
down_payment = self.env["product.product"].create({
|
|
"name": "Down Payment",
|
|
"list_price": 10,
|
|
"standard_price": 10,
|
|
"type": "service",
|
|
})
|
|
invoice = self._create_move(
|
|
ref="test_160_deduct_global_donwpayment",
|
|
partner_id=self.partner_local.id,
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv").id,
|
|
invoice_line_ids=[
|
|
Command.create({"product_id": self.service_vat_22.id, "price_unit": 10000.0, "tax_ids": self.tax_22}),
|
|
Command.create({"product_id": down_payment.id, "quantity": -1.0, "price_unit": 2000.0, "tax_ids": False}),
|
|
])
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "111", "Not an e-invoice")
|
|
invoice.action_post()
|
|
|
|
url = "odoo.addons.%s.models.account_move_line.AccountMoveLine._get_downpayment_lines"
|
|
result_value = invoice.invoice_line_ids.filtered(lambda x: x.quantity < 0.0)
|
|
with patch(url % "account", return_value=result_value), patch(url % "sale", return_value=result_value):
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FC", "160_deduct_global_donwpayment")
|
|
|
|
def test_170_uploaded_vendor_bill_with_global_fixed_discount(self):
|
|
""" Simulate upload xml document with global discount fixed line on vendor bill journal and test if it was
|
|
created correctly. """
|
|
new_bill = self._mock_attachment_upload(
|
|
journal=self.company_data['default_journal_purchase'],
|
|
filename='vendor_bill_with_global_fixed_discount',
|
|
)
|
|
self.assertEqual(new_bill.name, "e-FC AF4002353")
|
|
self.assertEqual(new_bill.invoice_date.strftime('%Y-%m-%d'), "2024-06-06")
|
|
self.assertEqual(new_bill.invoice_date_due.strftime('%Y-%m-%d'), "2024-06-07")
|
|
self.assertEqual(new_bill.invoice_partner_display_name, "FIERRO VIGNOLI S.A.")
|
|
global_discount_line = new_bill.invoice_line_ids\
|
|
.filtered(lambda line: line.name == 'descuento por forma de pago')
|
|
self.assertEqual(global_discount_line.price_total, -431.16)
|
|
|
|
def test_180_uploaded_vendor_bill_with_line_fixed_discount(self):
|
|
""" Simulate upload xml document with discount fixed line on vendor bill journal and test if it was created
|
|
correctly. """
|
|
new_bill = self._mock_attachment_upload(
|
|
journal=self.company_data['default_journal_purchase'],
|
|
filename='vendor_bill_with_line_fixed_discount',
|
|
)
|
|
self.assertEqual(new_bill.name, "e-FC F6557758")
|
|
self.assertEqual(new_bill.invoice_date.strftime('%Y-%m-%d'), "2024-05-31")
|
|
self.assertEqual(new_bill.invoice_date_due.strftime('%Y-%m-%d'), "2024-06-25")
|
|
self.assertEqual(new_bill.invoice_partner_display_name, "Administración Nacional de Telecomunicaciones")
|
|
|
|
def test_190_dedicated_addenda_page(self):
|
|
""" Verify that a dedicated addenda page is requested when the addenda are too long. """
|
|
|
|
def assert_narration_extra_params(invoice, narration, dedicated_addenda):
|
|
expected_extra_params = {}
|
|
if dedicated_addenda:
|
|
expected_extra_params = {
|
|
'nombreParametros': {'string': ['adenda']},
|
|
'valoresParametros': {'string': ['true']}
|
|
}
|
|
|
|
invoice.narration = narration
|
|
_, extra_params = invoice.l10n_uy_edi_document_id._get_report_params()
|
|
self.assertEqual(extra_params, expected_extra_params)
|
|
|
|
invoice = self._create_move(
|
|
invoice_line_ids=[
|
|
Command.create({
|
|
'product_id': self.service_vat_22.id,
|
|
'price_unit': 100.0,
|
|
}),
|
|
],
|
|
)
|
|
invoice.action_post()
|
|
self._send_and_print(invoice) # to generate the l10n_uy_edi.document
|
|
|
|
line_length = 140
|
|
max_lines = 6
|
|
|
|
# Test narration with exactly the maximum allowed lines (no dedicated page required)
|
|
assert_narration_extra_params(invoice, 'A' * line_length * max_lines, False) # 6 lines
|
|
# Test narration exceeding the maximum allowed lines (dedicated page required)
|
|
assert_narration_extra_params(invoice, 'A' * (line_length * max_lines + 10), True) # 6 lines and 10 chars
|
|
assert_narration_extra_params(invoice, 'A\nA\nA' + 'A' * line_length * 4, True) # 7 lines
|
|
|
|
def test_200_uploaded_vendor_bill_with_two_bills(self):
|
|
new_bills = self._mock_attachment_upload(
|
|
journal=self.company_data['default_journal_purchase'],
|
|
filename='sobre_with_2_vendor_bills',
|
|
)
|
|
|
|
self.assertRecordValues(new_bills, [
|
|
{
|
|
'name': 'e-FC A6310618',
|
|
'invoice_date': fields.Date.to_date('2024-10-03'),
|
|
'invoice_partner_display_name': 'BANCO ITAU URUGUAY S.A.',
|
|
},
|
|
{
|
|
'name': 'e-FC A6310619',
|
|
'invoice_date': fields.Date.to_date('2024-10-03'),
|
|
'invoice_partner_display_name': 'BANCO ITAU URUGUAY S.A.',
|
|
},
|
|
])
|
|
|
|
def test_210_e_invoice_xml_with_reduced_vat_tax(self):
|
|
""" Create e-Invoice, and check that the pre-generated xml is the same as the one expected """
|
|
invoice = self._create_move(
|
|
partner_id=self.partner_local.id,
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv").id
|
|
)
|
|
invoice.invoice_line_ids = [Command.set(invoice.invoice_line_ids.ids)] + [
|
|
Command.create({
|
|
'product_id': self.service_reduced_vat.id,
|
|
'quantity': 1,
|
|
'price_unit': 20,
|
|
})
|
|
]
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FC", "200_e_invoice_with_reduced_vat_tax")
|
|
|
|
def test_210_usd_company_uyu(self):
|
|
""" Test the behavior of invoices in UYU for a company in USD."""
|
|
self._configure_usd_company_currency()
|
|
invoice = self._create_move(currency_id=self.env.ref("base.UYU").id)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "101", "Not e-ticket")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-TK", "20_e_ticket")
|
|
|
|
def test_215_usd_company_usd(self):
|
|
"""Test the behavior of invoices in USD for a company in USD."""
|
|
self._configure_usd_company_currency()
|
|
invoice = self._create_move(
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv_exp").id,
|
|
partner_id=self.foreign_partner.id,
|
|
l10n_uy_edi_cfe_sale_mode="1",
|
|
l10n_uy_edi_cfe_transport_route="1",
|
|
currency_id=self.env.ref("base.USD").id,
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "121", "Not Expo e-invoice")
|
|
invoice.action_post()
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FCE", "60_e_invoice_another_currency")
|
|
|
|
def test_e_expo_invoice_global_discount(self):
|
|
""" Create an Expo e-Invoice (CFE 121) with a global discount line. The discount must be
|
|
reported in the DscRcgGlobal section with IndFactDR = 10 (Exportación y asimiladas) and
|
|
ValorDR taken from the line tax base (quantity * price_unit, not just the unit price).
|
|
MntExpoyAsim and MntTotal must be the total after applying the discount. """
|
|
invoice = self._create_move(
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv_exp").id,
|
|
partner_id=self.foreign_partner.id,
|
|
invoice_incoterm_id=self.env.ref("account.incoterm_FOB").id,
|
|
l10n_uy_edi_cfe_sale_mode="1",
|
|
l10n_uy_edi_cfe_transport_route="1",
|
|
invoice_line_ids=[
|
|
Command.create({"product_id": self.product_vat_22.id, "price_unit": 100.0}),
|
|
# quantity 2 x -5.0: ValorDR must be 10.00 (the tax base), not abs(price_unit) = 5.00
|
|
Command.create({"name": "Global Discount", "quantity": 2.0, "price_unit": -5.0, "tax_ids": self.tax_0}),
|
|
],
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "121", "Not Expo e-invoice")
|
|
invoice.action_post()
|
|
self.assertEqual(invoice.amount_total, 90.0, "Global discount not applied to the total")
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FCE", "230_e_expo_invoice_global_discount")
|
|
|
|
def test_e_expo_invoice_full_discount(self):
|
|
""" Create an Expo e-Invoice fully discounted (total = 0.0). MntExpoyAsim is a mandatory tag
|
|
for expo CFEs, so it must be reported with 0.00 value instead of being removed from the XML """
|
|
invoice = self._create_move(
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv_exp").id,
|
|
partner_id=self.foreign_partner.id,
|
|
invoice_incoterm_id=self.env.ref("account.incoterm_FOB").id,
|
|
l10n_uy_edi_cfe_sale_mode="1",
|
|
l10n_uy_edi_cfe_transport_route="1",
|
|
invoice_line_ids=[
|
|
Command.create({"product_id": self.product_vat_22.id, "price_unit": 100.0}),
|
|
Command.create({"name": "Full Discount", "price_unit": -100.0, "tax_ids": self.tax_0}),
|
|
],
|
|
)
|
|
self.assertEqual(invoice.l10n_latam_document_type_id.code, "121", "Not Expo e-invoice")
|
|
invoice.action_post()
|
|
self.assertEqual(invoice.amount_total, 0.0, "Global discount not applied to the total")
|
|
self._send_and_print(invoice)
|
|
self._check_cfe(invoice, "e-FCE", "235_e_expo_invoice_full_discount")
|
|
|
|
def test_global_discount_tax_mismatch(self):
|
|
""" A discount line whose taxes do not match the taxes of any regular line must be rejected
|
|
with a pre-send validation error (and the CFE should not be left in error state) """
|
|
invoice = self._create_move(
|
|
partner_id=self.partner_local.id,
|
|
l10n_latam_document_type_id=self.env.ref("l10n_uy.dc_e_inv").id,
|
|
invoice_line_ids=[
|
|
Command.create({"product_id": self.service_vat_22.id, "price_unit": 100.0, "tax_ids": self.tax_22}),
|
|
Command.create({"name": "Discount", "price_unit": -10.0, "tax_ids": self.tax_10}),
|
|
],
|
|
)
|
|
invoice.action_post()
|
|
with self.assertRaisesRegex(UserError, "must be associated to another line having the same taxes"):
|
|
self._send_and_print(invoice)
|
|
|
|
# Since it is an internal pre-send validation, the CFE state/error must remain unset
|
|
self.assertFalse(invoice.l10n_uy_edi_cfe_state)
|
|
self.assertFalse(invoice.l10n_uy_edi_error)
|