From e83ea033c261bca0e809905af9af7b4dec63cbfb Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 3 Jul 2020 08:13:20 +0200 Subject: [PATCH] Ask to scan a tray type when no storage type is configured The use cases we want to cover are: 1. Putaway for a package/good without storage type 2. Putaway for a package with a storage type configured to be stored in a tray (associated with a tray type) 3. Putaway for a package with a storage type NOT configured on a tray type The case 1. is implement in "stock_vertical_lift", the 2. was already implement in this module, this commit implements the case 3. A typical flow is: * We configure a generic Kardex Box storage type, not associated with any tray type, that is set on the package at reception (the reception person doesn't know the tray type at this point). this Kardex Box storage type is set on the Vertical Lift view (above the shuttles). * On the putaway transfer, as per the configuration above, the putaway changes the destination location to the Vertical Lift view. * When we scan the package in the shuttle's screen, as we have a storage type which is not configured on any locations in the shuttle (reminder, if we had, it would select the tray automatically), the user is asked to scan a tray type of the correct size. --- .../models/vertical_lift_operation_put.py | 42 +++++++++++++- .../tests/test_put.py | 55 +++++++++++++++++-- 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/stock_vertical_lift_storage_type/models/vertical_lift_operation_put.py b/stock_vertical_lift_storage_type/models/vertical_lift_operation_put.py index 79a081531..054295ae5 100644 --- a/stock_vertical_lift_storage_type/models/vertical_lift_operation_put.py +++ b/stock_vertical_lift_storage_type/models/vertical_lift_operation_put.py @@ -57,10 +57,44 @@ class VerticalLiftOperationPut(models.Model): return tuple(updated_transitions) - def _has_storage_type(self): + def _has_storage_type_domain(self): move_line = self.current_move_line_id - storage_type = move_line.package_id.package_storage_type_id - return bool(storage_type) + package_storage_type = move_line.package_id.package_storage_type_id + # When a put-away is done based on the package's storage type and no + # destination is found, we can have 2 reasons: + # + # 1. No location is available according to the storage types rules, + # for instance because they are full + # 2. The storage type of the package doesn't have any location + # configured in the shuttle's locations + # + # We want to differentiate the 2 cases and handle them differently. + # For 2. we consider the storage type is not meant to be in any + # shuttle's tray but the user still tries: act the same way as if we + # had no storage type at all, the user has to scan the tray type. + # For 1. we try to find a destination location and if none is found, + # a notification indicates that the shuttle is full. + # In any case, the user should always be able to scan a different + # tray type. + return [ + ("id", "!=", self.location_id.id), + ("id", "child_of", self.location_id.id), + ( + "allowed_location_storage_type_ids", + "in", + package_storage_type.location_storage_type_ids.ids, + ), + ] + + def _has_storage_type(self): + domain = self._has_storage_type_domain() + # we don't care about order and count, only if we have at least one + # configured location for the storage type: sorting by id and limit by + # 1 will give the best explain plan + compatible_locations = self.env["stock.location"].search( + domain, limit=1, order="id" + ) + return bool(compatible_locations) def _putaway_with_storage_type(self): move_line = self.current_move_line_id @@ -74,6 +108,8 @@ class VerticalLiftOperationPut(models.Model): move_line.package_level_id.location_dest_id = new_destination self.fetch_tray() return True + # no destination found: all the trays for this tray type are full, or rejected + # by the location storage type rules return False def _put_away_with_storage_type_failed(self): diff --git a/stock_vertical_lift_storage_type/tests/test_put.py b/stock_vertical_lift_storage_type/tests/test_put.py index 083266d12..0d98c52b6 100644 --- a/stock_vertical_lift_storage_type/tests/test_put.py +++ b/stock_vertical_lift_storage_type/tests/test_put.py @@ -21,8 +21,24 @@ class TestPut(VerticalLiftCase): {"name": "Small 8x", "only_empty": True} ) - # storage type used for Tray 1A PackageStorageType = cls.env["stock.package.storage.type"] + # package storage type used only to putaway a package temporarily in + # the vertical lift view location before being put in a shuttle, which + # will not be configured in any shuttle's locations, we'll use when + # the operator knows that a good has to go in a shuttle, but doesn't + # know yet in which tray type: when the first putaway is done, the good + # stays in the vertical lift view (above the shuttles), then, when the + # user scans the package in a shuttle, they have to scan a tray type. + cls.package_storage_type_buffer = PackageStorageType.create( + { + "name": "VLift Box", + "location_storage_type_ids": [ + (4, cls.location_storage_type_buffer.id), + ], + } + ) + # storage type used for Tray 1A, user won't have to scan a tray type + # when this storage type is already set on the package cls.package_storage_type_small_8x = PackageStorageType.create( { "name": "Small 8x", @@ -58,9 +74,7 @@ class TestPut(VerticalLiftCase): } ) - cls.package = cls.env["stock.quant.package"].create( - {"package_storage_type_id": cls.package_storage_type_small_8x.id} - ) + cls.package = cls.env["stock.quant.package"].create({}) cls._update_qty_in_location( cls.wh.wh_input_stock_loc_id, cls.product_socks, 10, package=cls.package ) @@ -97,6 +111,7 @@ class TestPut(VerticalLiftCase): ) def test_storage_type_put_away(self): + self.package.package_storage_type_id = self.package_storage_type_small_8x move_line = self.int_picking.move_line_ids self.assertEqual(move_line.location_dest_id, self.vertical_lift_loc) self.assertEqual( @@ -113,8 +128,38 @@ class TestPut(VerticalLiftCase): # the dest location was Vertical Lift, it has been change to Vertical # Lift/Shuttle 1, and the computation from there took the first cell # available, we should be the pos x1 and y1 in the tray A. - self.assertTrue(move_line.location_dest_id, self.location_1a_x1y1) + self.assertEqual(move_line.location_dest_id, self.location_1a_x1y1) + self.assertEqual( + move_line.package_level_id.location_dest_id, self.location_1a_x1y1 + ) # the state goes straight to "save", as we don't need to scan the tray type # when a putaway is available self.assertEqual(operation.state, "save") + + def test_storage_type_not_configured(self): + # if we do have a storage type, but the storage type is not used in the + # shuttle (although it may be used on the lift view location, so we can have an + # initial putaway rule that puts the package in the lift view location first), + # we have to ask to scan a tray type + self.package.package_storage_type_id = self.package_storage_type_buffer + move_line = self.int_picking.move_line_ids + self.assertEqual(move_line.location_dest_id, self.vertical_lift_loc) + self.assertEqual( + move_line.package_level_id.location_dest_id, self.vertical_lift_loc + ) + + operation = self._open_screen("put") + # we begin with an empty screen, user has to scan a package, product, + # or lot + self.assertEqual(operation.state, "scan_source") + operation.on_barcode_scanned(self.package.name) + + self.assertEqual(operation.current_move_line_id, move_line) + # it stays here, as the put-away has no rule to put the "buffer box" in + # any location + self.assertEqual(move_line.location_dest_id, self.vertical_lift_loc) + + # and the user has to scan a tray type (so we are back to the normal + # flow, tested in stock_vertical_lift) + self.assertEqual(operation.state, "scan_tray_type")