diff --git a/setup/shopfloor_reception_putinpack_restriction/odoo/addons/shopfloor_reception_putinpack_restriction b/setup/shopfloor_reception_putinpack_restriction/odoo/addons/shopfloor_reception_putinpack_restriction new file mode 120000 index 0000000000..b8560fb2f9 --- /dev/null +++ b/setup/shopfloor_reception_putinpack_restriction/odoo/addons/shopfloor_reception_putinpack_restriction @@ -0,0 +1 @@ +../../../../shopfloor_reception_putinpack_restriction \ No newline at end of file diff --git a/setup/shopfloor_reception_putinpack_restriction/setup.py b/setup/shopfloor_reception_putinpack_restriction/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/shopfloor_reception_putinpack_restriction/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/shopfloor_reception/services/reception.py b/shopfloor_reception/services/reception.py index c62c06354f..85044bb807 100644 --- a/shopfloor_reception/services/reception.py +++ b/shopfloor_reception/services/reception.py @@ -728,6 +728,20 @@ def _set_quantity__by_package(self, picking, selected_line, package): return self._response_for_select_move(picking) return self._response_for_set_destination(picking, selected_line) + def _set_quantity__by_new_package( + self, picking, selected_line, barcode: str, confirmation: str + ): + if confirmation != barcode: + return self._response_for_set_quantity( + picking, + selected_line, + message=self.msg_store.create_new_pack_ask_confirmation(barcode), + asking_confirmation=barcode, + ) + package = self.env["stock.quant.package"].create({"name": barcode}) + selected_line.result_package_id = package + return self._response_for_set_destination(picking, selected_line) + def _set_quantity__by_location(self, picking, selected_line, location): move_dest_location_ok, pick_type_dest_location_ok = self._check_location_ok( location, selected_line, picking @@ -1322,18 +1336,12 @@ def _set_quantity__by_barcode( handler = handlers_by_type.get(search_result.type) if handler: return handler(picking, selected_line, search_result.record) + # Nothing found, ask user if we should create a new pack for the scanned # barcode - if confirmation != barcode: - return self._response_for_set_quantity( - picking, - selected_line, - message=self.msg_store.create_new_pack_ask_confirmation(barcode), - asking_confirmation=barcode, - ) - package = self.env["stock.quant.package"].create({"name": barcode}) - selected_line.result_package_id = package - return self._response_for_set_destination(picking, selected_line) + return self._set_quantity__by_new_package( + picking, selected_line, barcode, confirmation + ) def _set_quantity__assign_quantity(self, picking, selected_line, quantity): # If this is a return line, we cannot assign more qty_done than what diff --git a/shopfloor_reception_mobile/static/src/scenario/reception.js b/shopfloor_reception_mobile/static/src/scenario/reception.js index 761e0aa658..619e098ec6 100644 --- a/shopfloor_reception_mobile/static/src/scenario/reception.js +++ b/shopfloor_reception_mobile/static/src/scenario/reception.js @@ -133,17 +133,17 @@ const Reception = { />
- + Existing pack - + New pack - + Process without pack @@ -212,6 +212,16 @@ const Reception = { `, computed: { + show_with_pack_actions: function () { + const put_in_pack_restriction = this.state.data.put_in_pack_restriction; + if (!put_in_pack_restriction) return true; + return put_in_pack_restriction === "with_package"; + }, + show_without_pack_actions: function () { + const put_in_pack_restriction = this.state.data.put_in_pack_restriction; + if (!put_in_pack_restriction) return true; + return put_in_pack_restriction === "no_package"; + }, visible_pickings: function () { return !_.isEmpty(this.filtered_pickings) ? this.filtered_pickings diff --git a/shopfloor_reception_putinpack_restriction/README.rst b/shopfloor_reception_putinpack_restriction/README.rst new file mode 100644 index 0000000000..6d348e0d32 --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/README.rst @@ -0,0 +1,105 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +========================================= +Shopfloor Reception Putinpack Restriction +========================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:71a2246186c7fd5828b67a511f45c844b51bd1124ca43605ea0531e073a117db + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fwms-lightgray.png?logo=github + :target: https://github.com/OCA/wms/tree/16.0/shopfloor_reception_putinpack_restriction + :alt: OCA/wms +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/wms-16-0/wms-16-0-shopfloor_reception_putinpack_restriction + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/wms&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows administrators to simplify the shopfloor user +interface by hiding package-related actions during the reception +scenario. + +**Table of contents** + +.. contents:: + :local: + +Use Cases / Context +=================== + +It is specifically designed for warehouses that want to control if goods +are put in a pack or cannot be put in a pack, here at the end of the +reception. This prevents warehouse operators from freely choosing by +hiding forbidden buttons. + +Configuration +============= + +You can configure on the picking type if you want to use packages or +not. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* ACSONE SA/NV +* BCIM + +Contributors +------------ + +- Nicolas Delbovier nicolas.delbovier@acsone.eu (https://www.acsone.eu/) +- Jacques-Etienne Baudoux (BCIM) je@bcim.be + +Other credits +------------- + +The development of this module has been financially supported by: + +- Alcyon Belux + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +This module is part of the `OCA/wms `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/shopfloor_reception_putinpack_restriction/__init__.py b/shopfloor_reception_putinpack_restriction/__init__.py new file mode 100644 index 0000000000..8f275c2963 --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/__init__.py @@ -0,0 +1,2 @@ +from . import services +from . import actions diff --git a/shopfloor_reception_putinpack_restriction/__manifest__.py b/shopfloor_reception_putinpack_restriction/__manifest__.py new file mode 100644 index 0000000000..1b46472ff0 --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2026 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Shopfloor Reception Putinpack Restriction", + "summary": """Restrict the use of packages in shopfloor reception""", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "author": "ACSONE SA/NV,BCIM,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/wms", + "depends": [ + "shopfloor_reception", + "stock_picking_putinpack_restriction", + ], + "data": [], + "demo": [], +} diff --git a/shopfloor_reception_putinpack_restriction/actions/__init__.py b/shopfloor_reception_putinpack_restriction/actions/__init__.py new file mode 100644 index 0000000000..51a1e70dbd --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/actions/__init__.py @@ -0,0 +1 @@ +from . import message diff --git a/shopfloor_reception_putinpack_restriction/actions/message.py b/shopfloor_reception_putinpack_restriction/actions/message.py new file mode 100644 index 0000000000..3782dd2cb7 --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/actions/message.py @@ -0,0 +1,31 @@ +# Copyright 2026 ACSONE SA/NV (https://www.acsone.eu) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging + +from odoo import _ + +from odoo.addons.component.core import Component + +_logger = logging.getLogger(__name__) + + +class MessageAction(Component): + _inherit = "shopfloor.message.action" + + def package_not_allowed_for_operation(self, picking): + return { + "message_type": "error", + "body": _( + "The operation '%(picking_type)s' does not allow the use of package.", + picking_type=picking.picking_type_id.display_name, + ), + } + + def package_required_for_operation(self, picking): + return { + "message_type": "error", + "body": _( + "The operation '%(picking_type)s' requires a package.", + picking_type=picking.picking_type_id.display_name, + ), + } diff --git a/shopfloor_reception_putinpack_restriction/readme/CONFIGURE.md b/shopfloor_reception_putinpack_restriction/readme/CONFIGURE.md new file mode 100644 index 0000000000..272b3308ad --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/readme/CONFIGURE.md @@ -0,0 +1 @@ +You can configure on the picking type if you want to use packages or not. \ No newline at end of file diff --git a/shopfloor_reception_putinpack_restriction/readme/CONTEXT.md b/shopfloor_reception_putinpack_restriction/readme/CONTEXT.md new file mode 100644 index 0000000000..551c0c79ea --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/readme/CONTEXT.md @@ -0,0 +1 @@ +It is specifically designed for warehouses that want to control if goods are put in a pack or cannot be put in a pack, here at the end of the reception. This prevents warehouse operators from freely choosing by hiding forbidden buttons. \ No newline at end of file diff --git a/shopfloor_reception_putinpack_restriction/readme/CONTRIBUTORS.md b/shopfloor_reception_putinpack_restriction/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..2e8f90a380 --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Nicolas Delbovier (https://www.acsone.eu/) +- Jacques-Etienne Baudoux (BCIM) \ No newline at end of file diff --git a/shopfloor_reception_putinpack_restriction/readme/CREDITS.md b/shopfloor_reception_putinpack_restriction/readme/CREDITS.md new file mode 100644 index 0000000000..501d13b72c --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/readme/CREDITS.md @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +- Alcyon Belux diff --git a/shopfloor_reception_putinpack_restriction/readme/DESCRIPTION.md b/shopfloor_reception_putinpack_restriction/readme/DESCRIPTION.md new file mode 100644 index 0000000000..9b17db50a4 --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module allows administrators to simplify the shopfloor user interface by hiding package-related actions during the reception scenario. \ No newline at end of file diff --git a/shopfloor_reception_putinpack_restriction/services/__init__.py b/shopfloor_reception_putinpack_restriction/services/__init__.py new file mode 100644 index 0000000000..aa19bba8ce --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/services/__init__.py @@ -0,0 +1 @@ +from . import reception diff --git a/shopfloor_reception_putinpack_restriction/services/reception.py b/shopfloor_reception_putinpack_restriction/services/reception.py new file mode 100644 index 0000000000..d3eef874ce --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/services/reception.py @@ -0,0 +1,125 @@ +# Copyright 2026 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + + +from datetime import timezone + +from odoo.addons.component.core import Component + +UTC = timezone.utc + + +class Reception(Component): + _inherit = "shopfloor.reception" + + def _check_picking_putinpack_restriction(self, picking, with_pack: bool): + if picking.put_in_pack_restriction == "no_package" and with_pack: + return self.msg_store.package_not_allowed_for_operation(picking) + if picking.put_in_pack_restriction == "with_package" and not with_pack: + return self.msg_store.package_required_for_operation(picking) + + def process_with_existing_pack(self, picking_id, selected_line_id, quantity): + picking = self.env["stock.picking"].browse(picking_id) + selected_line = self.env["stock.move.line"].browse(selected_line_id) + + if message := self._check_picking_putinpack_restriction( + picking, with_pack=True + ): + return self._response_for_set_quantity( + picking, selected_line, message=message + ) + res = super().process_with_existing_pack(picking_id, selected_line_id, quantity) + + return res + + def process_with_new_pack(self, picking_id, selected_line_id, quantity): + picking = self.env["stock.picking"].browse(picking_id) + selected_line = self.env["stock.move.line"].browse(selected_line_id) + + if message := self._check_picking_putinpack_restriction( + picking, with_pack=True + ): + return self._response_for_set_quantity( + picking, selected_line, message=message + ) + res = super().process_with_new_pack(picking_id, selected_line_id, quantity) + + return res + + def process_without_pack(self, picking_id, selected_line_id, quantity): + picking = self.env["stock.picking"].browse(picking_id) + selected_line = self.env["stock.move.line"].browse(selected_line_id) + + if message := self._check_picking_putinpack_restriction( + picking, with_pack=False + ): + return self._response_for_set_quantity( + picking, selected_line, message=message + ) + res = super().process_without_pack(picking_id, selected_line_id, quantity) + + return res + + def _set_quantity__by_package(self, picking, selected_line, package): + if message := self._check_picking_putinpack_restriction( + picking, with_pack=True + ): + return self._response_for_set_quantity( + picking, selected_line, message=message + ) + return super()._set_quantity__by_package(picking, selected_line, package) + + def _set_quantity__by_new_package( + self, picking, selected_line, barcode: str, confirmation: str + ): + if message := self._check_picking_putinpack_restriction( + picking, with_pack=True + ): + return self._response_for_set_quantity( + picking, selected_line, message=message + ) + return super()._set_quantity__by_new_package( + picking, selected_line, barcode, confirmation + ) + + def _response_for_set_quantity( + self, picking, line, message=None, asking_confirmation=None + ): + pip_restriction = picking.put_in_pack_restriction + + if pip_restriction == "no_package" and asking_confirmation: + return self._response_for_set_quantity( + picking, + line, + message=self.msg_store.package_not_allowed_for_operation(picking), + ) + + res = super()._response_for_set_quantity( + picking, line, message, asking_confirmation + ) + if pip_restriction: + res["data"]["set_quantity"]["put_in_pack_restriction"] = pip_restriction + return res + + +class ShopfloorReceptionValidatorResponse(Component): + _inherit = "shopfloor.reception.validator.response" + + @property + def _schema_set_quantity(self): + res = super()._schema_set_quantity + picking_type_model = self.env["stock.picking.type"] + selection_put_in_pack_restriction_values = [ + x[0] for x in picking_type_model._selection_put_in_pack_restriction() + ] + res.update( + { + "put_in_pack_restriction": { + "type": "string", + "nullable": True, + "required": False, + "allowed": selection_put_in_pack_restriction_values, + }, + } + ) + return res diff --git a/shopfloor_reception_putinpack_restriction/static/description/icon.png b/shopfloor_reception_putinpack_restriction/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/shopfloor_reception_putinpack_restriction/static/description/icon.png differ diff --git a/shopfloor_reception_putinpack_restriction/static/description/index.html b/shopfloor_reception_putinpack_restriction/static/description/index.html new file mode 100644 index 0000000000..6247522ce9 --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/static/description/index.html @@ -0,0 +1,455 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Shopfloor Reception Putinpack Restriction

+ +

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

+

This module allows administrators to simplify the shopfloor user +interface by hiding package-related actions during the reception +scenario.

+

Table of contents

+ +
+

Use Cases / Context

+

It is specifically designed for warehouses that want to control if goods +are put in a pack or cannot be put in a pack, here at the end of the +reception. This prevents warehouse operators from freely choosing by +hiding forbidden buttons.

+
+
+

Configuration

+

You can configure on the picking type if you want to use packages or +not.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • ACSONE SA/NV
  • +
  • BCIM
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The development of this module has been financially supported by:

+
    +
  • Alcyon Belux
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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.

+

This module is part of the OCA/wms project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/shopfloor_reception_putinpack_restriction/tests/__init__.py b/shopfloor_reception_putinpack_restriction/tests/__init__.py new file mode 100644 index 0000000000..cc2e7db27d --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/tests/__init__.py @@ -0,0 +1 @@ +from . import test_shopfloor_reception_putinpack_restriction diff --git a/shopfloor_reception_putinpack_restriction/tests/test_shopfloor_reception_putinpack_restriction.py b/shopfloor_reception_putinpack_restriction/tests/test_shopfloor_reception_putinpack_restriction.py new file mode 100644 index 0000000000..10aeae98cc --- /dev/null +++ b/shopfloor_reception_putinpack_restriction/tests/test_shopfloor_reception_putinpack_restriction.py @@ -0,0 +1,195 @@ +# Copyright 2026 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.shopfloor_reception.tests.common import CommonCase + + +class TestShopfloorReceptionPutinpackRestriction(CommonCase): + @classmethod + def setUpClassBaseData(cls): + res = super().setUpClassBaseData() + cls.picking = cls._create_picking() + cls.selected_move_line = cls.picking.move_line_ids.filtered( + lambda line: line.product_id == cls.product_a + ) + return res + + def test_process_with_existing_package_not_allowed(self): + self.picking.sudo().picking_type_id.put_in_pack_restriction = "no_package" + + self.env["stock.quant.package"].create({"name": "FOO"}) + response = self.service.dispatch( + "process_with_existing_pack", + params={ + "picking_id": self.picking.id, + "selected_line_id": self.selected_move_line.id, + "quantity": 2, + }, + ) + self.assert_response( + response, + next_state="set_quantity", + data={ + "picking": self.data.picking(self.picking), + "selected_move_line": self.data.move_lines(self.selected_move_line), + "confirmation_required": None, + "put_in_pack_restriction": "no_package", + }, + message=self.msg_store.package_not_allowed_for_operation(self.picking), + ) + + def test_process_with_new_package_not_allowed(self): + self.picking.sudo().picking_type_id.put_in_pack_restriction = "no_package" + + response = self.service.dispatch( + "process_with_new_pack", + params={ + "picking_id": self.picking.id, + "selected_line_id": self.selected_move_line.id, + "quantity": 2, + }, + ) + self.assert_response( + response, + next_state="set_quantity", + data={ + "picking": self.data.picking(self.picking), + "selected_move_line": self.data.move_lines(self.selected_move_line), + "confirmation_required": None, + "put_in_pack_restriction": "no_package", + }, + message=self.msg_store.package_not_allowed_for_operation(self.picking), + ) + + def test_process_without_package_not_allowed(self): + self.picking.sudo().picking_type_id.put_in_pack_restriction = "with_package" + + response = self.service.dispatch( + "process_without_pack", + params={ + "picking_id": self.picking.id, + "selected_line_id": self.selected_move_line.id, + "quantity": 2, + }, + ) + self.assert_response( + response, + next_state="set_quantity", + data={ + "picking": self.data.picking(self.picking), + "selected_move_line": self.data.move_lines(self.selected_move_line), + "confirmation_required": None, + "put_in_pack_restriction": "with_package", + }, + message=self.msg_store.package_required_for_operation(self.picking), + ) + + def test_set_quantity_not_allowed(self): + self.picking.sudo().picking_type_id.put_in_pack_restriction = "no_package" + + self.env["stock.quant.package"].create({"name": "FOO"}) + # Simulates a scan of a package + response = self.service.dispatch( + "set_quantity", + params={ + "picking_id": self.picking.id, + "selected_line_id": self.selected_move_line.id, + "quantity": 0, + "barcode": "FOO", + }, + ) + + self.assert_response( + response, + next_state="set_quantity", + data={ + "picking": self.data.picking(self.picking), + "selected_move_line": self.data.move_lines(self.selected_move_line), + "confirmation_required": None, + "put_in_pack_restriction": "no_package", + }, + message=self.msg_store.package_not_allowed_for_operation(self.picking), + ) + + def test_set_quantity_scan_not_broken(self): + """ + Ensure that the put in pack restriction does not prevent to update + the quantity by scanning a product/packaging/location + """ + self.picking.sudo().picking_type_id.put_in_pack_restriction = "no_package" + + # Set qty by product barcode + response = self.service.dispatch( + "set_quantity", + params={ + "picking_id": self.picking.id, + "selected_line_id": self.selected_move_line.id, + "quantity": 0, + "barcode": self.product_a.barcode, + }, + ) + self.assert_response( + response, + next_state="set_quantity", + data={ + "picking": self.data.picking(self.picking), + "selected_move_line": self.data.move_lines(self.selected_move_line), + "confirmation_required": None, + "put_in_pack_restriction": "no_package", + }, + ) + self.assertEqual(self.selected_move_line.qty_done, 1.0) + + # Set qty by packaging + self.env["product.packaging"].sudo().create( + { + "name": "Box", + "product_id": self.product_a.id, + "qty": 5.0, + "barcode": "PKG_TEST", + } + ) + response = self.service.dispatch( + "set_quantity", + params={ + "picking_id": self.picking.id, + "selected_line_id": self.selected_move_line.id, + "quantity": 0, + "barcode": "PKG_TEST", + }, + ) + self.assert_response( + response, + next_state="set_quantity", + data={ + "picking": self.data.picking(self.picking), + "selected_move_line": self.data.move_lines(self.selected_move_line), + "confirmation_required": None, + "put_in_pack_restriction": "no_package", + }, + ) + self.assertEqual(self.selected_move_line.qty_done, 5.0) + + # Set qty by location + loc_dest = ( + self.env["stock.location"] + .sudo() + .create( + { + "name": "Test Location", + "usage": "internal", + "location_id": self.picking.location_dest_id.id, + } + ) + ) + response = self.service.dispatch( + "set_quantity", + params={ + "picking_id": self.picking.id, + "selected_line_id": self.selected_move_line.id, + "quantity": 5, + "barcode": loc_dest.name, + }, + ) + self.assertIsNone(response.get("message")) + self.assertEqual(self.selected_move_line.location_dest_id, loc_dest)