[FIX] TestConfirmEskaera_Integration: limpieza de decoradores @patch y corrección de bugs

- Convertir 4 tests de decorador @patch a context manager 'with patch(...)' para evitar RuntimeError en LocalProxy de Werkzeug
- Corregir patrón env(user=..., context=dict(...)) en Odoo 18 (sin .with_context())
- Agregar website real al mock para integración con helpers de pricing (_get_pricing_info)
- Añadir pickup_date en fixture de existing_order para que _find_recent_draft_order localice correctamente
- BUGFIX: Agregar (5,) a order_line para limpiar líneas previas al actualizar pedido existente

Resultado: 0 failed, 0 errors de 4 tests en Docker para TestConfirmEskaera_Integration

BREAKING: _create_or_update_sale_order ahora limpia las líneas anteriores con (5,) antes de asignar las nuevas cuando se actualiza un pedido existente. Comportamiento previo (duplicación de líneas) era un bug.
This commit is contained in:
snt 2026-04-08 17:26:57 +02:00
parent e9809b90e9
commit ce393b6034
13 changed files with 857 additions and 192 deletions

View file

@ -1,9 +1,11 @@
# Copyright 2026 Criptomart
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from unittest.mock import patch
from odoo.exceptions import UserError
from odoo.tests import TransactionCase
from odoo.tests import tagged
from odoo.tests.common import TransactionCase
@tagged("-at_install", "post_install")
@ -24,6 +26,13 @@ class TestBatchSummary(TransactionCase):
"uom_po_id": cls.uom_unit.id,
}
)
cls.product_2 = cls.env["product.product"].create(
{
"name": "Test Product 2",
"uom_id": cls.uom_unit.id,
"uom_po_id": cls.uom_unit.id,
}
)
cls.batch = cls.env["stock.picking.batch"].create(
{"name": "Batch Test", "picking_type_id": cls.picking_type.id}
@ -113,6 +122,60 @@ class TestBatchSummary(TransactionCase):
picking.batch_id = batch.id
return batch
def _create_partial_batch(self, qty_demanded=2.0, qty_done=1.0, extra_move=False):
batch = self.env["stock.picking.batch"].create(
{"name": "Batch Partial", "picking_type_id": self.picking_type.id}
)
batch.picking_type_id.create_backorder = "ask"
picking = self.env["stock.picking"].create(
{
"picking_type_id": self.picking_type.id,
"location_id": self.location_src.id,
"location_dest_id": self.location_dest.id,
"batch_id": batch.id,
}
)
move = self.env["stock.move"].create(
{
"name": self.product.name,
"product_id": self.product.id,
"product_uom_qty": qty_demanded,
"product_uom": self.uom_unit.id,
"picking_id": picking.id,
"location_id": self.location_src.id,
"location_dest_id": self.location_dest.id,
}
)
self.env["stock.move.line"].create(
{
"move_id": move.id,
"picking_id": picking.id,
"product_id": self.product.id,
"product_uom_id": self.uom_unit.id,
"location_id": self.location_src.id,
"location_dest_id": self.location_dest.id,
"quantity": qty_done,
}
)
if extra_move:
self.env["stock.move"].create(
{
"name": self.product_2.name,
"product_id": self.product_2.id,
"product_uom_qty": 1.0,
"product_uom": self.uom_unit.id,
"picking_id": picking.id,
"location_id": self.location_src.id,
"location_dest_id": self.location_dest.id,
}
)
batch.action_confirm()
batch._compute_summary_line_ids()
return batch
# Tests
def test_totals_and_pending_with_conversion(self):
"""Totals aggregate per product with UoM conversion and pending."""
@ -190,14 +253,6 @@ class TestBatchSummary(TransactionCase):
self.assertFalse(batch.summary_line_ids)
# 2. Create pickings with moves (without batch assignment)
product2 = self.env["product.product"].create(
{
"name": "Test Product 2",
"uom_id": self.uom_unit.id,
"uom_po_id": self.uom_unit.id,
}
)
picking_a = self.env["stock.picking"].create(
{
"picking_type_id": self.picking_type.id,
@ -226,8 +281,8 @@ class TestBatchSummary(TransactionCase):
)
self.env["stock.move"].create(
{
"name": product2.name,
"product_id": product2.id,
"name": self.product_2.name,
"product_id": self.product_2.id,
"product_uom_qty": 10.0,
"product_uom": self.uom_unit.id,
"picking_id": picking_b.id,
@ -276,7 +331,7 @@ class TestBatchSummary(TransactionCase):
# Two products expected
products_in_summary = batch.summary_line_ids.mapped("product_id")
self.assertIn(self.product, products_in_summary)
self.assertIn(product2, products_in_summary)
self.assertIn(self.product_2, products_in_summary)
# Check aggregated quantities
line_product1 = batch.summary_line_ids.filtered(
@ -285,12 +340,12 @@ class TestBatchSummary(TransactionCase):
self.assertAlmostEqual(line_product1.qty_demanded, 8.0) # 5 + 3
line_product2 = batch.summary_line_ids.filtered(
lambda line: line.product_id == product2
lambda line: line.product_id == self.product_2
)
self.assertAlmostEqual(line_product2.qty_demanded, 10.0)
def test_done_requires_all_summary_lines_collected(self):
"""Batch validation must fail if there are unchecked collected lines."""
def test_done_requires_all_summary_lines_collected_without_backorder(self):
"""Full validation still blocks if processed products are unchecked."""
batch = self._create_batch_with_pickings()
batch.action_confirm()
@ -302,6 +357,32 @@ class TestBatchSummary(TransactionCase):
with self.assertRaises(UserError):
batch.action_done()
def test_partial_validation_waits_for_backorder_wizard_before_blocking(self):
"""Partial batch validation must open the backorder wizard first."""
batch = self._create_partial_batch()
with patch.object(
type(self.env["stock.picking"]),
"_check_backorder",
autospec=True,
return_value=batch.picking_ids,
):
action = batch.action_done()
self.assertIsInstance(action, dict)
self.assertEqual(action.get("res_model"), "stock.backorder.confirmation")
def test_partial_validation_checks_only_processed_products(self):
"""Unchecked lines with no processed quantity must remain informative."""
batch = self._create_partial_batch(extra_move=True)
with self.assertRaisesRegex(UserError, self.product.display_name) as err:
batch._check_all_products_collected([self.product.id])
self.assertNotIn(self.product_2.display_name, str(err.exception))
def test_check_all_products_collected_passes_when_all_checked(self):
"""Collected validation helper must pass when all lines are checked."""