Maintainers
This module is maintained by the OCA.
-
+
+
+
OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.
diff --git a/shopfloor_packing/tests/__init__.py b/shopfloor_packing/tests/__init__.py index e84b11d98c7..b3db59427f7 100644 --- a/shopfloor_packing/tests/__init__.py +++ b/shopfloor_packing/tests/__init__.py @@ -1,2 +1,5 @@ -from . import test_cluster_picking_pack_picking -from . import test_cluster_picking_unload +from . import ( + test_cluster_picking_pack_picking, + test_cluster_picking_pick_pack, + test_cluster_picking_unload, +) diff --git a/shopfloor_packing/tests/common.py b/shopfloor_packing/tests/common.py new file mode 100644 index 00000000000..0655d670229 --- /dev/null +++ b/shopfloor_packing/tests/common.py @@ -0,0 +1,16 @@ +# Copyright 2021 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.shopfloor.tests.test_cluster_picking_unload import ( + ClusterPickingUnloadingCommonCase, +) + + +# pylint: disable=missing-return +class ClusterPickingUnloadPackingCommonCase(ClusterPickingUnloadingCommonCase): + @classmethod + def setUpClassBaseData(cls, *args, **kwargs): + super().setUpClassBaseData(*args, **kwargs) + cls.bin1.write({"name": "bin1", "is_internal": True}) + cls.bin2.write({"name": "bin2", "is_internal": True}) + cls.menu.sudo().pack_pickings = True diff --git a/shopfloor_packing/tests/test_cluster_picking_pack_picking.py b/shopfloor_packing/tests/test_cluster_picking_pack_picking.py index 22a44f761b7..1be91709e04 100644 --- a/shopfloor_packing/tests/test_cluster_picking_pack_picking.py +++ b/shopfloor_packing/tests/test_cluster_picking_pack_picking.py @@ -1,19 +1,6 @@ # Copyright 2021 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.addons.shopfloor.tests.test_cluster_picking_unload import ( - ClusterPickingUnloadingCommonCase, -) - - -# pylint: disable=missing-return -class ClusterPickingUnloadPackingCommonCase(ClusterPickingUnloadingCommonCase): - @classmethod - def setUpClassBaseData(cls, *args, **kwargs): - super().setUpClassBaseData(*args, **kwargs) - cls.bin1.write({"name": "bin1", "is_internal": True}) - cls.bin2.write({"name": "bin2", "is_internal": True}) - cls.menu.sudo().pack_pickings = True +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from .common import ClusterPickingUnloadPackingCommonCase class TestClusterPickingPrepareUnload(ClusterPickingUnloadPackingCommonCase): diff --git a/shopfloor_packing/tests/test_cluster_picking_pick_pack.py b/shopfloor_packing/tests/test_cluster_picking_pick_pack.py new file mode 100644 index 00000000000..ad256cc31ea --- /dev/null +++ b/shopfloor_packing/tests/test_cluster_picking_pick_pack.py @@ -0,0 +1,556 @@ +# Copyright 2024 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.fields import first + +from .common import ClusterPickingUnloadPackingCommonCase + + +class TestClusterPickingPrepareUnload(ClusterPickingUnloadPackingCommonCase): + def _create_package_type(self): + self.carrier_product = ( + self.env["product.product"] + .sudo() + .create( + { + "name": "Test Product", + "type": "service", + } + ) + ) + self.carrier = ( + self.env["delivery.carrier"] + .sudo() + .create( + { + "name": "Test Carrier", + "product_id": self.carrier_product.id, + } + ) + ) + self.package_type = ( + self.env["stock.package.type"] + .sudo() + .create( + { + "name": "BOX-5", + "package_carrier_type": "none", + "number_of_parcels": 5.0, + "barcode": "BOX-5", + } + ) + ) + self.package_types = self.env["stock.package.type"].search( + [("package_carrier_type", "=", "none")], order="number_of_parcels,name" + ) + + def test_prepare_unload_all_same_dest_with_dest_package(self): + """ + Activate the behavior that allows to pack at the pick step (cluster) + Activate the behavior that change the default action -> Scan the package type + At the unload step, ask to select a delivery package (from types) + """ + self.menu.sudo().write( + { + "pick_pack_same_time": True, + "default_pack_pickings_action": "package_type", + } + ) + + move_lines = self.move_lines + self._set_dest_package_and_done(move_lines[:1], self.bin2) + self._set_dest_package_and_done(move_lines[1:], self.bin1) + move_lines.write({"location_dest_id": self.packing_location.id}) + response = self.service.dispatch( + "prepare_unload", params={"picking_batch_id": self.batch.id} + ) + location = self.packing_location + # The first bin to process is bin1 we should therefore scan the bin 1 + # to pack and put in pack + picking = move_lines[-1].picking_id + data = self.data_detail.pack_picking_detail(picking) + self.assert_response( + response, + next_state="pack_picking_scan_pack", + data=data, + ) + lines = picking.move_line_ids.filtered( + lambda ml: ml.result_package_id.is_internal + ).sorted(key=lambda ml: ml.result_package_id.name) + # we scan the pack + response = self.service.dispatch( + "scan_packing_to_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "barcode": self.bin1.name, + }, + ) + + data = self.data.select_package(picking, lines) + self.assert_response( + response, + next_state="select_package", + data=data, + ) + # we process to the put in pack + response = self.service.dispatch( + "put_in_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "nbr_packages": 4, + }, + ) + message = self.service.msg_store.stock_picking_packed_successfully(picking) + result_package = picking.move_line_ids.mapped("result_package_id") + self.assertEqual(len(result_package), 1) + self.assertEqual(result_package[0].number_of_parcels, 4) + + picking = move_lines[0].picking_id + data = self.data_detail.pack_picking_detail(picking) + # message = self.service.msg_store.stock_picking_packed_successfully(picking) + self.assert_response( + response, next_state="pack_picking_scan_pack", data=data, message=message + ) + lines = picking.move_line_ids.filtered( + lambda ml: ml.result_package_id.is_internal + ).sorted(key=lambda ml: ml.result_package_id.name) + # we scan the pack + response = self.service.dispatch( + "scan_packing_to_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "barcode": self.bin2.name, + }, + ) + data = self.data.select_package(picking, lines) + self.assert_response( + response, + next_state="select_package", + data=data, + ) + # we process to the put in pack + response = self.service.dispatch( + "put_in_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "nbr_packages": 2, + }, + ) + data = self._data_for_batch(self.batch, location) + message = self.service.msg_store.stock_picking_packed_successfully(picking) + self.assert_response( + response, next_state="unload_all", data=data, message=message + ) + + result_package = picking.move_line_ids.mapped("result_package_id") + self.assertEqual(len(result_package), 1) + self.assertEqual(result_package[0].number_of_parcels, 2) + + def test_pack_no_package_type(self): + """ + Activate the behavior that allows to pack at the pick step (cluster) + Activate the behavior that change the default action -> Scan the package type + At the unload step, ask to select a delivery package (from types) + """ + self.menu.sudo().write( + { + "pick_pack_same_time": True, + "default_pack_pickings_action": "package_type", + } + ) + + move_lines = self.move_lines + self._set_dest_package_and_done(move_lines[:1], self.bin2) + self._set_dest_package_and_done(move_lines[1:], self.bin1) + move_lines.write({"location_dest_id": self.packing_location.id}) + response = self.service.dispatch( + "prepare_unload", params={"picking_batch_id": self.batch.id} + ) + # The first bin to process is bin1 we should therefore scan the bin 1 + # to pack and put in pack + picking = move_lines[-1].picking_id + data = self.data_detail.pack_picking_detail(picking) + self.assert_response( + response, + next_state="pack_picking_scan_pack", + data=data, + ) + lines = picking.move_line_ids.filtered( + lambda ml: ml.result_package_id.is_internal + ).sorted(key=lambda ml: ml.result_package_id.name) + # we scan the pack + response = self.service.dispatch( + "scan_packing_to_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "barcode": self.bin1.name, + }, + ) + + data = self.data.select_package(picking, lines) + self.assert_response( + response, + next_state="select_package", + data=data, + ) + # We use new pack + response = self.service.dispatch( + "list_delivery_package_types", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "selected_line_ids": lines.ids, + }, + ) + message = { + "message_type": "warning", + "body": "No delivery package type available.", + } + data = self.data.select_package(picking, lines) + self.assert_response( + response, next_state="select_package", data=data, message=message + ) + + def test_list_delivery_package_picking_done(self): + """ """ + self.menu.sudo().write( + { + "pick_pack_same_time": True, + "default_pack_pickings_action": "package_type", + } + ) + + move_lines = self.move_lines + self._set_dest_package_and_done(move_lines[:1], self.bin2) + self._set_dest_package_and_done(move_lines[1:], self.bin1) + move_lines.write({"location_dest_id": self.packing_location.id}) + response = self.service.dispatch( + "prepare_unload", params={"picking_batch_id": self.batch.id} + ) + # The first bin to process is bin1 we should therefore scan the bin 1 + # to pack and put in pack + picking = move_lines[-1].picking_id + data = self.data_detail.pack_picking_detail(picking) + self.assert_response( + response, + next_state="pack_picking_scan_pack", + data=data, + ) + lines = picking.move_line_ids.filtered( + lambda ml: ml.result_package_id.is_internal + ).sorted(key=lambda ml: ml.result_package_id.name) + # we scan the pack + response = self.service.dispatch( + "scan_packing_to_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "barcode": self.bin1.name, + }, + ) + + data = self.data.select_package(picking, lines) + self.assert_response( + response, + next_state="select_package", + data=data, + ) + + picking._action_done() + # Delivery is already done + response = self.service.dispatch( + "list_delivery_package_types", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "selected_line_ids": lines.ids, + }, + ) + message = {"message_type": "info", "body": "Operation already processed."} + data = {} + self.assert_response(response, next_state="start", data=data, message=message) + + def test_list_delivery_package_picking_qty_superior(self): + self._create_package_type() + self.menu.sudo().write( + { + "pick_pack_same_time": True, + "default_pack_pickings_action": "package_type", + } + ) + + move_lines = self.move_lines + self._set_dest_package_and_done(move_lines[:1], self.bin2) + self._set_dest_package_and_done(move_lines[1:], self.bin1) + move_lines.write({"location_dest_id": self.packing_location.id}) + response = self.service.dispatch( + "prepare_unload", params={"picking_batch_id": self.batch.id} + ) + # The first bin to process is bin1 we should therefore scan the bin 1 + # to pack and put in pack + picking = move_lines[-1].picking_id + picking.carrier_id = self.carrier + data = self.data_detail.pack_picking_detail(picking) + self.assert_response( + response, + next_state="pack_picking_scan_pack", + data=data, + ) + lines = picking.move_line_ids.filtered( + lambda ml: ml.result_package_id.is_internal + ).sorted(key=lambda ml: ml.result_package_id.name) + # we scan the pack + response = self.service.dispatch( + "scan_packing_to_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "barcode": self.bin1.name, + }, + ) + + data = self.data.select_package(picking, lines) + self.assert_response( + response, + next_state="select_package", + data=data, + ) + line = first(picking.move_line_ids) + line.qty_done = line.reserved_qty + 1 + # Delivery is already done + response = self.service.dispatch( + "list_delivery_package_types", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "selected_line_ids": lines.ids, + }, + ) + message = { + "message_type": "warning", + "body": "The quantity scanned for one or more lines " + "cannot be higher than the maximum allowed. (%s : %s > %s)" + % (line.product_id.name, line.qty_done, line.reserved_qty), + } + next_picking = first( + self.batch.picking_ids.filtered(lambda p: p.is_shopfloor_packing_todo) + ) + data = self.data.select_package(next_picking, lines) + # data = self.data_detail.pack_picking_detail(next_picking) + self.assert_response( + response, next_state="select_package", data=data, message=message + ) + + def test_pack_package_type(self): + self._create_package_type() + self.menu.sudo().write( + { + "pick_pack_same_time": True, + "default_pack_pickings_action": "package_type", + } + ) + + move_lines = self.move_lines + self._set_dest_package_and_done(move_lines[:1], self.bin2) + self._set_dest_package_and_done(move_lines[1:], self.bin1) + move_lines.write({"location_dest_id": self.packing_location.id}) + response = self.service.dispatch( + "prepare_unload", params={"picking_batch_id": self.batch.id} + ) + # The first bin to process is bin1 we should therefore scan the bin 1 + # to pack and put in pack + picking = move_lines[-1].picking_id + picking.carrier_id = self.carrier + data = self.data_detail.pack_picking_detail(picking) + self.assert_response( + response, + next_state="pack_picking_scan_pack", + data=data, + ) + lines = picking.move_line_ids.filtered( + lambda ml: ml.result_package_id.is_internal + ).sorted(key=lambda ml: ml.result_package_id.name) + # we scan the pack + response = self.service.dispatch( + "scan_packing_to_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "barcode": self.bin1.name, + }, + ) + + data = self.data.select_package(picking, lines) + self.assert_response( + response, + next_state="select_package", + data=data, + ) + # We use new pack + response = self.service.dispatch( + "list_delivery_package_types", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "selected_line_ids": lines.ids, + }, + ) + data = {} + data["packaging"] = self.data.delivery_packaging_list(self.package_types) + data["picking"] = self.data.picking(picking) + self.assert_response( + response, + next_state="select_delivery_packaging", + data=data, + ) + + response = self.service.dispatch( + "put_in_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "package_type_id": self.package_type.id, + }, + ) + message = self.service.msg_store.stock_picking_packed_successfully(picking) + next_picking = self.batch.picking_ids.filtered( + lambda p: p.is_shopfloor_packing_todo + ) + data = data = self.data_detail.pack_picking_detail(next_picking) + + self.assert_response( + response, next_state="pack_picking_scan_pack", data=data, message=message + ) + + def test_pack_package_type_scan(self): + self._create_package_type() + self.menu.sudo().write( + { + "pick_pack_same_time": True, + "default_pack_pickings_action": "package_type", + } + ) + + move_lines = self.move_lines + self._set_dest_package_and_done(move_lines[:1], self.bin2) + self._set_dest_package_and_done(move_lines[1:], self.bin1) + move_lines.write({"location_dest_id": self.packing_location.id}) + response = self.service.dispatch( + "prepare_unload", params={"picking_batch_id": self.batch.id} + ) + # The first bin to process is bin1 we should therefore scan the bin 1 + # to pack and put in pack + picking = move_lines[-1].picking_id + picking.carrier_id = self.carrier + data = self.data_detail.pack_picking_detail(picking) + self.assert_response( + response, + next_state="pack_picking_scan_pack", + data=data, + ) + lines = picking.move_line_ids.filtered( + lambda ml: ml.result_package_id.is_internal + ).sorted(key=lambda ml: ml.result_package_id.name) + # we scan the pack + response = self.service.dispatch( + "scan_packing_to_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "barcode": self.bin1.name, + }, + ) + + data = self.data.select_package(picking, lines) + self.assert_response( + response, + next_state="select_package", + data=data, + ) + + # we scan the package type + response = self.service.dispatch( + "scan_package_action", + params={ + "picking_id": picking.id, + "selected_line_ids": lines.ids, + "barcode": "BOX-5", + }, + ) + message = self.service.msg_store.stock_picking_packed_successfully(picking) + next_picking = first( + self.batch.picking_ids.filtered(lambda p: p.is_shopfloor_packing_todo) + ) + data = self.data_detail.pack_picking_detail(next_picking) + self.assert_response( + response, next_state="pack_picking_scan_pack", message=message, data=data + ) + + def test_pack_package_type_no_picking(self): + self._create_package_type() + self.menu.sudo().write( + { + "pick_pack_same_time": True, + "default_pack_pickings_action": "package_type", + } + ) + + move_lines = self.move_lines + self._set_dest_package_and_done(move_lines[:1], self.bin2) + self._set_dest_package_and_done(move_lines[1:], self.bin1) + move_lines.write({"location_dest_id": self.packing_location.id}) + response = self.service.dispatch( + "prepare_unload", params={"picking_batch_id": self.batch.id} + ) + # The first bin to process is bin1 we should therefore scan the bin 1 + # to pack and put in pack + picking = move_lines[-1].picking_id + picking.carrier_id = self.carrier + data = self.data_detail.pack_picking_detail(picking) + self.assert_response( + response, + next_state="pack_picking_scan_pack", + data=data, + ) + lines = picking.move_line_ids.filtered( + lambda ml: ml.result_package_id.is_internal + ).sorted(key=lambda ml: ml.result_package_id.name) + # we scan the pack + response = self.service.dispatch( + "scan_packing_to_pack", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "barcode": self.bin1.name, + }, + ) + + data = self.data.select_package(picking, lines) + self.assert_response( + response, + next_state="select_package", + data=data, + ) + + # Another action validated the picking + picking._action_done() + + # We use new pack + response = self.service.dispatch( + "list_delivery_package_types", + params={ + "picking_batch_id": self.batch.id, + "picking_id": picking.id, + "selected_line_ids": lines.ids, + }, + ) + data = {} + data["packaging"] = self.data.delivery_packaging_list(self.package_types) + data["picking"] = self.data.picking(picking) + message = {"message_type": "info", "body": "Operation already processed."} + data = {} + self.assert_response(response, next_state="start", data=data, message=message) diff --git a/shopfloor_packing/views/shopfloor_menu.xml b/shopfloor_packing/views/shopfloor_menu.xml index d24718b4c04..d251ba72d14 100644 --- a/shopfloor_packing/views/shopfloor_menu.xml +++ b/shopfloor_packing/views/shopfloor_menu.xml @@ -14,6 +14,7 @@ attrs="{'invisible': [('scenario', '!=', 'cluster_picking')]}" >