From c9800d587c6fbd63cc195faf4f79e6d5fc0a4c73 Mon Sep 17 00:00:00 2001 From: Mmequignon Date: Thu, 14 Sep 2023 16:56:30 +0200 Subject: [PATCH] shopfloor_reception: set_quantity - Add cancel button --- shopfloor_reception/services/reception.py | 44 +++++++++ .../tests/test_set_quantity_action.py | 90 +++++++++++++++++++ .../static/src/scenario/reception.js | 1 + .../static/src/scenario/reception_states.js | 15 ++-- 4 files changed, 142 insertions(+), 8 deletions(-) diff --git a/shopfloor_reception/services/reception.py b/shopfloor_reception/services/reception.py index bb9f73e6d6b..12b9a0ad747 100644 --- a/shopfloor_reception/services/reception.py +++ b/shopfloor_reception/services/reception.py @@ -1160,6 +1160,22 @@ def set_quantity( ) return self._response_for_set_quantity(picking, selected_line) + def set_quantity__cancel_action(self, picking_id, selected_line_id): + picking = self.env["stock.picking"].browse(picking_id) + selected_line = self.env["stock.move.line"].browse(selected_line_id) + message = self._check_picking_status(picking) + if message: + return self._response_for_set_quantity( + picking, selected_line, message=message + ) + if selected_line.exists(): + if selected_line.product_uom_qty: + stock = self._actions_for("stock") + stock.unmark_move_line_as_picked(selected_line) + else: + selected_line.unlink() + return self._response_for_select_move(picking) + def _set_quantity__process__set_qty_and_split(self, picking, line, quantity): move = line.move_id sum(move.move_line_ids.mapped("qty_done")) @@ -1429,6 +1445,16 @@ def set_quantity(self): "confirmation": {"type": "boolean"}, } + def set_quantity__cancel_action(self): + return { + "picking_id": {"coerce": to_int, "required": True, "type": "integer"}, + "selected_line_id": { + "coerce": to_int, + "type": "integer", + "required": True, + }, + } + def process_with_existing_pack(self): return { "picking_id": {"coerce": to_int, "required": True, "type": "integer"}, @@ -1560,6 +1586,9 @@ def _set_lot_next_states(self): def _set_quantity_next_states(self): return {"set_quantity", "select_move", "set_destination"} + def _set_quantity__cancel_action_next_states(self): + return {"set_quantity", "select_move"} + def _set_destination_next_states(self): return {"set_destination", "select_move"} @@ -1640,6 +1669,16 @@ def _schema_set_quantity(self): }, } + @property + def _schema_set_quantity__cancel_action(self): + return { + "selected_move_line": { + "type": "list", + "schema": {"type": "dict", "schema": self.schemas.move_line()}, + }, + "picking": {"type": "dict", "schema": self.schemas.picking()}, + } + @property def _schema_set_destination(self): return { @@ -1717,6 +1756,11 @@ def set_lot_confirm_action(self): def set_quantity(self): return self._response_schema(next_states=self._set_quantity_next_states()) + def set_quantity__cancel_action(self): + return self._response_schema( + next_states=self._set_quantity__cancel_action_next_states() + ) + def process_with_existing_pack(self): return self._response_schema( next_states=self._process_with_existing_pack_next_states() diff --git a/shopfloor_reception/tests/test_set_quantity_action.py b/shopfloor_reception/tests/test_set_quantity_action.py index 7cf981eacc3..8881e64a5fd 100644 --- a/shopfloor_reception/tests/test_set_quantity_action.py +++ b/shopfloor_reception/tests/test_set_quantity_action.py @@ -84,3 +84,93 @@ def test_process_without_package(self): }, ) self.assertFalse(self.selected_move_line.result_package_id) + + def test_cancel_action(self): + picking = self._create_picking() + move_product_a = picking.move_lines.filtered( + lambda l: l.product_id == self.product_a + ) + # User 1 and 2 selects the same picking + service_user_1 = self.service + service_user_1.dispatch("scan_document", params={"barcode": picking.name}) + user2 = self.shopfloor_manager + service_user_2 = self._get_service_for_user(user2) + response = service_user_2.dispatch( + "scan_document", params={"barcode": picking.name} + ) + # both users selects the same move + service_user_1.dispatch( + "scan_line", + params={"picking_id": picking.id, "barcode": self.product_a.barcode}, + ) + move_line_user_1 = move_product_a.move_line_ids + service_user_2.dispatch( + "scan_line", + params={"picking_id": picking.id, "barcode": self.product_a.barcode}, + ) + move_line_user_2 = move_product_a.move_line_ids - move_line_user_1 + # And both sets the qty done to 10 + service_user_1.dispatch( + "set_quantity", + params={ + "picking_id": picking.id, + "selected_line_id": move_line_user_1.id, + "quantity": 10, + }, + ) + service_user_2.dispatch( + "set_quantity", + params={ + "picking_id": picking.id, + "selected_line_id": move_line_user_2.id, + "quantity": 10, + }, + ) + # Users are blocked, product_uom_qty is 10, but both users have qty_done=10 + # on their move line, therefore, none of them can confirm + expected_message = { + "body": "You cannot process that much units.", + "message_type": "error", + } + response = service_user_1.dispatch( + "process_with_new_pack", + params={ + "picking_id": picking.id, + "selected_line_id": move_line_user_1.id, + "quantity": 10.0, + }, + ) + self.assertMessage(response, expected_message) + response = service_user_2.dispatch( + "process_with_new_pack", + params={ + "picking_id": picking.id, + "selected_line_id": move_line_user_2.id, + "quantity": 10.0, + }, + ) + self.assertMessage(response, expected_message) + # make user1 cancel + service_user_1.dispatch( + "set_quantity__cancel_action", + params={ + "picking_id": picking.id, + "selected_line_id": move_line_user_1.id, + }, + ) + # Since we reused the move line created by odoo for the first user, we only + # reset the line + self.assertTrue(move_line_user_1.exists()) + self.assertFalse(move_line_user_1.shopfloor_user_id) + self.assertEqual(move_line_user_1.qty_done, 0) + self.assertEqual(move_line_user_1.product_uom_qty, 10) + # make user cancel + service_user_2.dispatch( + "set_quantity__cancel_action", + params={ + "picking_id": picking.id, + "selected_line_id": move_line_user_2.id, + }, + ) + # This line has been created by shopfloor, therefore, we unlinked it + self.assertFalse(move_line_user_2.exists()) diff --git a/shopfloor_reception_mobile/static/src/scenario/reception.js b/shopfloor_reception_mobile/static/src/scenario/reception.js index f366d422d90..38d2531eca8 100644 --- a/shopfloor_reception_mobile/static/src/scenario/reception.js +++ b/shopfloor_reception_mobile/static/src/scenario/reception.js @@ -153,6 +153,7 @@ const Reception = { + diff --git a/shopfloor_reception_mobile/static/src/scenario/reception_states.js b/shopfloor_reception_mobile/static/src/scenario/reception_states.js index fca90f6e804..4a2438a87d3 100644 --- a/shopfloor_reception_mobile/static/src/scenario/reception_states.js +++ b/shopfloor_reception_mobile/static/src/scenario/reception_states.js @@ -171,6 +171,7 @@ export const reception_states = function () { events: { qty_edit: "on_qty_edit", go_back: "on_back", + cancel: "on_cancel", }, on_qty_edit: (qty) => { this.scan_destination_qty = parseInt(qty, 10); @@ -188,14 +189,12 @@ export const reception_states = function () { ); }, on_cancel: () => { - // TODO: this endpoing is currently missing in the backend, - // and it's currently in the roadmap. - // Once it's implemented, uncomment this call. - // this.wait_call( - // this.odoo.call("cancel", { - // package_level_id: this.state.data.id, - // }) - // ); + this.wait_call( + this.odoo.call("set_quantity__cancel_action", { + picking_id: this.state.data.picking.id, + selected_line_id: this.line_being_handled.id, + }) + ); }, on_add_to_existing_pack: () => { this.wait_call(