From 99fecaaecfa0a6ff60d407ec4bc8940ccbadeb13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Thu, 26 Jan 2023 13:40:16 +0100 Subject: [PATCH 01/11] shopfloor, checkout: use lot_not_found_in_picking msg --- shopfloor/actions/message.py | 12 ++++++++++++ shopfloor/services/checkout.py | 5 +---- shopfloor/tests/test_checkout_scan_line.py | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/shopfloor/actions/message.py b/shopfloor/actions/message.py index d7b36db0da..c07b30d00b 100644 --- a/shopfloor/actions/message.py +++ b/shopfloor/actions/message.py @@ -520,6 +520,18 @@ def lot_multiple_packages_scan_package(self): "body": _("This lot is part of multiple packages, please scan a package."), } + def lot_not_found(self): + return { + "message_type": "error", + "body": _("This lot does not exist anymore."), + } + + def lot_not_found_in_picking(self): + return { + "message_type": "error", + "body": _("Lot is not in the current transfer."), + } + def lot_not_found_in_pickings(self): return { "message_type": "warning", diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index 0b2ee075df..4f79336c92 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -561,10 +561,7 @@ def _select_lines_from_lot( if not lines: return self._response_for_select_line( picking, - message={ - "message_type": "error", - "body": _("Lot is not in the current transfer."), - }, + message=self.msg_store.lot_not_found_in_picking(), ) # When lots are as units outside of packages, we can select them for diff --git a/shopfloor/tests/test_checkout_scan_line.py b/shopfloor/tests/test_checkout_scan_line.py index b79efcbf15..4ebe5b2f94 100644 --- a/shopfloor/tests/test_checkout_scan_line.py +++ b/shopfloor/tests/test_checkout_scan_line.py @@ -257,7 +257,7 @@ def test_scan_line_error_lot_not_in_picking(self): self._test_scan_line_error( picking, lot.name, - {"message_type": "error", "body": "Lot is not in the current transfer."}, + self.msg_store.lot_not_found_in_picking(), ) def test_scan_line_error_lot_in_two_packages(self): From 36302e933b9f5c96403213fe1532c8a526c8964c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 30 Jan 2023 11:23:03 +0100 Subject: [PATCH 02/11] shopfloor, checkout: allow change lot on line select When it comes to select a line, scanning the wrong lot will invite the user to scan it again to change it on the relevant line only if there is one sharing the same product. --- shopfloor/actions/change_package_lot.py | 11 +++ shopfloor/actions/message.py | 21 +++++ shopfloor/services/checkout.py | 89 +++++++++++++++++++--- shopfloor/tests/test_checkout_scan_line.py | 45 ++++++++++- 4 files changed, 151 insertions(+), 15 deletions(-) diff --git a/shopfloor/actions/change_package_lot.py b/shopfloor/actions/change_package_lot.py index 18a89aae33..4a2ed00c50 100644 --- a/shopfloor/actions/change_package_lot.py +++ b/shopfloor/actions/change_package_lot.py @@ -215,3 +215,14 @@ def change_package(self, move_line, package, response_ok_func, response_error_fu else: message = self.msg_store.units_replaced_by_package(package) return response_ok_func(move_line, message=message) + + def filter_lines_allowed_to_change_lot(self, move_lines, lot): + """Filter move lines allowed to change their lot. + + We cannot change a lot on a move having ancestors. That would mean we + already picked up the wrong lot on the previous move(s) and Odoo already + restricts the reservation based on the previous move(s). + """ + return move_lines.filtered( + lambda l: (l.product_id == lot.product_id and not l.move_id.move_orig_ids) + ) diff --git a/shopfloor/actions/message.py b/shopfloor/actions/message.py index c07b30d00b..9fd6810be1 100644 --- a/shopfloor/actions/message.py +++ b/shopfloor/actions/message.py @@ -106,6 +106,15 @@ def package_different_change(self): ), } + def lot_different_change(self): + return { + "message_type": "warning", + "body": _( + "You scanned a different lot with the same product, " + "do you want to change lot? Scan it again to confirm." + ), + } + def package_not_available_in_picking(self, package, picking): return { "message_type": "warning", @@ -903,3 +912,15 @@ def package_transfer_not_allowed_scan_location(self): "please scan a location instead." ), } + + def lot_changed(self): + return { + "message_type": "info", + "body": _("Lot changed"), + } + + def lot_change_no_line_found(self): + return { + "message_type": "error", + "body": _("Unable to find a line with the same product but different lot."), + } diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index 4f79336c92..6511b5e677 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -41,24 +41,29 @@ class Checkout(Component): _description = __doc__ def _response_for_select_line( - self, picking, message=None, need_confirm_pack_all=False + self, picking, message=None, need_confirm_pack_all=False, need_confirm_lot=False ): if all(line.shopfloor_checkout_done for line in picking.move_line_ids): return self._response_for_summary(picking, message=message) return self._response( next_state="select_line", data=self._data_for_select_line( - picking, need_confirm_pack_all=need_confirm_pack_all + picking, + need_confirm_pack_all=need_confirm_pack_all, + need_confirm_lot=need_confirm_lot, ), message=message, ) - def _data_for_select_line(self, picking, need_confirm_pack_all=False): + def _data_for_select_line( + self, picking, need_confirm_pack_all=False, need_confirm_lot=False + ): return { "picking": self._data_for_stock_picking(picking), "group_lines_by_location": True, "show_oneline_package_content": self.work.menu.show_oneline_package_content, "need_confirm_pack_all": need_confirm_pack_all, + "need_confirm_lot": need_confirm_lot, } def _response_for_summary(self, picking, need_confirm=False, message=None): @@ -422,7 +427,7 @@ def _deselect_lines(self, lines): {"qty_done": 0, "shopfloor_user_id": False} ) - def scan_line(self, picking_id, barcode, confirm_pack_all=False): + def scan_line(self, picking_id, barcode, confirm_pack_all=False, confirm_lot=False): """Scan move lines of the stock picking It allows to select move lines of the stock picking for the next @@ -453,7 +458,7 @@ def scan_line(self, picking_id, barcode, confirm_pack_all=False): search_result = self._scan_line_find(picking, barcode) result_handler = getattr(self, "_select_lines_from_" + search_result.type) - kw = {"confirm_pack_all": confirm_pack_all} + kw = {"confirm_pack_all": confirm_pack_all, "confirm_lot": confirm_lot} return result_handler(picking, selection_lines, search_result.record, **kw) def _scan_line_find(self, picking, barcode, search_types=None): @@ -505,6 +510,7 @@ def _select_lines_from_package( def _select_lines_from_product( self, picking, selection_lines, product, prefill_qty=1, check_lot=True, **kw ): + # TODO: should we propagate 'kw.get("message")' content on each return? if product.tracking in ("lot", "serial") and check_lot: return self._response_for_select_line( picking, message=self.msg_store.scan_lot_on_product_tracked_by_lot() @@ -534,7 +540,8 @@ def _select_lines_from_product( # Select all the lines of the package when we scan a product in a # package and we have only one. return self._select_lines_from_package( - picking, selection_lines, packages, prefill_qty=prefill_qty + picking, selection_lines, packages, prefill_qty=prefill_qty, + message=kw.get("message"), ) else: # There is no package on selected lines, so also select all other lines @@ -547,7 +554,9 @@ def _select_lines_from_product( lines = self._select_lines( lines, prefill_qty=prefill_qty, related_lines=related_lines ) - return self._response_for_select_package(picking, lines) + return self._response_for_select_package( + picking, lines, message=kw.get("message") + ) def _select_lines_from_packaging(self, picking, selection_lines, packaging, **kw): return self._select_lines_from_product( @@ -557,12 +566,51 @@ def _select_lines_from_packaging(self, picking, selection_lines, packaging, **kw def _select_lines_from_lot( self, picking, selection_lines, lot, prefill_qty=1, **kw ): + message = None lines = selection_lines.filtered(lambda l: l.lot_id == lot) if not lines: - return self._response_for_select_line( - picking, - message=self.msg_store.lot_not_found_in_picking(), + change_package_lot = self._actions_for("change.package.lot") + if not kw.get("confirm_lot"): + lines_same_product = ( + change_package_lot.filter_lines_allowed_to_change_lot( + selection_lines, lot + ) + ) + # However if one line is matching the same product, invite the user + # to change the lot by scanning again the lot + if len(lines_same_product) == 1: + return self._response_for_select_line( + picking, + message=self.msg_store.lot_different_change(), + need_confirm_lot=True, + ) + return self._response_for_select_line( + picking, + message=self.msg_store.lot_not_found_in_picking(), + ) + # Change lot confirmed + line = fields.first( + selection_lines.filtered( + lambda l: l.product_id == lot.product_id and l.lot_id != lot + ) + ) + if not line: + return self._response_for_select_line( + picking, + message=self.msg_store.lot_change_no_line_found(), + ) + response_ok_func = self._change_lot_response_handler_ok + response_error_func = self._change_lot_response_handler_error + message = change_package_lot.change_lot( + line, lot, response_ok_func, response_error_func ) + if message["message_type"] == "error": + return self._response_for_select_line(picking, message=message) + else: + lines = line + # Some lines have been recreated, refresh the recordset + # to avoid CacheMiss error + selection_lines = self._lines_to_pack(picking) # When lots are as units outside of packages, we can select them for # packing, but if they are in a package, we want the user to scan the packages. @@ -573,6 +621,8 @@ def _select_lines_from_lot( # package, but also if we have one lot as a package and the same lot as # a unit in another line. In both cases, we want the user to scan the # package. + # NOTE: change_pack_lot already checked this, so if we changed the lot + # we are already safe. if packages and len({line.package_id for line in lines}) > 1: return self._response_for_select_line( picking, message=self.msg_store.lot_multiple_packages_scan_package() @@ -581,7 +631,11 @@ def _select_lines_from_lot( # Select all the lines of the package when we scan a lot in a # package and we have only one. return self._select_lines_from_package( - picking, selection_lines, packages, prefill_qty=prefill_qty, **kw + picking, + selection_lines, + packages, + prefill_qty=prefill_qty, + message=message, ) first_allowed_line = fields.first(lines) @@ -591,8 +645,15 @@ def _select_lines_from_lot( first_allowed_line.product_id, prefill_qty=prefill_qty, check_lot=False, + message=message, ) + def _change_lot_response_handler_ok(self, move_line, message=None): + return message + + def _change_lot_response_handler_error(self, move_line, message=None): + return message + def _select_lines_from_serial(self, picking, selection_lines, lot, **kw): # Search for serial number is actually the same as searching for lot (as of v14...) return self._select_lines_from_lot(picking, selection_lines, lot, **kw) @@ -1466,6 +1527,11 @@ def scan_line(self): "nullable": True, "required": False, }, + "confirm_lot": { + "type": "boolean", + "nullable": True, + "required": False, + }, } def select_line(self): @@ -1688,6 +1754,7 @@ def _schema_stock_picking_details(self): group_lines_by_location={"type": "boolean"}, show_oneline_package_content={"type": "boolean"}, need_confirm_pack_all={"type": "boolean"}, + need_confirm_lot={"type": "boolean"}, ) @property diff --git a/shopfloor/tests/test_checkout_scan_line.py b/shopfloor/tests/test_checkout_scan_line.py index 4ebe5b2f94..5d670e880d 100644 --- a/shopfloor/tests/test_checkout_scan_line.py +++ b/shopfloor/tests/test_checkout_scan_line.py @@ -1,5 +1,7 @@ # Copyright 2020 Camptocamp SA (http://www.camptocamp.com) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import _ + from .test_checkout_scan_line_base import CheckoutScanLineCaseBase @@ -132,7 +134,7 @@ def test_scan_line_product_in_one_package_all_package_lines_ok(self): # more than one package, it would be an error. self._test_scan_line_ok(self.product_a.barcode, picking.move_line_ids) - def _test_scan_line_error(self, picking, barcode, message): + def _test_scan_line_error(self, picking, barcode, message, need_confirm_lot=False): """Test errors for /scan_line :param picking: the picking we are currently working with (selected) @@ -145,7 +147,9 @@ def _test_scan_line_error(self, picking, barcode, message): self.assert_response( response, next_state="select_line", - data=self._data_for_select_line(picking), + data=dict( + self._data_for_select_line(picking), need_confirm_lot=need_confirm_lot + ), message=message, ) @@ -247,17 +251,50 @@ def test_scan_line_error_product_not_in_picking(self): }, ) - def test_scan_line_error_lot_not_in_picking(self): + def test_scan_line_error_lot_different_change_success(self): + """Scan the wrong lot while a line with the same product exists.""" picking = self._create_picking(lines=[(self.product_a, 10)]) self._fill_stock_for_moves(picking.move_lines, in_lot=True) picking.action_assign() + previous_lot = picking.move_line_ids.lot_id + # Create a lot that is not registered in the location we are working on + # so a draft inventory for control is generated automatically when the + # lot is changed. lot = self.env["stock.production.lot"].create( {"product_id": self.product_a.id, "company_id": self.env.company.id} ) self._test_scan_line_error( picking, lot.name, - self.msg_store.lot_not_found_in_picking(), + self.msg_store.lot_different_change(), + need_confirm_lot=True, + ) + # Second scan to confirm the change of lot + response = self.service.dispatch( + "scan_line", + params={ + "picking_id": picking.id, + "barcode": lot.name, + "confirm_lot": True, + }, + ) + message = self.msg_store.lot_replaced_by_lot(previous_lot, lot) + inventory_message = _("A draft inventory has been created for control.") + message["body"] = f"{message['body']} {inventory_message}" + self.assert_response( + response, + next_state="select_package", + data={ + "selected_move_lines": [ + self._move_line_data(ml) for ml in picking.move_line_ids + ], + "picking": self.service.data.picking(picking), + "packing_info": self.service._data_for_packing_info(picking), + "no_package_enabled": not self.service.options.get( + "checkout__disable_no_package" + ), + }, + message=message, ) def test_scan_line_error_lot_in_two_packages(self): From 5b23b044a27128b0da29519ff1beaaad279893af Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Tue, 31 Jan 2023 15:32:37 +0100 Subject: [PATCH 03/11] shopfloor checkout: fix change lot --- shopfloor/actions/message.py | 3 ++- shopfloor/services/checkout.py | 7 ++++--- shopfloor/tests/test_checkout_base.py | 1 + .../static/wms/src/scenario/checkout_states.js | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/shopfloor/actions/message.py b/shopfloor/actions/message.py index 9fd6810be1..926dd31e4f 100644 --- a/shopfloor/actions/message.py +++ b/shopfloor/actions/message.py @@ -111,7 +111,8 @@ def lot_different_change(self): "message_type": "warning", "body": _( "You scanned a different lot with the same product, " - "do you want to change lot? Scan it again to confirm." + "do you want to change lot? Scan it again to confirm. " + "The first line matching this product will be updated. " ), } diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index 6511b5e677..16e1c5429f 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -576,14 +576,15 @@ def _select_lines_from_lot( selection_lines, lot ) ) - # However if one line is matching the same product, invite the user - # to change the lot by scanning again the lot - if len(lines_same_product) == 1: + # If there's at least one product matching we are good to go. + # In any case, only the 1st line matching will be affected. + if lines_same_product: return self._response_for_select_line( picking, message=self.msg_store.lot_different_change(), need_confirm_lot=True, ) + # TODO: add a msg saying the lot has been changed return self._response_for_select_line( picking, message=self.msg_store.lot_not_found_in_picking(), diff --git a/shopfloor/tests/test_checkout_base.py b/shopfloor/tests/test_checkout_base.py index 120e1e71af..0d8b120282 100644 --- a/shopfloor/tests/test_checkout_base.py +++ b/shopfloor/tests/test_checkout_base.py @@ -54,6 +54,7 @@ def _data_for_select_line(self, picking, **kw): "group_lines_by_location": True, "show_oneline_package_content": False, "need_confirm_pack_all": False, + "need_confirm_lot": False, } data.update(kw) return data diff --git a/shopfloor_mobile/static/wms/src/scenario/checkout_states.js b/shopfloor_mobile/static/wms/src/scenario/checkout_states.js index a69b9e5b07..f0dc3d9f10 100644 --- a/shopfloor_mobile/static/wms/src/scenario/checkout_states.js +++ b/shopfloor_mobile/static/wms/src/scenario/checkout_states.js @@ -64,6 +64,7 @@ export const checkout_states = function ($instance) { picking_id: $instance.state.data.picking.id, barcode: scanned.text, confirm_pack_all: $instance.state.data.need_confirm_pack_all, + confirm_lot: $instance.state.data.need_confirm_lot, }) ); }, From 9b276b025c54f3a79eb416f5148c460b941af33b Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Wed, 8 Feb 2023 11:42:23 +0100 Subject: [PATCH 04/11] shopfloor checkout: add hook to select lines by lot --- shopfloor/services/checkout.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index 16e1c5429f..663c4e0fe8 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -567,7 +567,7 @@ def _select_lines_from_lot( self, picking, selection_lines, lot, prefill_qty=1, **kw ): message = None - lines = selection_lines.filtered(lambda l: l.lot_id == lot) + lines = self._picking_lines_by_lot(picking, selection_lines, lot) if not lines: change_package_lot = self._actions_for("change.package.lot") if not kw.get("confirm_lot"): @@ -649,6 +649,10 @@ def _select_lines_from_lot( message=message, ) + def _picking_lines_by_lot(self, picking, selection_lines, lot): + """Control filtering of selected lines by given lot.""" + return selection_lines.filtered(lambda l: l.lot_id == lot) + def _change_lot_response_handler_ok(self, move_line, message=None): return message From e68c7bfa32633493e67895cc27974fa4f1d01acb Mon Sep 17 00:00:00 2001 From: Simone Orsi Date: Mon, 13 Feb 2023 14:20:36 +0100 Subject: [PATCH 05/11] shopfloor: add hook before assign after change lot --- shopfloor/actions/change_package_lot.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/shopfloor/actions/change_package_lot.py b/shopfloor/actions/change_package_lot.py index 4a2ed00c50..bb56cbafb2 100644 --- a/shopfloor/actions/change_package_lot.py +++ b/shopfloor/actions/change_package_lot.py @@ -161,6 +161,9 @@ def is_lesser(value, other, rounding): # lines move_line.move_id._action_assign() + self._change_pack_lot_change_lot__before_assign( + previous_lot, lot, to_assign_moves + ) # Find other available goods for the lines which were using the # lot before... to_assign_moves._action_assign() @@ -170,6 +173,11 @@ def is_lesser(value, other, rounding): message["body"] = "{} {}".format(message["body"], " ".join(message_parts)) return response_ok_func(move_line, message=message) + def _change_pack_lot_change_lot__before_assign( + self, previous_lot, lot, to_assign_moves + ): + pass + def _package_content_replacement_allowed(self, package, move_line): # we can't replace by a package which doesn't contain the product... return move_line.product_id in package.quant_ids.product_id From 6d33b3227a6e2274758aefa319fe98a924ab5cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Tue, 21 Feb 2023 15:01:02 +0100 Subject: [PATCH 06/11] shopfloor, checkout: check lot on change confirmation --- shopfloor/actions/message.py | 9 +++++++++ shopfloor/services/checkout.py | 18 ++++++++++++------ shopfloor/tests/test_checkout_base.py | 2 +- shopfloor/tests/test_checkout_scan_line.py | 6 +++--- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/shopfloor/actions/message.py b/shopfloor/actions/message.py index 926dd31e4f..85786c7901 100644 --- a/shopfloor/actions/message.py +++ b/shopfloor/actions/message.py @@ -920,6 +920,15 @@ def lot_changed(self): "body": _("Lot changed"), } + def lot_change_wrong_lot(self, lot_name): + return { + "message_type": "error", + "body": _("Scanned lot differs from the previous scan: %(lot)s.") + % { + "lot": lot_name, + }, + } + def lot_change_no_line_found(self): return { "message_type": "error", diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index 663c4e0fe8..9ff4d296d1 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -41,7 +41,7 @@ class Checkout(Component): _description = __doc__ def _response_for_select_line( - self, picking, message=None, need_confirm_pack_all=False, need_confirm_lot=False + self, picking, message=None, need_confirm_pack_all=False, need_confirm_lot=None ): if all(line.shopfloor_checkout_done for line in picking.move_line_ids): return self._response_for_summary(picking, message=message) @@ -56,7 +56,7 @@ def _response_for_select_line( ) def _data_for_select_line( - self, picking, need_confirm_pack_all=False, need_confirm_lot=False + self, picking, need_confirm_pack_all=False, need_confirm_lot=None ): return { "picking": self._data_for_stock_picking(picking), @@ -427,7 +427,7 @@ def _deselect_lines(self, lines): {"qty_done": 0, "shopfloor_user_id": False} ) - def scan_line(self, picking_id, barcode, confirm_pack_all=False, confirm_lot=False): + def scan_line(self, picking_id, barcode, confirm_pack_all=False, confirm_lot=None): """Scan move lines of the stock picking It allows to select move lines of the stock picking for the next @@ -582,13 +582,19 @@ def _select_lines_from_lot( return self._response_for_select_line( picking, message=self.msg_store.lot_different_change(), - need_confirm_lot=True, + need_confirm_lot=lot.name, ) # TODO: add a msg saying the lot has been changed return self._response_for_select_line( picking, message=self.msg_store.lot_not_found_in_picking(), ) + # Validate the scanned lot against the previous one + if lot.name != kw["confirm_lot"]: + return self._response_for_select_line( + picking, + message=self.msg_store.lot_change_wrong_lot(kw["confirm_lot"]), + ) # Change lot confirmed line = fields.first( selection_lines.filtered( @@ -1533,7 +1539,7 @@ def scan_line(self): "required": False, }, "confirm_lot": { - "type": "boolean", + "type": "string", "nullable": True, "required": False, }, @@ -1759,7 +1765,7 @@ def _schema_stock_picking_details(self): group_lines_by_location={"type": "boolean"}, show_oneline_package_content={"type": "boolean"}, need_confirm_pack_all={"type": "boolean"}, - need_confirm_lot={"type": "boolean"}, + need_confirm_lot={"type": "string", "nullable": True}, ) @property diff --git a/shopfloor/tests/test_checkout_base.py b/shopfloor/tests/test_checkout_base.py index 0d8b120282..c5134ed85b 100644 --- a/shopfloor/tests/test_checkout_base.py +++ b/shopfloor/tests/test_checkout_base.py @@ -54,7 +54,7 @@ def _data_for_select_line(self, picking, **kw): "group_lines_by_location": True, "show_oneline_package_content": False, "need_confirm_pack_all": False, - "need_confirm_lot": False, + "need_confirm_lot": None, } data.update(kw) return data diff --git a/shopfloor/tests/test_checkout_scan_line.py b/shopfloor/tests/test_checkout_scan_line.py index 5d670e880d..2bb0c876cb 100644 --- a/shopfloor/tests/test_checkout_scan_line.py +++ b/shopfloor/tests/test_checkout_scan_line.py @@ -134,7 +134,7 @@ def test_scan_line_product_in_one_package_all_package_lines_ok(self): # more than one package, it would be an error. self._test_scan_line_ok(self.product_a.barcode, picking.move_line_ids) - def _test_scan_line_error(self, picking, barcode, message, need_confirm_lot=False): + def _test_scan_line_error(self, picking, barcode, message, need_confirm_lot=None): """Test errors for /scan_line :param picking: the picking we are currently working with (selected) @@ -267,7 +267,7 @@ def test_scan_line_error_lot_different_change_success(self): picking, lot.name, self.msg_store.lot_different_change(), - need_confirm_lot=True, + need_confirm_lot=lot.name, ) # Second scan to confirm the change of lot response = self.service.dispatch( @@ -275,7 +275,7 @@ def test_scan_line_error_lot_different_change_success(self): params={ "picking_id": picking.id, "barcode": lot.name, - "confirm_lot": True, + "confirm_lot": lot.name, }, ) message = self.msg_store.lot_replaced_by_lot(previous_lot, lot) From 015fbc806c1b316251e89056580641b148a6a05b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 11 Sep 2023 13:08:16 +0200 Subject: [PATCH 07/11] fixup! shopfloor, checkout: check lot on change confirmation --- shopfloor/services/checkout.py | 11 ++++++----- shopfloor/tests/test_checkout_scan_line.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index 9ff4d296d1..a4de64c1a9 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -582,7 +582,7 @@ def _select_lines_from_lot( return self._response_for_select_line( picking, message=self.msg_store.lot_different_change(), - need_confirm_lot=lot.name, + need_confirm_lot=lot.id, ) # TODO: add a msg saying the lot has been changed return self._response_for_select_line( @@ -590,10 +590,11 @@ def _select_lines_from_lot( message=self.msg_store.lot_not_found_in_picking(), ) # Validate the scanned lot against the previous one - if lot.name != kw["confirm_lot"]: + if lot.id != kw["confirm_lot"]: + expected_lot = lot.browse(kw["confirm_lot"]).exists() return self._response_for_select_line( picking, - message=self.msg_store.lot_change_wrong_lot(kw["confirm_lot"]), + message=self.msg_store.lot_change_wrong_lot(expected_lot.name), ) # Change lot confirmed line = fields.first( @@ -1539,7 +1540,7 @@ def scan_line(self): "required": False, }, "confirm_lot": { - "type": "string", + "type": "integer", "nullable": True, "required": False, }, @@ -1765,7 +1766,7 @@ def _schema_stock_picking_details(self): group_lines_by_location={"type": "boolean"}, show_oneline_package_content={"type": "boolean"}, need_confirm_pack_all={"type": "boolean"}, - need_confirm_lot={"type": "string", "nullable": True}, + need_confirm_lot={"type": "integer", "nullable": True}, ) @property diff --git a/shopfloor/tests/test_checkout_scan_line.py b/shopfloor/tests/test_checkout_scan_line.py index 2bb0c876cb..3068939a5b 100644 --- a/shopfloor/tests/test_checkout_scan_line.py +++ b/shopfloor/tests/test_checkout_scan_line.py @@ -275,7 +275,7 @@ def test_scan_line_error_lot_different_change_success(self): params={ "picking_id": picking.id, "barcode": lot.name, - "confirm_lot": lot.name, + "confirm_lot": lot.id, }, ) message = self.msg_store.lot_replaced_by_lot(previous_lot, lot) From 5a3446751daf359b1a17982dc0ba4a021f0409c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 11 Sep 2023 13:12:30 +0200 Subject: [PATCH 08/11] fixup! shopfloor, checkout: allow change lot on line select --- shopfloor/services/checkout.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index a4de64c1a9..b317a47d58 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -540,7 +540,10 @@ def _select_lines_from_product( # Select all the lines of the package when we scan a product in a # package and we have only one. return self._select_lines_from_package( - picking, selection_lines, packages, prefill_qty=prefill_qty, + picking, + selection_lines, + packages, + prefill_qty=prefill_qty, message=kw.get("message"), ) else: From cf2feec6e1cb0fc6b1fa3d2eb1fe9e1cb3a0aa38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 11 Sep 2023 13:19:15 +0200 Subject: [PATCH 09/11] fixup! shopfloor, checkout: use lot_not_found_in_picking msg --- shopfloor/actions/message.py | 6 ------ shopfloor/services/checkout.py | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/shopfloor/actions/message.py b/shopfloor/actions/message.py index 85786c7901..3361340cfd 100644 --- a/shopfloor/actions/message.py +++ b/shopfloor/actions/message.py @@ -536,12 +536,6 @@ def lot_not_found(self): "body": _("This lot does not exist anymore."), } - def lot_not_found_in_picking(self): - return { - "message_type": "error", - "body": _("Lot is not in the current transfer."), - } - def lot_not_found_in_pickings(self): return { "message_type": "warning", diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index b317a47d58..75b5a83f31 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -590,7 +590,7 @@ def _select_lines_from_lot( # TODO: add a msg saying the lot has been changed return self._response_for_select_line( picking, - message=self.msg_store.lot_not_found_in_picking(), + message=self.msg_store.lot_not_found_in_picking(lot, picking), ) # Validate the scanned lot against the previous one if lot.id != kw["confirm_lot"]: From 912da920778d0db465a4d7eda569c67c024a42bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Mon, 11 Sep 2023 14:34:20 +0200 Subject: [PATCH 10/11] fixup! fixup! shopfloor, checkout: check lot on change confirmation --- shopfloor/tests/test_checkout_scan_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shopfloor/tests/test_checkout_scan_line.py b/shopfloor/tests/test_checkout_scan_line.py index 3068939a5b..181e8195ff 100644 --- a/shopfloor/tests/test_checkout_scan_line.py +++ b/shopfloor/tests/test_checkout_scan_line.py @@ -267,7 +267,7 @@ def test_scan_line_error_lot_different_change_success(self): picking, lot.name, self.msg_store.lot_different_change(), - need_confirm_lot=lot.name, + need_confirm_lot=lot.id, ) # Second scan to confirm the change of lot response = self.service.dispatch( From 7877df44fe26463ec3cceb7bc4e87f611c57fb0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Alix?= Date: Wed, 13 Sep 2023 09:54:53 +0200 Subject: [PATCH 11/11] fixup! shopfloor checkout: fix change lot --- shopfloor/services/checkout.py | 1 - 1 file changed, 1 deletion(-) diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index 75b5a83f31..e9f9af3385 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -587,7 +587,6 @@ def _select_lines_from_lot( message=self.msg_store.lot_different_change(), need_confirm_lot=lot.id, ) - # TODO: add a msg saying the lot has been changed return self._response_for_select_line( picking, message=self.msg_store.lot_not_found_in_picking(lot, picking),