diff --git a/shopfloor/actions/data.py b/shopfloor/actions/data.py index 0b364c2254..8506191362 100644 --- a/shopfloor/actions/data.py +++ b/shopfloor/actions/data.py @@ -13,7 +13,7 @@ class DataAction(Component): def location(self, record, **kw): parser = self._location_parser data = self._jsonify(record.with_context(location=record.id), parser, **kw) - if "with_operation_progress" in kw: + if kw.get("with_operation_progress"): lines_blacklist = ( kw.get("progress_lines_blacklist") or self.env["stock.move.line"].browse() @@ -46,7 +46,7 @@ def _get_picking_parser(self, record, **kw): # and it may reduce performance significatively # when dealing with a large number of pickings. # Thus, we make it optional. - if "with_progress" in kw: + if kw.get("with_progress"): parser.append("progress") return parser @@ -72,6 +72,7 @@ def _picking_parser(self, **kw): "bulk_line_count", "total_weight:weight", "scheduled_date", + "priority", ] @ensure_model("stock.quant.package") diff --git a/shopfloor/actions/schema.py b/shopfloor/actions/schema.py index 34314960e4..5c8491e004 100644 --- a/shopfloor/actions/schema.py +++ b/shopfloor/actions/schema.py @@ -27,6 +27,7 @@ def picking(self): "scheduled_date": {"type": "string", "nullable": False, "required": True}, "progress": {"type": "float", "nullable": True}, "location_dest": self._schema_dict_of(self.location(), required=False), + "priority": {"type": "string", "nullable": True, "required": False}, } def move_line(self, with_packaging=False, with_picking=False): diff --git a/shopfloor/models/stock_move_line.py b/shopfloor/models/stock_move_line.py index e70ae50307..b70d90f4c7 100644 --- a/shopfloor/models/stock_move_line.py +++ b/shopfloor/models/stock_move_line.py @@ -24,6 +24,7 @@ class StockMoveLine(models.Model): # we search lines based on their location in some workflows location_id = fields.Many2one(index=True) package_id = fields.Many2one(index=True) + result_package_id = fields.Many2one(index=True) # allow domain on picking_id.xxx without too much perf penalty picking_id = fields.Many2one(auto_join=True) diff --git a/shopfloor/models/stock_picking.py b/shopfloor/models/stock_picking.py index bb11e54c2b..3bff487489 100644 --- a/shopfloor/models/stock_picking.py +++ b/shopfloor/models/stock_picking.py @@ -116,3 +116,14 @@ def split_assigned_move_lines(self, move_lines=None): ) assigned_moves._action_assign() return new_picking.id + + def _put_in_pack(self, move_line_ids, create_package_level=True): + """ + Marks the corresponding move lines as 'shopfloor_checkout_done' + when the package is created in the backend. + + """ + new_package = super()._put_in_pack(move_line_ids, create_package_level) + lines = move_line_ids.filtered(lambda p: p.result_package_id == new_package) + lines.write({"shopfloor_checkout_done": True}) + return new_package diff --git a/shopfloor/services/checkout.py b/shopfloor/services/checkout.py index 0bc23b384a..52d815fdb1 100644 --- a/shopfloor/services/checkout.py +++ b/shopfloor/services/checkout.py @@ -103,7 +103,6 @@ def _response_for_select_package(self, picking, lines, message=None): data={ "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" ), @@ -111,14 +110,6 @@ def _response_for_select_package(self, picking, lines, message=None): message=message, ) - def _data_for_packing_info(self, picking): - """Return the packing information - - Intended to be extended. - """ - # TODO: This could be avoided if included in the picking parser. - return "" - def _response_for_select_dest_package(self, picking, move_lines, message=None): packages = picking.mapped("move_line_ids.result_package_id").filtered( "packaging_id" @@ -363,7 +354,7 @@ def _domain_for_list_stock_picking(self): ] def _order_for_list_stock_picking(self): - return "scheduled_date asc, id asc" + return "priority desc, scheduled_date asc, id asc" def list_stock_picking(self): """List stock.picking records available @@ -1478,11 +1469,8 @@ def done(self, picking_id, confirmation=False): }, ) lines_done = self._lines_checkout_done(picking) - dest_location = picking.location_dest_id - child_locations = self.env["stock.location"].search( - [("id", "child_of", dest_location.id), ("usage", "!=", "view")] - ) - if len(child_locations) > 0 and child_locations != dest_location: + dest_location = lines_done.move_id.location_dest_id + if len(dest_location) != 1 or dest_location.usage == "view": return self._response_for_select_child_location( picking, ) diff --git a/shopfloor/services/cluster_picking.py b/shopfloor/services/cluster_picking.py index 721f835c72..f5fec4b87d 100644 --- a/shopfloor/services/cluster_picking.py +++ b/shopfloor/services/cluster_picking.py @@ -776,8 +776,11 @@ def scan_destination_pack(self, picking_batch_id, move_line_id, barcode, quantit qty_done=quantity, ) move_line.write({"qty_done": quantity, "result_package_id": bin_package.id}) - - zero_check = move_line.picking_id.picking_type_id.shopfloor_zero_check + # Only apply zero check if the product is of type "product". + zero_check = ( + move_line.product_id.type == "product" + and move_line.picking_id.picking_type_id.shopfloor_zero_check + ) if zero_check and move_line.location_id.planned_qty_in_location_is_empty(): return self._response_for_zero_check(batch, move_line) diff --git a/shopfloor/services/zone_picking.py b/shopfloor/services/zone_picking.py index d73283f13b..fad89f3671 100644 --- a/shopfloor/services/zone_picking.py +++ b/shopfloor/services/zone_picking.py @@ -667,12 +667,15 @@ def _scan_source_package( ) else: change_package_lot = self._actions_for("change.package.lot") + move_line = first(move_lines) response = change_package_lot.change_package( - first(move_lines), + move_line, package, - # FIXME we may need to pass the quantity being done - self._response_for_set_line_destination, - self._response_for_change_pack_lot, + response_ok_func=functools.partial( + self._response_for_set_line_destination, + qty_done=self._get_prefill_qty(move_line, qty=0), + ), + response_error_func=self._response_for_change_pack_lot, ) else: response = self._list_move_lines(sublocation or self.zone_location) @@ -959,7 +962,11 @@ def _set_destination_location( location_changed = True # Zero check - zero_check = self.picking_type.shopfloor_zero_check + # Only apply zero check if the product is of type "product". + zero_check = ( + move_line.product_id.type == "product" + and self.picking_type.shopfloor_zero_check + ) if zero_check and move_line.location_id.planned_qty_in_location_is_empty(): response = self._response_for_zero_check(move_line) return (location_changed, response) @@ -1042,7 +1049,11 @@ def _set_destination_package(self, move_line, quantity, package): return (package_changed, response) package_changed = True # Zero check - zero_check = self.picking_type.shopfloor_zero_check + # Only apply zero check if the product is of type "product". + zero_check = ( + move_line.product_id.type == "product" + and self.picking_type.shopfloor_zero_check + ) if zero_check and move_line.location_id.planned_qty_in_location_is_empty(): response = self._response_for_zero_check(move_line) return (package_changed, response) @@ -1434,7 +1445,10 @@ def change_pack_lot(self, move_line_id, barcode): # pre-configured callable used to generate the response as the # change.package.lot component is not aware of the needed response type # and related parameters for zone picking scenario - response_ok_func = functools.partial(self._response_for_set_line_destination) + response_ok_func = functools.partial( + self._response_for_set_line_destination, + qty_done=self._get_prefill_qty(move_line), + ) response_error_func = functools.partial(self._response_for_change_pack_lot) response = None change_package_lot = self._actions_for("change.package.lot") diff --git a/shopfloor/tests/test_actions_data.py b/shopfloor/tests/test_actions_data.py index 0fbe90233e..de502b4f76 100644 --- a/shopfloor/tests/test_actions_data.py +++ b/shopfloor/tests/test_actions_data.py @@ -155,6 +155,7 @@ def test_data_picking(self): "partner": {"id": self.customer.id, "name": self.customer.name}, "carrier": {"id": carrier.id, "name": carrier.name}, "ship_carrier": None, + "priority": "0", } self.assertEqual(data.pop("scheduled_date").split("T")[0], "2020-08-03") self.assertDictEqual(data, expected) @@ -179,6 +180,7 @@ def test_data_picking_with_progress(self): "carrier": {"id": carrier.id, "name": carrier.name}, "ship_carrier": None, "progress": 0.0, + "priority": "0", } self.assertEqual(data.pop("scheduled_date").split("T")[0], "2020-08-03") self.assertDictEqual(data, expected) diff --git a/shopfloor/tests/test_checkout_base.py b/shopfloor/tests/test_checkout_base.py index c5134ed85b..738c3172a6 100644 --- a/shopfloor/tests/test_checkout_base.py +++ b/shopfloor/tests/test_checkout_base.py @@ -68,7 +68,6 @@ def _assert_select_package_qty_above(self, response, picking): self._move_line_data(ml) for ml in picking.move_line_ids.sorted() ], "picking": self._picking_summary_data(picking), - "packing_info": "", "no_package_enabled": True, }, message={ diff --git a/shopfloor/tests/test_checkout_done.py b/shopfloor/tests/test_checkout_done.py index a9acce2032..1e6c93c3aa 100644 --- a/shopfloor/tests/test_checkout_done.py +++ b/shopfloor/tests/test_checkout_done.py @@ -65,8 +65,34 @@ def test_done_partial_confirm(self): "done", params={"picking_id": self.picking.id, "confirmation": True} ) - self.assertRecordValues(self.picking, [{"state": "assigned"}]) + self.assertRecordValues(self.picking, [{"state": "done"}]) + + self.assert_response( + response, + next_state="select_document", + message=self.service.msg_store.transfer_done_success(self.picking), + data={"restrict_scan_first": False}, + ) + def test_done_ask_destination_location(self): + """Check asking for destination location for view type location.""" + view_location = ( + self.env["stock.location"] + .sudo() + .create( + { + "name": "Test Location Usage View", + "location_id": self.picking.move_lines.location_dest_id.id, + "usage": "view", + } + ) + ) + self.picking.move_lines.location_dest_id = view_location + response = self.service.dispatch( + "done", params={"picking_id": self.picking.id, "confirmation": True} + ) + + self.assertRecordValues(self.picking, [{"state": "assigned"}]) self.assert_response( response, next_state="select_child_location", diff --git a/shopfloor/tests/test_checkout_list_delivery_packaging.py b/shopfloor/tests/test_checkout_list_delivery_packaging.py index 48fc87c446..2e702a83a5 100644 --- a/shopfloor/tests/test_checkout_list_delivery_packaging.py +++ b/shopfloor/tests/test_checkout_list_delivery_packaging.py @@ -111,7 +111,6 @@ def test_list_delivery_packaging_not_available(self): "selected_move_lines": [ self._move_line_data(ml) for ml in selected_lines.sorted() ], - "packing_info": self.service._data_for_packing_info(self.picking), "no_package_enabled": not self.service.options.get( "checkout__disable_no_package" ), diff --git a/shopfloor/tests/test_checkout_scan_package_action.py b/shopfloor/tests/test_checkout_scan_package_action.py index 446f50debd..a2609d3f8e 100644 --- a/shopfloor/tests/test_checkout_scan_package_action.py +++ b/shopfloor/tests/test_checkout_scan_package_action.py @@ -172,7 +172,6 @@ def test_scan_package_action_scan_package_keep_source_package_error(self): data={ "picking": self.data.picking(picking), "selected_move_lines": self.data.move_lines(selected_lines), - "packing_info": self.service._data_for_packing_info(picking), "no_package_enabled": not self.service.options.get( "checkout__disable_no_package" ), @@ -455,3 +454,28 @@ def test_scan_package_action_scan_not_found(self): selected_line, message={"message_type": "error", "body": "Barcode not found"}, ) + + def test_put_in_pack(self): + picking = self._create_picking( + lines=[(self.product_a, 10), (self.product_b, 20)] + ) + self._fill_stock_for_moves(picking.move_lines) + picking.action_assign() + + # Test that the move lines are marked as 'shopfloor_checkout_done' + # when putting them in a pack in the backend. + picking._put_in_pack(picking.move_line_ids) + self.assertTrue( + all(line.shopfloor_checkout_done for line in picking.move_line_ids) + ) + + # Check that we return those lines to the frontend. + res = self.service.dispatch( + "summary", + params={ + "picking_id": picking.id, + }, + ) + returned_lines = res["data"]["summary"]["picking"]["move_lines"] + expected_line_ids = [line["id"] for line in returned_lines] + self.assertEqual(expected_line_ids, picking.move_line_ids.ids) diff --git a/shopfloor/tests/test_checkout_select_package_base.py b/shopfloor/tests/test_checkout_select_package_base.py index 5f4ffe42ce..b3be128eab 100644 --- a/shopfloor/tests/test_checkout_select_package_base.py +++ b/shopfloor/tests/test_checkout_select_package_base.py @@ -20,7 +20,6 @@ def _assert_selected_response( self._move_line_data(ml) for ml in selected_lines.sorted() ], "picking": self._picking_summary_data(picking), - "packing_info": packing_info, "no_package_enabled": no_package_enabled, }, message=message, diff --git a/shopfloor/tests/test_zone_picking_change_pack_lot.py b/shopfloor/tests/test_zone_picking_change_pack_lot.py index 7e080c90e2..282f0398fa 100644 --- a/shopfloor/tests/test_zone_picking_change_pack_lot.py +++ b/shopfloor/tests/test_zone_picking_change_pack_lot.py @@ -87,6 +87,7 @@ def test_change_pack_lot_change_pack_ok(self): message=self.service.msg_store.package_replaced_by_package( previous_package, self.free_package ), + qty_done=self.service._get_prefill_qty(move_line), ) def test_change_pack_lot_change_lot_ok(self): @@ -137,4 +138,5 @@ def test_change_pack_lot_change_lot_ok(self): message=self.service.msg_store.lot_replaced_by_lot( previous_lot, self.free_lot ), + qty_done=self.service._get_prefill_qty(move_line), ) diff --git a/shopfloor/tests/test_zone_picking_select_line.py b/shopfloor/tests/test_zone_picking_select_line.py index b673d59510..d6b486c134 100644 --- a/shopfloor/tests/test_zone_picking_select_line.py +++ b/shopfloor/tests/test_zone_picking_select_line.py @@ -348,6 +348,7 @@ def test_scan_source_barcode_package_can_replace_in_line(self): message=self.service.msg_store.package_replaced_by_package( package1, package1b ), + qty_done=self.service._get_prefill_qty(move_lines[0]), ) # Check the package has been changed on the move line self.assertEqual(self.picking1.package_level_ids[0].package_id, package1b) diff --git a/shopfloor/views/shopfloor_menu.xml b/shopfloor/views/shopfloor_menu.xml index 868dc85c6c..5617de5f9e 100644 --- a/shopfloor/views/shopfloor_menu.xml +++ b/shopfloor/views/shopfloor_menu.xml @@ -112,6 +112,7 @@ name="scan_location_or_pack_first" attrs="{'invisible': [('scan_location_or_pack_first_is_possible', '=', False)]}" > +
total + val.qty_done, 0); + } return 0; }, lot: function () { diff --git a/shopfloor_mobile/static/wms/src/scenario/checkout.js b/shopfloor_mobile/static/wms/src/scenario/checkout.js index d88c2fa524..8eb4c7115e 100644 --- a/shopfloor_mobile/static/wms/src/scenario/checkout.js +++ b/shopfloor_mobile/static/wms/src/scenario/checkout.js @@ -76,8 +76,8 @@ const Checkout = {
- -

+ +

+ +

+ - - + +