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)]}" /> +