From 84abf3c2ed8d79bda3a598e83f9247b449221a28 Mon Sep 17 00:00:00 2001 From: Nicolas Delbovier Date: Thu, 16 Apr 2026 12:45:45 +0200 Subject: [PATCH 1/5] [IMP] shopfloor: add split option to unmark_move_line_as_picked Introduces a `split` parameter to `unmark_move_line_as_picked` to provide control over picking fragmentation. Previously, unmarking a move line would always trigger backorder logic, moving lines into a new picking (see https://github.com/OCA/wms/pull/543). While technically correct for some workflows, this was functionally confusing for UI users as items would "disappear" from their current view upon unmarking. --- shopfloor/actions/stock.py | 23 ++++++------ shopfloor/tests/test_actions_stock.py | 51 +++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 11 deletions(-) diff --git a/shopfloor/actions/stock.py b/shopfloor/actions/stock.py index 6c661d84ea5..2907dee3786 100644 --- a/shopfloor/actions/stock.py +++ b/shopfloor/actions/stock.py @@ -152,32 +152,33 @@ def mark_move_line_as_picked( lines.write(values_assigned) move_lines.picking_id.filtered(lambda p: p.user_id != user).user_id = user.id - def unmark_move_line_as_picked(self, move_lines): + def unmark_move_line_as_picked(self, move_lines, split=True): """Reverse the change from `mark_move_line_as_picked`.""" move_lines.write( { "shopfloor_user_id": False, "qty_done": 0, "result_package_id": False, + "lot_id": False, } ) pickings = move_lines.picking_id for picking in pickings: - lines_still_assigned = picking.move_line_ids.filtered( - lambda x: x.shopfloor_user_id - ) - if lines_still_assigned: - # Because there is other lines in the picking still assigned - # The picking has to be split - unmark_lines = picking.move_line_ids & move_lines - unmark_lines._extract_in_split_order(default={"user_id": False}) - else: - pickings.write( + still_assigned_users = picking.move_line_ids.shopfloor_user_id + if not still_assigned_users: + picking.write( { "user_id": False, "printed": False, } ) + else: + # Decide what to do if there are other lines in the picking still assigned + if split: + unmarked_lines = picking.move_line_ids & move_lines + unmarked_lines._extract_in_split_order(default={"user_id": False}) + else: + picking.user_id = still_assigned_users[0] def validate_moves(self, moves): """Validate moves in different ways depending on several criterias: diff --git a/shopfloor/tests/test_actions_stock.py b/shopfloor/tests/test_actions_stock.py index 00524458c2e..819dc135ad3 100644 --- a/shopfloor/tests/test_actions_stock.py +++ b/shopfloor/tests/test_actions_stock.py @@ -23,6 +23,17 @@ def setUpClass(cls): cls._fill_stock_for_moves(cls.move1) cls.picking.action_assign() + cls.other_user = ( + cls.env["res.users"] + .sudo() + .create( + { + "name": "Other user", + "login": "other_user", + } + ) + ) + @classmethod def setUpClassVars(cls): super().setUpClassVars() @@ -46,3 +57,43 @@ def test_unmark_move_line_as_picked(self): self.assertTrue(self.picking.move_line_ids.shopfloor_user_id) self.assertFalse(picking_not_assigned.move_line_ids.shopfloor_user_id) self.assertFalse(picking_not_assigned.user_id) + + def test_unmark_move_line_as_picked_nosplit_full_picking(self): + lines = self.picking.move_line_ids + self.stock.mark_move_line_as_picked(lines) + self.assertEqual(self.picking.user_id, self.env.user) + + self.stock.unmark_move_line_as_picked(lines, split=False) + self.assertFalse(lines.shopfloor_user_id) + self.assertEqual(lines.picking_id, self.picking) + self.assertFalse(self.picking.user_id) + + def test_unmark_move_line_as_picked_nosplit_partial_picking_same_user(self): + lines = self.picking.move_line_ids + self.stock.mark_move_line_as_picked(lines) + self.assertEqual(self.picking.user_id, self.env.user) + + line_unpicked = lines[0] + self.stock.unmark_move_line_as_picked(line_unpicked, split=False) + self.assertFalse(line_unpicked.shopfloor_user_id) + self.assertEqual(line_unpicked.picking_id, self.picking) + self.assertEqual(self.picking.user_id, self.env.user) + + def test_unmark_move_line_as_picked_nosplit_partial_picking_different_user(self): + lines = self.picking.move_line_ids + self.stock.mark_move_line_as_picked(lines[0], user=self.env.user, split=False) + self.stock.mark_move_line_as_picked(lines[1], user=self.other_user, split=False) + + user_line = lines.filtered(lambda line: line.shopfloor_user_id == self.env.user) + other_line = lines.filtered( + lambda line: line.shopfloor_user_id == self.other_user + ) + self.assertEqual(self.picking.user_id, self.other_user) + self.assertEqual(len(user_line), 1) + self.assertEqual(len(other_line), 1) + + self.stock.unmark_move_line_as_picked(other_line, split=False) + self.assertFalse(other_line.shopfloor_user_id) + self.assertEqual(user_line.shopfloor_user_id, self.env.user) + self.assertEqual(lines.picking_id, self.picking) + self.assertEqual(self.picking.user_id, self.env.user) From 9630f386d26b1c810c962a4b5f99272bbb0badec Mon Sep 17 00:00:00 2001 From: Nicolas Delbovier Date: Thu, 26 Mar 2026 11:59:47 +0100 Subject: [PATCH 2/5] [FIX] shopfloor_reception_mobile: link cancel button to cancel action in'set_quantity' state The button was not doing anything when clicked --- shopfloor_reception_mobile/static/src/scenario/reception.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shopfloor_reception_mobile/static/src/scenario/reception.js b/shopfloor_reception_mobile/static/src/scenario/reception.js index ce2117091a5..135091ccb0a 100644 --- a/shopfloor_reception_mobile/static/src/scenario/reception.js +++ b/shopfloor_reception_mobile/static/src/scenario/reception.js @@ -155,7 +155,7 @@ const Reception = { - + From b9405d7480ce829bddd88914cd4840690aa3058f Mon Sep 17 00:00:00 2001 From: Nicolas Delbovier Date: Thu, 9 Apr 2026 09:49:58 +0200 Subject: [PATCH 3/5] [IMP] shopfloor_reception: prevent backorder creation on line cancellation Currently, when a user cancels their progress on a move line within the Shopfloor reception app, the underlying `stock.move` is split into a new backorder picking. This causes the line to disappear from the current Shopfloor UI view. This behavior is confusing for operators who expect the line to remain visible so they can re-process it or correct their mistakes without switching to a different picking. --- shopfloor_reception/services/reception.py | 2 +- .../tests/test_set_quantity_action.py | 55 ++++++++++++++++++- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/shopfloor_reception/services/reception.py b/shopfloor_reception/services/reception.py index d44c368cdfd..ba9e5ec8f01 100644 --- a/shopfloor_reception/services/reception.py +++ b/shopfloor_reception/services/reception.py @@ -1324,7 +1324,7 @@ def set_quantity__cancel_action(self, picking_id, selected_line_id): if selected_line.exists(): if selected_line.reserved_uom_qty: stock = self._actions_for("stock") - stock.unmark_move_line_as_picked(selected_line) + stock.unmark_move_line_as_picked(selected_line, split=False) else: selected_line.unlink() return self._response_for_select_move(picking) diff --git a/shopfloor_reception/tests/test_set_quantity_action.py b/shopfloor_reception/tests/test_set_quantity_action.py index a7bf4b4b897..cceb476a373 100644 --- a/shopfloor_reception/tests/test_set_quantity_action.py +++ b/shopfloor_reception/tests/test_set_quantity_action.py @@ -80,10 +80,10 @@ def test_process_without_package(self): ) self.assertFalse(self.selected_move_line.result_package_id) - def test_cancel_action(self): - picking = self._create_picking() + def test_cancel_action_concurrent(self): + picking = self.picking move_product_a = picking.move_ids.filtered( - lambda l: l.product_id == self.product_a + lambda m: m.product_id == self.product_a ) # User 1 and 2 selects the same picking service_user_1 = self.service @@ -169,3 +169,52 @@ def test_cancel_action(self): ) # This line has been created by shopfloor, therefore, we unlinked it self.assertFalse(move_line_user_2.exists()) + + def test_cancel_action_no_backorder(self): + picking = self.picking + move_line = self.selected_move_line + move = self.selected_move_line.move_id + + self.service.dispatch( + "process_without_pack", + params={ + "picking_id": picking.id, + "selected_line_id": move_line.id, + "quantity": 2, + }, + ) + self.assertEqual(move.quantity_done, 2) + self.assertEqual(len(move.move_line_ids), 2) + + new_move_line = move.move_line_ids - move_line + self.assertEqual(new_move_line.reserved_qty, 8) + + # Make some modifications on the new move line + self.service.dispatch( + "set_quantity", + params={ + "picking_id": picking.id, + "selected_line_id": new_move_line.id, + "quantity": 3, + }, + ) + new_move_line.lot_id = self._create_lot() + self.assertTrue(new_move_line.lot_id) + self.assertTrue(new_move_line.qty_done) + + # cancel modifications on new move line + self.service.dispatch( + "set_quantity__cancel_action", + params={ + "picking_id": picking.id, + "selected_line_id": new_move_line.id, + }, + ) + self.assertTrue(new_move_line.exists()) + self.assertEqual(new_move_line.reserved_qty, 8) + self.assertFalse(new_move_line.lot_id) + self.assertEqual( + new_move_line.picking_id, + move_line.picking_id, + "Cancelling the move line should not move it into a backorder", + ) From 549d23ff85c9acbfcf469894ad6efd04e59c1205 Mon Sep 17 00:00:00 2001 From: Nicolas Delbovier Date: Fri, 17 Apr 2026 16:39:26 +0200 Subject: [PATCH 4/5] [FIX] shopfloor: reset printed status when unmarking --- shopfloor/actions/stock.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shopfloor/actions/stock.py b/shopfloor/actions/stock.py index 2907dee3786..2f8825c1c86 100644 --- a/shopfloor/actions/stock.py +++ b/shopfloor/actions/stock.py @@ -176,7 +176,9 @@ def unmark_move_line_as_picked(self, move_lines, split=True): # Decide what to do if there are other lines in the picking still assigned if split: unmarked_lines = picking.move_line_ids & move_lines - unmarked_lines._extract_in_split_order(default={"user_id": False}) + unmarked_lines._extract_in_split_order( + default={"user_id": False, "printed": False} + ) else: picking.user_id = still_assigned_users[0] From 4b790b87f3951aae1173c0f98901d4b7581b483e Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 17 Apr 2026 16:57:10 +0000 Subject: [PATCH 5/5] [BOT] post-merge updates --- README.md | 6 +++--- shopfloor/README.rst | 2 +- shopfloor/__manifest__.py | 2 +- shopfloor/static/description/index.html | 2 +- shopfloor_reception/README.rst | 2 +- shopfloor_reception/__manifest__.py | 2 +- shopfloor_reception/static/description/index.html | 2 +- shopfloor_reception_mobile/README.rst | 2 +- shopfloor_reception_mobile/__manifest__.py | 2 +- shopfloor_reception_mobile/static/description/index.html | 2 +- 10 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 17cbc0d29f3..485ebe3bf98 100644 --- a/README.md +++ b/README.md @@ -29,14 +29,14 @@ addon | version | maintainers | summary [sale_stock_release_channel_delivery_date](sale_stock_release_channel_delivery_date/) | 16.0.1.1.2 | jbaudoux | Compute expected date based on available release channels [sale_stock_release_channel_partner_by_date](sale_stock_release_channel_partner_by_date/) | 16.0.1.1.0 | sebalix | Release channels integration with Sales [sale_stock_release_channel_partner_by_date_delivery](sale_stock_release_channel_partner_by_date_delivery/) | 16.0.1.1.1 | sebalix | Filters channels on sales based on selected carrier. -[shopfloor](shopfloor/) | 16.0.2.16.2 | guewen simahawk sebalix | manage warehouse operations with barcode scanners +[shopfloor](shopfloor/) | 16.0.2.16.3 | guewen simahawk sebalix | manage warehouse operations with barcode scanners [shopfloor_base](shopfloor_base/) | 16.0.1.2.1 | guewen simahawk sebalix | Core module for creating mobile apps [shopfloor_batch_automatic_creation](shopfloor_batch_automatic_creation/) | 16.0.1.1.0 | guewen | Create batch transfers for Cluster Picking [shopfloor_mobile](shopfloor_mobile/) | 16.0.1.4.1 | simahawk | Mobile frontend for WMS Shopfloor app [shopfloor_mobile_base](shopfloor_mobile_base/) | 16.0.1.2.0 | simahawk | Mobile frontend for WMS Shopfloor app [shopfloor_mobile_base_auth_api_key](shopfloor_mobile_base_auth_api_key/) | 16.0.1.0.0 | | Provides authentication via API key to Shopfloor base mobile app -[shopfloor_reception](shopfloor_reception/) | 16.0.1.6.7 | mmequignon JuMiSanAr | Reception scenario for shopfloor -[shopfloor_reception_mobile](shopfloor_reception_mobile/) | 16.0.1.1.2 | JuMiSanAr | Scenario for receiving products +[shopfloor_reception](shopfloor_reception/) | 16.0.1.6.8 | mmequignon JuMiSanAr | Reception scenario for shopfloor +[shopfloor_reception_mobile](shopfloor_reception_mobile/) | 16.0.1.1.3 | JuMiSanAr | Scenario for receiving products [shopfloor_reception_refund_return](shopfloor_reception_refund_return/) | 16.0.1.0.0 | mmequignon | Mark created return as to refund [shopfloor_rest_log](shopfloor_rest_log/) | 16.0.1.0.0 | simahawk | Integrate rest_log into Shopfloor app [shopfloor_workstation](shopfloor_workstation/) | 16.0.1.0.0 | | Manage warehouse workstation with barcode scanners diff --git a/shopfloor/README.rst b/shopfloor/README.rst index 21d9f05f19d..8fba5a6686f 100644 --- a/shopfloor/README.rst +++ b/shopfloor/README.rst @@ -11,7 +11,7 @@ Shopfloor !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:cdba7e2cc373fe847373edb11404381cbae37c91bf1cc810d257ba2602aa4ae7 + !! source digest: sha256:22f8e5b0353c5637d5aea9d6f6ae884fd29d5d25e456451b5d76e0926b14b45c !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/shopfloor/__manifest__.py b/shopfloor/__manifest__.py index 2c56784d5f1..551ff614db9 100644 --- a/shopfloor/__manifest__.py +++ b/shopfloor/__manifest__.py @@ -6,7 +6,7 @@ { "name": "Shopfloor", "summary": "manage warehouse operations with barcode scanners", - "version": "16.0.2.16.2", + "version": "16.0.2.16.3", "development_status": "Beta", "category": "Inventory", "website": "https://github.com/OCA/wms", diff --git a/shopfloor/static/description/index.html b/shopfloor/static/description/index.html index 1986edf98c7..6ebeeaeef14 100644 --- a/shopfloor/static/description/index.html +++ b/shopfloor/static/description/index.html @@ -372,7 +372,7 @@

Shopfloor

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:cdba7e2cc373fe847373edb11404381cbae37c91bf1cc810d257ba2602aa4ae7 +!! source digest: sha256:22f8e5b0353c5637d5aea9d6f6ae884fd29d5d25e456451b5d76e0926b14b45c !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

Shopfloor is a barcode scanner application for internal warehouse operations.

diff --git a/shopfloor_reception/README.rst b/shopfloor_reception/README.rst index f425cbb9070..ccd148f33ad 100644 --- a/shopfloor_reception/README.rst +++ b/shopfloor_reception/README.rst @@ -11,7 +11,7 @@ Shopfloor Reception !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:f4ad59cbe1f5457c033cdeb6f33a81adc81c40e2ba2e87cf1b12c9ffbc9cf3e3 + !! source digest: sha256:4a5cae1dea64b85f2f987a26658f70b634462808055727f8e4475218e37783a8 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/shopfloor_reception/__manifest__.py b/shopfloor_reception/__manifest__.py index 9949f22c53d..fcd3c221c62 100644 --- a/shopfloor_reception/__manifest__.py +++ b/shopfloor_reception/__manifest__.py @@ -1,7 +1,7 @@ { "name": "Shopfloor Reception", "summary": "Reception scenario for shopfloor", - "version": "16.0.1.6.7", + "version": "16.0.1.6.8", "development_status": "Beta", "category": "Inventory", "website": "https://github.com/OCA/wms", diff --git a/shopfloor_reception/static/description/index.html b/shopfloor_reception/static/description/index.html index 5ff155b6605..030780eb904 100644 --- a/shopfloor_reception/static/description/index.html +++ b/shopfloor_reception/static/description/index.html @@ -372,7 +372,7 @@

Shopfloor Reception

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:f4ad59cbe1f5457c033cdeb6f33a81adc81c40e2ba2e87cf1b12c9ffbc9cf3e3 +!! source digest: sha256:4a5cae1dea64b85f2f987a26658f70b634462808055727f8e4475218e37783a8 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

Shopfloor implementation of the reception scenario. diff --git a/shopfloor_reception_mobile/README.rst b/shopfloor_reception_mobile/README.rst index 08644d8a2b7..c31048cc289 100644 --- a/shopfloor_reception_mobile/README.rst +++ b/shopfloor_reception_mobile/README.rst @@ -11,7 +11,7 @@ Shopfloor reception mobile !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:e6e5bdb9f20c104e11b2d9c6c67bdf6b418ca342dd36317955c679a68d278ca4 + !! source digest: sha256:379f4cd0674b8cebf272d622ff9e2103d9cc92fdf9bb6c0b3c685d57aaa10f49 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/shopfloor_reception_mobile/__manifest__.py b/shopfloor_reception_mobile/__manifest__.py index 88acd316c1d..3511b01fe03 100644 --- a/shopfloor_reception_mobile/__manifest__.py +++ b/shopfloor_reception_mobile/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Shopfloor reception mobile", "summary": "Scenario for receiving products", - "version": "16.0.1.1.2", + "version": "16.0.1.1.3", "development_status": "Beta", "depends": ["shopfloor_mobile_base", "shopfloor_mobile", "shopfloor_reception"], "author": "Camptocamp, Odoo Community Association (OCA)", diff --git a/shopfloor_reception_mobile/static/description/index.html b/shopfloor_reception_mobile/static/description/index.html index 97f13521bbf..d7c48940536 100644 --- a/shopfloor_reception_mobile/static/description/index.html +++ b/shopfloor_reception_mobile/static/description/index.html @@ -372,7 +372,7 @@

Shopfloor reception mobile

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:e6e5bdb9f20c104e11b2d9c6c67bdf6b418ca342dd36317955c679a68d278ca4 +!! source digest: sha256:379f4cd0674b8cebf272d622ff9e2103d9cc92fdf9bb6c0b3c685d57aaa10f49 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

Frontend for the reception scenario in shopfloor.