diff --git a/shopfloor_batch_automatic_creation/actions/picking_batch_auto_create.py b/shopfloor_batch_automatic_creation/actions/picking_batch_auto_create.py
index 6095e9e8d6..862d7c371a 100644
--- a/shopfloor_batch_automatic_creation/actions/picking_batch_auto_create.py
+++ b/shopfloor_batch_automatic_creation/actions/picking_batch_auto_create.py
@@ -29,6 +29,7 @@ def create_batch(
group_by_commercial_partner=False,
maximum_number_of_preparation_lines=False,
stock_device_types=None,
+ split_picking_exceeding_limits=False,
**kwargs
):
make_picking_batch = self.env["make.picking.batch"].create(
@@ -37,6 +38,7 @@ def create_batch(
group_by_commercial_partner=group_by_commercial_partner,
maximum_number_of_preparation_lines=maximum_number_of_preparation_lines,
stock_device_types=stock_device_types,
+ split_picking_exceeding_limits=split_picking_exceeding_limits,
**kwargs
)
)
@@ -48,12 +50,14 @@ def _prepare_make_picking_batch_values(
group_by_commercial_partner=False,
maximum_number_of_preparation_lines=False,
stock_device_types=None,
+ split_picking_exceeding_limits=False,
**kwargs
):
values = {
"restrict_to_same_partner": group_by_commercial_partner,
"maximum_number_of_preparation_lines": maximum_number_of_preparation_lines,
"restrict_to_same_priority": True,
+ "split_picking_exceeding_limits": split_picking_exceeding_limits,
}
if picking_types and picking_types.ids:
values["picking_type_ids"] = [Command.set(picking_types.ids)]
diff --git a/shopfloor_batch_automatic_creation/models/shopfloor_menu.py b/shopfloor_batch_automatic_creation/models/shopfloor_menu.py
index d6350848ce..7e44964c7d 100644
--- a/shopfloor_batch_automatic_creation/models/shopfloor_menu.py
+++ b/shopfloor_batch_automatic_creation/models/shopfloor_menu.py
@@ -29,6 +29,14 @@ class ShopfloorMenu(models.Model):
string="Maximum number of preparation lines for the batch",
required=True,
)
+ batch_split_picking_exceeding_limits = fields.Boolean(
+ string="Split pickings exceeding limits",
+ help="If checked, the pickings exceeding the maximum number of lines, "
+ "volume or weight of available devices will be split into multiple pickings "
+ "to respect the limits. If unchecked, the pickings exceeding the limits will not "
+ "be added to the batch. The limits are defined by the limits of the last available "
+ "devices.",
+ )
stock_device_type_ids = fields.Many2many(
comodel_name="stock.device.type",
relation="shopfloor_menu_device_type_rel",
diff --git a/shopfloor_batch_automatic_creation/services/cluster_picking.py b/shopfloor_batch_automatic_creation/services/cluster_picking.py
index 5bfb22f50c..f006d3b4ec 100644
--- a/shopfloor_batch_automatic_creation/services/cluster_picking.py
+++ b/shopfloor_batch_automatic_creation/services/cluster_picking.py
@@ -25,6 +25,7 @@ def _batch_auto_create(self):
group_by_commercial_partner=menu.batch_group_by_commercial_partner,
maximum_number_of_preparation_lines=menu.batch_maximum_number_of_preparation_lines,
stock_device_types=menu.stock_device_type_ids,
+ split_picking_exceeding_limits=menu.batch_split_picking_exceeding_limits,
shopfloor_menu=menu,
)
diff --git a/shopfloor_batch_automatic_creation/tests/test_batch_create.py b/shopfloor_batch_automatic_creation/tests/test_batch_create.py
index cc814920d4..6d7ca430f9 100644
--- a/shopfloor_batch_automatic_creation/tests/test_batch_create.py
+++ b/shopfloor_batch_automatic_creation/tests/test_batch_create.py
@@ -2,6 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
# pylint: disable=missing-return
+from contextlib import contextmanager
from odoo.addons.shopfloor.tests.common import CommonCase
from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES
@@ -168,9 +169,8 @@ def test_create_batch_max_volume(self):
def test_create_batch_max_volume_all_exceed(self):
"""Test batch creation with all pickings exceeding the max volume.
- In such case the batch is anyway created with the first picking in it
- because it's ok to have one picking exceeding the max volume (otherwise
- those pickings will never be processed).
+ In such case no batch is created except if the option to split the
+ picking exceeding the limits is enabled.
"""
# each picking has 2 lines of 10 units, set volume of 0.1m3 per unit,
# we'll have a total volume of 2m3 per picking
@@ -180,6 +180,8 @@ def test_create_batch_max_volume_all_exceed(self):
self.product_d.volume = 0.1
self.pickings.move_ids._compute_volume()
self.pickings._compute_volume()
+ for picking in self.pickings:
+ self.assertGreater(picking.volume, 1)
# with a max volume of 1, we can normally take no picking
self.device.max_volume = 1
batch = self.auto_batch.create_batch(
@@ -187,7 +189,15 @@ def test_create_batch_max_volume_all_exceed(self):
stock_device_types=self.device,
maximum_number_of_preparation_lines=20,
)
- self.assertFalse(batch.picking_ids)
+ self.assertFalse(batch)
+ batch = self.auto_batch.create_batch(
+ self.picking_type,
+ stock_device_types=self.device,
+ maximum_number_of_preparation_lines=20,
+ split_picking_exceeding_limits=True,
+ )
+ self.assertTrue(batch.picking_ids)
+ self.assertLessEqual(sum(batch.picking_ids.mapped("volume")), 1)
def test_cluster_picking_select(self):
self.menu.sudo().batch_create = True
@@ -280,3 +290,33 @@ def test_specific_device_sort_key(self):
lines.mapped("product_id"),
self.product_b + self.product_c + self.product_d + self.product_a,
)
+
+ def test_menu_options_passed_to_batch_wizard(self):
+ self.menu.sudo().batch_create = True
+ self.menu.sudo().batch_group_by_commercial_partner = True
+ self.menu.sudo().batch_maximum_number_of_preparation_lines = 10
+ self.menu.sudo().batch_split_picking_exceeding_limits = True
+ wizard_class = self.env["make.picking.batch"].__class__
+ method_called = False
+
+ @contextmanager
+ def mock_create_batch_and_check_attributes(*args, **kwargs):
+ def side_effect(*args, **kwargs):
+ nonlocal method_called
+ method_called = True
+ self_mock = args[0]
+ self.assertEqual(self_mock.restrict_to_same_partner, True)
+ self.assertEqual(self_mock.maximum_number_of_preparation_lines, 10)
+ self.assertEqual(self_mock.split_picking_exceeding_limits, True)
+ return self.env["stock.picking.batch"].create(
+ {"name": "test", "picking_ids": self.pickings.ids}
+ )
+
+ original_create_batch = wizard_class._create_batch
+ wizard_class._create_batch = side_effect
+ yield
+ wizard_class._create_batch = original_create_batch
+
+ with mock_create_batch_and_check_attributes():
+ self.service.dispatch("find_batch")
+ self.assertTrue(method_called)
diff --git a/shopfloor_batch_automatic_creation/views/shopfloor_menu_views.xml b/shopfloor_batch_automatic_creation/views/shopfloor_menu_views.xml
index 74b3fec1e9..e7d4d6c22d 100644
--- a/shopfloor_batch_automatic_creation/views/shopfloor_menu_views.xml
+++ b/shopfloor_batch_automatic_creation/views/shopfloor_menu_views.xml
@@ -21,6 +21,10 @@
name="batch_maximum_number_of_preparation_lines"
attrs="{'invisible': [('batch_create', '=', False)]}"
/>
+