From 4b2e3982834c4fba033a469d4682ae64be182983 Mon Sep 17 00:00:00 2001 From: Thierry Ducrest Date: Fri, 26 Jan 2024 10:59:45 +0100 Subject: [PATCH] shopfloor_delivery_shipment: Group package by sales When working from a location after a dock and then a location. There can be a long list of packages without hints about the one belonging to a delivery already started. To improve this, the packages are grouped by sales order, keep on top of the list the sales order started but not yet all loaded. --- .../services/delivery_shipment.py | 82 +++++++++++++------ .../tests/test_delivery_shipment_base.py | 5 +- ...est_delivery_shipment_scan_document_lot.py | 4 +- ...delivery_shipment_scan_document_package.py | 6 +- ...delivery_shipment_scan_document_picking.py | 6 +- ...delivery_shipment_scan_document_product.py | 5 +- 6 files changed, 72 insertions(+), 36 deletions(-) diff --git a/shopfloor_delivery_shipment/services/delivery_shipment.py b/shopfloor_delivery_shipment/services/delivery_shipment.py index 38f34503a3..ea28114761 100644 --- a/shopfloor_delivery_shipment/services/delivery_shipment.py +++ b/shopfloor_delivery_shipment/services/delivery_shipment.py @@ -563,17 +563,6 @@ def _data_for_content_to_load_from_pickings(self, shipment_advice): It returns a dict where keys are source locations and values are dictionaries listing package_levels and move_lines that remain to load from transfers partially loaded in a shipment. - - E.g: - { - "SRC_LOCATION1": { - "package_levels": [{PKG_LEVEL_DATA}, ...], - "move_lines": [{MOVE_LINE_DATA}, ...], - }, - "SRC_LOCATION2": { - ... - }, - } """ domain = self._find_move_lines_domain(shipment_advice) # Restrict to lines not loaded @@ -593,7 +582,7 @@ def _data_for_content_to_load_from_pickings(self, shipment_advice): pickings_partially_loaded = loaded_lines.picking_id domain += [("picking_id", "in", pickings_partially_loaded.ids)] move_lines = self.env["stock.move.line"].search(domain) - return self._prepare_data_for_content(move_lines) + return self._prepare_data_for_content(move_lines, False) def _data_for_content_to_load_from_picking( self, shipment_advice, picking=None, location=None @@ -601,11 +590,34 @@ def _data_for_content_to_load_from_picking( """Return a dictionary where keys are source locations and values are dictionaries listing package_levels and move_lines loaded or to load. + """ + # Grab move lines to sort, restricted to the current delivery + if picking: + move_lines = self._find_move_lines_to_process_from_picking( + shipment_advice, picking + ) + elif location: + move_lines = self._find_move_lines_from_location(shipment_advice, location) + group_by_sales = bool(location) + return self._prepare_data_for_content(move_lines, group_by_sales) + + def _prepare_data_for_content(self, move_lines, group_by_sales): + """Returns data for package levels and/or move lines. + + The data is grouped by source location with a key for package levels and + a key for move lines. + The package levels can be grouped by related sales order and if they are not + they will be all stored into a key whose name is only spaces. + The move lines are not grouped by sales. E.g: { "SRC_LOCATION1": { - "package_levels": [{PKG_LEVEL_DATA}, ...], + "package_levels": { + "sale 01": [{PKG_LEVEL_DATA}, ...], + "sale 02": [{PKG_LEVEL_DATA}, ...], + " ": [{PKG_LEVEL_DATA}, ...], + } "move_lines": [{MOVE_LINE_DATA}, ...], }, "SRC_LOCATION2": { @@ -613,26 +625,44 @@ def _data_for_content_to_load_from_picking( }, } """ - # Grab move lines to sort, restricted to the current delivery - if picking: - move_lines = self._find_move_lines_to_process_from_picking( - shipment_advice, picking - ) - elif location: - move_lines = self._find_move_lines_from_location(shipment_advice, location) - return self._prepare_data_for_content(move_lines) - - def _prepare_data_for_content(self, move_lines): + empty_group_name = " " data = collections.OrderedDict() package_level_ids = [] + # Sort and group move lines by source location and prepare the data - for move_line in move_lines.sorted(lambda ml: ml.location_id.name): + sales_started = [] + if group_by_sales: + sales_started = move_lines.shipment_advice_id.loaded_picking_ids.sale_id + # Grouping by sales (the packages) sort starting from last position + # 2 the lines NOT in a sales order whose loading has started. + # 1 the lines in a sales being loaded and the line is done. + # 0 the lines in a sales being loaded and not yet done + move_lines = move_lines.sorted( + lambda ml: 2 + if ml.move_id.sale_line_id.order_id not in sales_started + else (1 if ml.package_level_id.is_done else 0) + ) + else: + move_lines = move_lines.sorted(lambda ml: ml.location_id.name) + + for move_line in move_lines: location_data = data.setdefault(move_line.location_id.name, {}) if move_line.package_level_id: - pl_data = location_data.setdefault("package_levels", []) + pl_data = location_data.setdefault( + "package_levels", collections.OrderedDict() + ) if move_line.package_level_id.id in package_level_ids: continue - pl_data.append(self.data.package_level(move_line.package_level_id)) + if group_by_sales: + group_name = ( + move_line.move_id.sale_line_id.order_id.name or empty_group_name + ) + else: + group_name = empty_group_name + pl_group_data = pl_data.setdefault(group_name, []) + pl_group_data.append( + self.data.package_level(move_line.package_level_id) + ) package_level_ids.append(move_line.package_level_id.id) else: location_data.setdefault("move_lines", []).append( diff --git a/shopfloor_delivery_shipment/tests/test_delivery_shipment_base.py b/shopfloor_delivery_shipment/tests/test_delivery_shipment_base.py index 67b023792a..c3fab2279d 100644 --- a/shopfloor_delivery_shipment/tests/test_delivery_shipment_base.py +++ b/shopfloor_delivery_shipment/tests/test_delivery_shipment_base.py @@ -128,7 +128,10 @@ def assert_response_scan_document( shipment_advice, picking ) if lines_to_load: - data["content"] = self.service._prepare_data_for_content(lines_to_load) + group_by_sales = bool(location) + data["content"] = self.service._prepare_data_for_content( + lines_to_load, group_by_sales + ) self.assert_response( response, next_state="scan_document", diff --git a/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_lot.py b/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_lot.py index 23b4803a3a..fd6c7987b0 100644 --- a/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_lot.py +++ b/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_lot.py @@ -132,7 +132,7 @@ def test_scan_document_shipment_not_planned_lot_not_planned(self): ) # 'package_levels' key contains the package available from the same delivery self.assertEqual( - content[location_src]["package_levels"], + content[location_src]["package_levels"][" "], self.service.data.package_levels(self.picking1.package_level_ids), ) @@ -187,7 +187,7 @@ def test_scan_document_lot_already_loaded(self): ) # 'package_levels' key contains the package available from the same delivery self.assertEqual( - content[location_src]["package_levels"], + content[location_src]["package_levels"][" "], self.service.data.package_levels(self.picking1.package_level_ids), ) diff --git a/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_package.py b/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_package.py index ab7d628f84..268d39c740 100644 --- a/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_package.py +++ b/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_package.py @@ -91,8 +91,9 @@ def test_scan_document_shipment_not_planned_package_not_planned(self): self.service.data.move_lines(self.picking1.move_line_ids_without_package), ) # 'package_levels' key contains the package which has been loaded + # grouped in a dict self.assertEqual( - content[location_src]["package_levels"], + content[location_src]["package_levels"][" "], self.service.data.package_levels(package_level), ) @@ -141,8 +142,9 @@ def test_scan_document_package_already_loaded(self): self.service.data.move_lines(self.picking1.move_line_ids_without_package), ) # 'package_levels' key contains the package which has been loaded + # grouped in a dic self.assertEqual( - content[location_src]["package_levels"], + content[location_src]["package_levels"][" "], self.service.data.package_levels(package_level), ) diff --git a/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_picking.py b/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_picking.py index e6cf0963d4..31ef35740d 100644 --- a/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_picking.py +++ b/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_picking.py @@ -70,7 +70,7 @@ def test_scan_document_shipment_planned_picking_planned(self): ) # 'package_levels' key contains the packages self.assertEqual( - content[location_src]["package_levels"], + content[location_src]["package_levels"][" "], self.service.data.package_levels(self.picking1.package_level_ids), ) @@ -199,7 +199,7 @@ def test_scan_document_shipment_not_planned_picking_partially_planned(self): self.assertNotIn("move_lines", content[location_src]) # 'package_levels' key contains the not planned packages self.assertEqual( - content[location_src]["package_levels"], + content[location_src]["package_levels"][" "], self.service.data.package_levels(self.picking1.package_level_ids), ) @@ -261,6 +261,6 @@ def test_scan_document_shipment_not_planned_picking_partially_loaded(self): self.assertNotIn("move_lines", content[location_src]) # 'package_levels' key contains the already loaded package levels self.assertEqual( - content[location_src]["package_levels"], + content[location_src]["package_levels"][" "], self.service.data.package_levels(self.picking1.package_level_ids), ) diff --git a/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_product.py b/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_product.py index e6ef1abc59..9a0ca1c68b 100644 --- a/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_product.py +++ b/shopfloor_delivery_shipment/tests/test_delivery_shipment_scan_document_product.py @@ -126,8 +126,9 @@ def test_scan_document_shipment_not_planned_product_not_planned(self): ), ) # 'package_levels' key contains the package available from the same delivery + # grouped in a dict self.assertEqual( - content[location_src]["package_levels"], + content[location_src]["package_levels"][" "], self.service.data.package_levels(self.picking1.package_level_ids), ) @@ -184,7 +185,7 @@ def test_scan_document_product_already_loaded(self): ) # 'package_levels' key contains the package available from the same delivery self.assertEqual( - content[location_src]["package_levels"], + content[location_src]["package_levels"][" "], self.service.data.package_levels(self.picking1.package_level_ids), )