Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions account_background_post/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_crear_duplicar_y_validar_facturas_masivas_cliente
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from datetime import date

from odoo.tests.common import TransactionCase


class TestCrearDuplicarYValidarFacturasMasivasCliente(TransactionCase):
Comment on lines +3 to +6
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Considere usar SavepointCase en lugar de TransactionCase para acelerar las pruebas cuando no se necesitan commits ni verificar efectos de transacción.

Suggested change
from odoo.tests.common import TransactionCase
class TestCrearDuplicarYValidarFacturasMasivasCliente(TransactionCase):
from odoo.tests.common import SavepointCase
class TestCrearDuplicarYValidarFacturasMasivasCliente(SavepointCase):

Copilot uses AI. Check for mistakes.
def setUp(self):
super().setUp()

# Creación del Cliente
self.cliente = self.env["res.partner"].create(
{
"name": "ADHOC SA",
"vat": "30714295698",
"is_company": True,
}
)

# Usar un producto existente o crear uno con los valores por defecto necesarios
self.producto = self.env["product.product"].search([("type", "=", "service")], limit=1)
if not self.producto:
# Crear plantilla de producto primero
template = self.env["product.template"].create(
{
"name": "Servicio de Test",
"list_price": 100.00,
"type": "service",
}
)
self.producto = template.product_variant_ids[0]
Comment on lines +19 to +30
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Buscar un producto existente puede introducir flakiness (p. ej., impuestos por defecto, cuentas o políticas diferentes). Para aislar el test, cree siempre un producto específico para la prueba con 'taxes_id': [(6, 0, [])] y demás campos requeridos.

Suggested change
# Usar un producto existente o crear uno con los valores por defecto necesarios
self.producto = self.env["product.product"].search([("type", "=", "service")], limit=1)
if not self.producto:
# Crear plantilla de producto primero
template = self.env["product.template"].create(
{
"name": "Servicio de Test",
"list_price": 100.00,
"type": "service",
}
)
self.producto = template.product_variant_ids[0]
# Siempre crear un producto específico para la prueba con taxes_id vacío y demás campos requeridos
template = self.env["product.template"].create(
{
"name": "Servicio de Test",
"list_price": 100.00,
"type": "service",
"taxes_id": [(6, 0, [])],
}
)
self.producto = template.product_variant_ids[0]

Copilot uses AI. Check for mistakes.

def test_crear_duplicar_y_validar_facturas_masivas_cliente(self):
"""
Validar la funcionalidad backend principal del flujo de gestión de Facturas de Cliente.
El test simula la creación de una factura borrador, su posterior duplicación programática
para generar múltiples borradores, y finalmente, la validación masiva de estos documentos.
"""

# Paso 1: Creación de la Factura Borrador (Simulación 00:07 - 00:15)
move_line_vals = [
(0, 0, {"product_id": self.producto.id, "quantity": 1.0, "price_unit": 100.00, "name": "Línea de Servicio"})
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El test asume 0% de impuestos pero no lo garantiza; si el producto o la compañía tienen impuestos por defecto, los montos no serán 100.00 y la aserción fallará. Propongo establecer explícitamente impuestos vacíos en la línea: agregar 'tax_ids': [(6, 0, [])].

Suggested change
(0, 0, {"product_id": self.producto.id, "quantity": 1.0, "price_unit": 100.00, "name": "Línea de Servicio"})
(0, 0, {
"product_id": self.producto.id,
"quantity": 1.0,
"price_unit": 100.00,
"name": "Línea de Servicio",
"tax_ids": [(6, 0, [])],
})

Copilot uses AI. Check for mistakes.
]

factura_base = self.env["account.move"].create(
{
"partner_id": self.cliente.id,
"move_type": "out_invoice",
"invoice_date": date.today(),
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Para respetar el contexto/zonas horarias de Odoo en pruebas, use fields.Date.context_today(self) en lugar de date.today().

Copilot uses AI. Check for mistakes.
"invoice_line_ids": move_line_vals,
}
)

# Validaciones de Estado Inicial (Post-Creación)
self.assertEqual(factura_base.state, "draft", "La factura base debe estar en estado Borrador.")

# Validaciones de Datos de Monto (Pre-Validación)
self.assertAlmostEqual(
factura_base.amount_untaxed, 100.00, msg="El subtotal (Excluido de Impuestos) debe ser $100.00."
)
self.assertAlmostEqual(
factura_base.amount_total,
100.00,
msg="El total de la factura debe ser $100.00 (asumiendo 0% de impuesto para el test).",
)
self.assertAlmostEqual(factura_base.amount_tax, 0.00, msg="El impuesto de la factura debe ser $0.00.")

# Paso 2: Duplicación de la Factura (Simulación 00:18 - 00:26)
factura_duplicada_1 = factura_base.copy()
factura_duplicada_2 = factura_base.copy()
factura_duplicada_3 = factura_base.copy()
facturas_duplicadas = factura_duplicada_1 + factura_duplicada_2 + factura_duplicada_3
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Para combinar recordsets en Odoo, el operador recomendado es '|' (unión) en lugar de '+', ya que '|' elimina duplicados y es más idiomático: facturas_duplicadas = factura_duplicada_1 | factura_duplicada_2 | factura_duplicada_3.

Suggested change
facturas_duplicadas = factura_duplicada_1 + factura_duplicada_2 + factura_duplicada_3
facturas_duplicadas = factura_duplicada_1 | factura_duplicada_2 | factura_duplicada_3

Copilot uses AI. Check for mistakes.

# Validaciones de Estado para Facturas Duplicadas
self.assertTrue(
all(f.state == "draft" for f in facturas_duplicadas),
"Todas las facturas duplicadas deben estar en estado Borrador.",
)

# Paso 3: Validación Masiva (Simulación 00:33 - 00:40)
facturas_a_validar = factura_base + facturas_duplicadas
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Mismo criterio que arriba: use la unión de recordsets con '|' para combinar: facturas_a_validar = factura_base | facturas_duplicadas.

Suggested change
facturas_a_validar = factura_base + facturas_duplicadas
facturas_a_validar = factura_base | facturas_duplicadas

Copilot uses AI. Check for mistakes.
facturas_a_validar.action_post()

# Validaciones de Estado Final (Post-Validación Masiva)
self.assertTrue(
all(f.state == "posted" for f in facturas_a_validar),
"Todas las facturas deben haber pasado a estado Publicado/Validado ('posted').",
)
Comment on lines +84 to +87
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

La verificación de estado 'posted' se realiza dos veces (aquí y en 100-103). Es redundante; elimina una de las dos para evitar duplicación.

Copilot uses AI. Check for mistakes.

# Validaciones de Datos de Monto (Post-Validación)
self.assertTrue(
all(abs(f.amount_total - 100.00) < 0.001 for f in facturas_a_validar),
"El total en todas las facturas validadas debe ser $100.00.",
)

# Validaciones de Relaciones y Documentos Generados
self.assertTrue(
all(f.line_ids for f in facturas_a_validar),
"Cada factura validada debe tener líneas de asiento contable asociadas.",
)
self.assertTrue(
all(f.state == "posted" for f in facturas_a_validar),
"El estado 'posted' de las facturas implica la creación y contabilización del asiento contable subyacente.",
)
self.assertTrue(
all(f.name != "Draft" for f in facturas_a_validar),
"Cada factura validada debe tener un número de documento asignado (no 'Draft').",
)