From 20fb9f948064576b5fb0e7eee06c015b98d0ed07 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 25 Nov 2020 10:43:03 +0100 Subject: [PATCH 1/3] shopfloor: add service options via menu conf --- shopfloor/__manifest__.py | 3 ++- shopfloor/models/shopfloor_menu.py | 13 +++++++++++++ shopfloor/services/service.py | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/shopfloor/__manifest__.py b/shopfloor/__manifest__.py index 79026c348b..4f6dfa30d2 100644 --- a/shopfloor/__manifest__.py +++ b/shopfloor/__manifest__.py @@ -17,8 +17,9 @@ "depends": [ "stock", "stock_picking_batch", - "base_rest", "base_jsonify", + "base_rest", + "base_sparse_field", "auth_api_key", # OCA / stock-logistics-warehouse "stock_picking_completion_info", diff --git a/shopfloor/models/shopfloor_menu.py b/shopfloor/models/shopfloor_menu.py index c2a351282c..e398fe32cf 100644 --- a/shopfloor/models/shopfloor_menu.py +++ b/shopfloor/models/shopfloor_menu.py @@ -2,6 +2,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo import _, api, exceptions, fields, models +from odoo.addons.base_sparse_field.models.fields import Serialized + class ShopfloorMenu(models.Model): _name = "shopfloor.menu" @@ -33,6 +35,17 @@ class ShopfloorMenu(models.Model): ) scenario = fields.Selection(selection="_selection_scenario", required=True) + # TODO: `options` field allows to provide custom options for the scenario, + # (or for any other kind of service). + # Developers should probably have a way to register scenario and their options + # which will be computed in this field at the end. + # This would allow to get rid of hardcoded settings like + # `_scenario_allowing_create_moves` or `_scenario_allowing_unreserve_other_moves`. + # For now is not included in any view as it should be customizable by scenario. + # Maybe we can have a wizard accessible via a button on the menu tree view. + # There's no automation here. Developers are responsible for their usage + # and/or their exposure to the scenario api. + options = Serialized(default={}) move_create_is_possible = fields.Boolean(compute="_compute_move_create_is_possible") # only available for some scenarios, move_create_is_possible defines if the option diff --git a/shopfloor/services/service.py b/shopfloor/services/service.py index a5252f2239..603c55d90c 100644 --- a/shopfloor/services/service.py +++ b/shopfloor/services/service.py @@ -414,6 +414,24 @@ def _work_ctx_get_menu_id(self, rec_id): def _work_ctx_get_profile_id(self, rec_id): return "profile", self.env["shopfloor.profile"].browse(rec_id).exists() + _options = {} + + @property + def options(self): + """Compute options for current service. + + If the service has a menu, options coming from the menu are injected. + """ + if self._options: + return self._options + + options = {} + if self._requires_header_menu and getattr(self.work, "menu", None): + options = self.work.menu.options or {} + options.update(getattr(self.work, "options", {})) + self._options = options + return self._options + class BaseShopfloorProcess(AbstractComponent): """Base class for process rest service""" From 48ecd12cf13b953092ea048e0fcc1e434c386110 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 25 Nov 2020 10:44:24 +0100 Subject: [PATCH 2/3] shopfloor: checkout option to turn off 'no_package' --- shopfloor/services/checkout.py | 13 +++++ shopfloor/tests/test_checkout_no_package.py | 47 +++++++++++++------ shopfloor/tests/test_checkout_select_line.py | 12 +++++ .../test_checkout_select_package_base.py | 16 ++++--- 4 files changed, 67 insertions(+), 21 deletions(-) diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index 88ff30d3c9..fe45bd74a1 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -1,5 +1,8 @@ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from werkzeug.exceptions import BadRequest + from odoo import _ from odoo.addons.base_rest.components.service import to_int @@ -73,6 +76,9 @@ def _response_for_select_package(self, picking, lines, message=None): "selected_move_lines": self._data_for_move_lines(lines.sorted()), "picking": self.data.picking(picking), "packing_info": self._data_for_packing_info(picking), + "no_package_enabled": not self.options.get( + "checkout:disable_no_package" + ), }, message=message, ) @@ -758,6 +764,8 @@ def no_package(self, picking_id, selected_line_ids): Transitions: * select_line: goes back to selection of lines to work on next lines """ + if self.options.get("checkout:disable_no_package"): + raise BadRequest("`checkout.no_package` endpoint is not enabled") picking = self.env["stock.picking"].browse(picking_id) message = self._check_picking_status(picking) if message: @@ -1180,6 +1188,11 @@ def _states(self): "select_package": dict( self._schema_selected_lines, packing_info={"type": "string", "nullable": True}, + no_package_enabled={ + "type": "boolean", + "nullable": True, + "required": False, + }, ), "change_quantity": self._schema_selected_lines, "select_dest_package": self._schema_select_package, diff --git a/shopfloor/tests/test_checkout_no_package.py b/shopfloor/tests/test_checkout_no_package.py index 7242077825..e60a67ecfc 100644 --- a/shopfloor/tests/test_checkout_no_package.py +++ b/shopfloor/tests/test_checkout_no_package.py @@ -1,37 +1,44 @@ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import werkzeug + from .test_checkout_base import CheckoutCommonCase from .test_checkout_select_package_base import CheckoutSelectPackageMixin class CheckoutNoPackageCase(CheckoutCommonCase, CheckoutSelectPackageMixin): - def test_no_package_ok(self): - picking = self._create_picking( + @classmethod + def setUpClassBaseData(cls): + super().setUpClassBaseData() + cls.picking = picking = cls._create_picking( lines=[ - (self.product_a, 10), - (self.product_b, 10), - (self.product_c, 10), - (self.product_d, 10), + (cls.product_a, 10), + (cls.product_b, 10), + (cls.product_c, 10), + (cls.product_d, 10), ] ) - pack1_moves = picking.move_lines[:3] - pack2_moves = picking.move_lines[3:] + cls.pack1_moves = pack1_moves = picking.move_lines[:3] + cls.pack2_moves = pack2_moves = picking.move_lines[3:] # put in 2 packs, for this test, we'll work on pack1 - self._fill_stock_for_moves(pack1_moves) - self._fill_stock_for_moves(pack2_moves) + cls._fill_stock_for_moves(pack1_moves) + cls._fill_stock_for_moves(pack2_moves) picking.action_assign() - move_line1, move_line2, move_line3 = pack1_moves.move_line_ids + def test_no_package_ok(self): + move_line1, move_line2, move_line3 = self.pack1_moves.move_line_ids selected_lines = move_line1 + move_line2 # we'll put only the first 2 lines (product A and B) w/ no package move_line1.qty_done = move_line1.product_uom_qty move_line2.qty_done = move_line2.product_uom_qty move_line3.qty_done = 0 - response = self.service.dispatch( "no_package", - params={"picking_id": picking.id, "selected_line_ids": selected_lines.ids}, + params={ + "picking_id": self.picking.id, + "selected_line_ids": selected_lines.ids, + }, ) self.assertRecordValues( @@ -48,9 +55,21 @@ def test_no_package_ok(self): response, # go pack to the screen to select lines to put in packages next_state="select_line", - data={"picking": self._stock_picking_data(picking)}, + data={"picking": self._stock_picking_data(self.picking)}, message={ "message_type": "success", "body": "Product(s) processed as raw product(s)", }, ) + + def test_no_package_disabled(self): + self.service.work.options = {"checkout:disable_no_package": True} + with self.assertRaises(werkzeug.exceptions.BadRequest) as err: + self.service.dispatch( + "no_package", + params={ + "picking_id": self.picking.id, + "selected_line_ids": self.pack1_moves.move_line_ids.ids, + }, + ) + self.assertEqual(err.name, "`checkout.no_package` endpoint is not enabled") diff --git a/shopfloor/tests/test_checkout_select_line.py b/shopfloor/tests/test_checkout_select_line.py index 477fed1f3f..2218060026 100644 --- a/shopfloor/tests/test_checkout_select_line.py +++ b/shopfloor/tests/test_checkout_select_line.py @@ -31,6 +31,18 @@ def test_select_line_package_ok(self): ) self._assert_selected(response, selected_lines) + def test_select_line_no_package_disabled(self): + selected_lines = self.moves_pack.move_line_ids + self.service.work.options = {"checkout:disable_no_package": True} + response = self.service.dispatch( + "select_line", + params={ + "picking_id": self.picking.id, + "package_id": selected_lines.package_id.id, + }, + ) + self._assert_selected(response, selected_lines, no_package_enabled=False) + def test_select_line_move_line_package_ok(self): selected_lines = self.moves_pack.move_line_ids # When we select a single line but the line is part of a package, diff --git a/shopfloor/tests/test_checkout_select_package_base.py b/shopfloor/tests/test_checkout_select_package_base.py index 151beb76e2..f361b26fd8 100644 --- a/shopfloor/tests/test_checkout_select_package_base.py +++ b/shopfloor/tests/test_checkout_select_package_base.py @@ -4,7 +4,12 @@ class CheckoutSelectPackageMixin: def _assert_selected_response( - self, response, selected_lines, message=None, packing_info=False + self, + response, + selected_lines, + message=None, + packing_info=False, + no_package_enabled=True, ): picking = selected_lines.mapped("picking_id") self.assert_response( @@ -16,6 +21,7 @@ def _assert_selected_response( ], "picking": self._picking_summary_data(picking), "packing_info": picking.shopfloor_packing_info if packing_info else "", + "no_package_enabled": no_package_enabled, }, message=message, ) @@ -41,9 +47,7 @@ def _assert_selected_qties( response, selected_lines, message=message, packing_info=packing_info ) - def _assert_selected( - self, response, selected_lines, message=None, packing_info=False - ): + def _assert_selected(self, response, selected_lines, message=None, **kw): picking = selected_lines.mapped("picking_id") unselected_lines = picking.move_line_ids - selected_lines for line in selected_lines: @@ -54,6 +58,4 @@ def _assert_selected( ) for line in unselected_lines: self.assertEqual(line.qty_done, 0) - self._assert_selected_response( - response, selected_lines, message=message, packing_info=packing_info - ) + self._assert_selected_response(response, selected_lines, message=message, **kw) From a2e9d21fbc4a855cfdeadfca47c9cb6067563049 Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 25 Nov 2020 10:45:02 +0100 Subject: [PATCH 3/3] shopfloor_mobile: checkout disable no_package via options --- shopfloor_mobile/static/wms/src/scenario/checkout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shopfloor_mobile/static/wms/src/scenario/checkout.js b/shopfloor_mobile/static/wms/src/scenario/checkout.js index 3910aae358..306c0c61fc 100644 --- a/shopfloor_mobile/static/wms/src/scenario/checkout.js +++ b/shopfloor_mobile/static/wms/src/scenario/checkout.js @@ -90,7 +90,7 @@ const Checkout = { >New pack - +