Fix stock picking batch date and does not split batchs by consumer group
This commit is contained in:
parent
b73f031dfb
commit
828278573d
2 changed files with 48 additions and 59 deletions
|
|
@ -890,47 +890,41 @@ class GroupOrder(models.Model):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
StockPickingBatch = self.env["stock.picking.batch"].sudo()
|
StockPickingBatch = self.env["stock.picking.batch"].sudo()
|
||||||
|
|
||||||
# Group sale orders by consumer_group_id
|
# Create batches per group order, not per consumer group.
|
||||||
groups = {}
|
# If multiple picking types exist, keep one batch per picking type.
|
||||||
for so in sale_orders:
|
grouped_pickings = {}
|
||||||
group_id = so.consumer_group_id.id or False
|
pickings = sale_orders.picking_ids.filtered(
|
||||||
if group_id not in groups:
|
|
||||||
groups[group_id] = self.env["sale.order"]
|
|
||||||
groups[group_id] |= so
|
|
||||||
|
|
||||||
for consumer_group_id, group_sale_orders in groups.items():
|
|
||||||
# Get pickings without batch
|
|
||||||
pickings = group_sale_orders.picking_ids.filtered(
|
|
||||||
lambda p: p.state not in ("done", "cancel") and not p.batch_id
|
lambda p: p.state not in ("done", "cancel") and not p.batch_id
|
||||||
)
|
)
|
||||||
|
for picking in pickings:
|
||||||
|
grouped_pickings.setdefault(
|
||||||
|
picking.picking_type_id.id, self.env["stock.picking"]
|
||||||
|
)
|
||||||
|
grouped_pickings[picking.picking_type_id.id] |= picking
|
||||||
|
|
||||||
|
scheduled_date = self.pickup_date
|
||||||
|
if not scheduled_date and self.delivery_date:
|
||||||
|
scheduled_date = self.delivery_date - timedelta(days=1)
|
||||||
|
|
||||||
|
for picking_type_id, pickings in grouped_pickings.items():
|
||||||
if not pickings:
|
if not pickings:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Get consumer group name for batch description
|
batch_desc = self.name
|
||||||
consumer_group = self.env["res.partner"].browse(consumer_group_id)
|
|
||||||
batch_desc = (
|
|
||||||
f"{self.name} - {consumer_group.name}" if consumer_group else self.name
|
|
||||||
)
|
|
||||||
|
|
||||||
# Create the batch
|
|
||||||
batch = StockPickingBatch.create(
|
batch = StockPickingBatch.create(
|
||||||
{
|
{
|
||||||
"description": batch_desc,
|
"description": batch_desc,
|
||||||
"company_id": self.company_id.id,
|
"company_id": self.company_id.id,
|
||||||
"picking_type_id": pickings[0].picking_type_id.id,
|
"picking_type_id": picking_type_id,
|
||||||
"scheduled_date": self.pickup_date,
|
"scheduled_date": scheduled_date,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Assign pickings to the batch
|
|
||||||
pickings.write({"batch_id": batch.id})
|
pickings.write({"batch_id": batch.id})
|
||||||
|
|
||||||
_logger.info(
|
_logger.info(
|
||||||
"Cron: Created batch %s with %d pickings for group order %s, "
|
"Cron: Created batch %s with %d pickings for group order %s",
|
||||||
"consumer group %s",
|
|
||||||
batch.name,
|
batch.name,
|
||||||
len(pickings),
|
len(pickings),
|
||||||
self.name,
|
self.name,
|
||||||
consumer_group.name if consumer_group else "N/A",
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -227,8 +227,8 @@ class TestCronPickingBatch(TransactionCase):
|
||||||
"Cron should snapshot and preserve the current cycle pickup_date when confirming",
|
"Cron should snapshot and preserve the current cycle pickup_date when confirming",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_cron_creates_picking_batch_per_consumer_group(self):
|
def test_cron_creates_single_picking_batch_for_group_order(self):
|
||||||
"""Test that cron creates separate picking batches per consumer group."""
|
"""Test that cron creates a single picking batch for the whole group order."""
|
||||||
# Create group order with cutoff yesterday (past)
|
# Create group order with cutoff yesterday (past)
|
||||||
group_order = self._create_group_order(cutoff_in_past=True)
|
group_order = self._create_group_order(cutoff_in_past=True)
|
||||||
|
|
||||||
|
|
@ -247,39 +247,30 @@ class TestCronPickingBatch(TransactionCase):
|
||||||
self.assertTrue(so1.picking_ids, "Sale order 1 should have pickings")
|
self.assertTrue(so1.picking_ids, "Sale order 1 should have pickings")
|
||||||
self.assertTrue(so2.picking_ids, "Sale order 2 should have pickings")
|
self.assertTrue(so2.picking_ids, "Sale order 2 should have pickings")
|
||||||
|
|
||||||
# Check that pickings have batch_id assigned
|
# Check that all pickings share the same batch
|
||||||
for picking in so1.picking_ids:
|
|
||||||
self.assertTrue(
|
|
||||||
picking.batch_id,
|
|
||||||
"Picking from SO1 should be assigned to a batch",
|
|
||||||
)
|
|
||||||
|
|
||||||
for picking in so2.picking_ids:
|
|
||||||
self.assertTrue(
|
|
||||||
picking.batch_id,
|
|
||||||
"Picking from SO2 should be assigned to a batch",
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check that batches are different (one per consumer group)
|
|
||||||
batch_1 = so1.picking_ids[0].batch_id
|
batch_1 = so1.picking_ids[0].batch_id
|
||||||
batch_2 = so2.picking_ids[0].batch_id
|
batch_2 = so2.picking_ids[0].batch_id
|
||||||
|
|
||||||
self.assertNotEqual(
|
self.assertEqual(
|
||||||
batch_1.id,
|
batch_1.id,
|
||||||
batch_2.id,
|
batch_2.id,
|
||||||
"Different consumer groups should have different batches",
|
"Different consumer groups in the same group order should share one batch",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check batch descriptions contain consumer group names
|
# Check that there is only one batch record created
|
||||||
self.assertIn(
|
self.assertEqual(
|
||||||
self.consumer_group_1.name,
|
self.env["stock.picking.batch"].search_count(
|
||||||
batch_1.description,
|
[("description", "=", group_order.name)]
|
||||||
"Batch 1 description should include consumer group 1 name",
|
),
|
||||||
|
1,
|
||||||
|
"Only one batch should be created for a single group order",
|
||||||
)
|
)
|
||||||
self.assertIn(
|
|
||||||
self.consumer_group_2.name,
|
# Check batch description uses the group order name only
|
||||||
batch_2.description,
|
self.assertEqual(
|
||||||
"Batch 2 description should include consumer group 2 name",
|
batch_1.description,
|
||||||
|
group_order.name,
|
||||||
|
"Batch description should be the group order name",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_cron_same_consumer_group_same_batch(self):
|
def test_cron_same_consumer_group_same_batch(self):
|
||||||
|
|
@ -342,10 +333,10 @@ class TestCronPickingBatch(TransactionCase):
|
||||||
self.assertTrue(so.picking_ids, "Sale order should have pickings")
|
self.assertTrue(so.picking_ids, "Sale order should have pickings")
|
||||||
batch = so.picking_ids[0].batch_id
|
batch = so.picking_ids[0].batch_id
|
||||||
self.assertTrue(batch, "Picking should have a batch")
|
self.assertTrue(batch, "Picking should have a batch")
|
||||||
# scheduled_date should be set (not False/None)
|
self.assertEqual(
|
||||||
self.assertTrue(
|
batch.scheduled_date.date(),
|
||||||
batch.scheduled_date,
|
group_order.pickup_date,
|
||||||
"Batch should have a scheduled_date set",
|
"Batch scheduled_date should be the pickup date (day before delivery)",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_cron_does_not_duplicate_batches(self):
|
def test_cron_does_not_duplicate_batches(self):
|
||||||
|
|
@ -364,13 +355,17 @@ class TestCronPickingBatch(TransactionCase):
|
||||||
|
|
||||||
self.assertTrue(so.picking_ids, "Sale order should have pickings")
|
self.assertTrue(so.picking_ids, "Sale order should have pickings")
|
||||||
batch_first = so.picking_ids[0].batch_id
|
batch_first = so.picking_ids[0].batch_id
|
||||||
batch_count_first = self.env["stock.picking.batch"].search_count([])
|
batch_count_first = self.env["stock.picking.batch"].search_count(
|
||||||
|
[("description", "=", group_order.name)]
|
||||||
|
)
|
||||||
|
|
||||||
# Call second time
|
# Call second time
|
||||||
group_order._confirm_linked_sale_orders()
|
group_order._confirm_linked_sale_orders()
|
||||||
|
|
||||||
batch_second = so.picking_ids[0].batch_id
|
batch_second = so.picking_ids[0].batch_id
|
||||||
batch_count_second = self.env["stock.picking.batch"].search_count([])
|
batch_count_second = self.env["stock.picking.batch"].search_count(
|
||||||
|
[("description", "=", group_order.name)]
|
||||||
|
)
|
||||||
|
|
||||||
# Should be same batch, no duplicates
|
# Should be same batch, no duplicates
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
@ -427,7 +422,7 @@ class TestCronPickingBatch(TransactionCase):
|
||||||
def _patched_action_confirm(recordset):
|
def _patched_action_confirm(recordset):
|
||||||
should_fail = any(so.name == "SO-FAIL" for so in recordset)
|
should_fail = any(so.name == "SO-FAIL" for so in recordset)
|
||||||
if should_fail and not recordset.env.context.get("from_orderpoint"):
|
if should_fail and not recordset.env.context.get("from_orderpoint"):
|
||||||
raise UserError("Simulated stock route error")
|
raise UserError()
|
||||||
return original_action_confirm(recordset)
|
return original_action_confirm(recordset)
|
||||||
|
|
||||||
with patch.object(SaleOrderClass, "action_confirm", _patched_action_confirm):
|
with patch.object(SaleOrderClass, "action_confirm", _patched_action_confirm):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue