From 48f3d6010286c9fb98005a4cec5bbf0d69f80ee3 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 2 Jul 2020 08:24:49 +0200 Subject: [PATCH 1/5] Add stock_vertical_lift_storage_type Compatibility module between stock_vertical_lift and stock_storage_type (in OCA/wms). In the vertical lift's Putaway screen, when a good is scanned for a putaway, the user has to scan the tray type of the corresponding size, so an empty place in a matching tray is found. When we use storage types, we should know what tray is compatible with the storage type. Changes with this module: * The storage types of trays cannot be selected in the locations form, they have to be set in the Tray types. * In the lift put-away screen, when a package has a storage type, the user isn't asked to scan a tray type, instead, the putaway of the Package Storage Type is applied. --- stock_vertical_lift_storage_type/__init__.py | 1 + .../__manifest__.py | 21 +++ .../models/__init__.py | 3 + .../models/stock_location.py | 50 ++++++++ .../models/stock_location_tray_type.py | 29 +++++ .../models/vertical_lift_operation_put.py | 87 +++++++++++++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 14 ++ .../tests/__init__.py | 3 + .../tests/common.py | 27 ++++ .../tests/test_put.py | 120 ++++++++++++++++++ .../tests/test_stock_location.py | 90 +++++++++++++ .../tests/test_tray_type.py | 38 ++++++ .../views/stock_location_tray_type_views.xml | 18 +++ .../views/stock_location_views.xml | 15 +++ 15 files changed, 517 insertions(+) create mode 100644 stock_vertical_lift_storage_type/__init__.py create mode 100644 stock_vertical_lift_storage_type/__manifest__.py create mode 100644 stock_vertical_lift_storage_type/models/__init__.py create mode 100644 stock_vertical_lift_storage_type/models/stock_location.py create mode 100644 stock_vertical_lift_storage_type/models/stock_location_tray_type.py create mode 100644 stock_vertical_lift_storage_type/models/vertical_lift_operation_put.py create mode 100644 stock_vertical_lift_storage_type/readme/CONTRIBUTORS.rst create mode 100644 stock_vertical_lift_storage_type/readme/DESCRIPTION.rst create mode 100644 stock_vertical_lift_storage_type/tests/__init__.py create mode 100644 stock_vertical_lift_storage_type/tests/common.py create mode 100644 stock_vertical_lift_storage_type/tests/test_put.py create mode 100644 stock_vertical_lift_storage_type/tests/test_stock_location.py create mode 100644 stock_vertical_lift_storage_type/tests/test_tray_type.py create mode 100644 stock_vertical_lift_storage_type/views/stock_location_tray_type_views.xml create mode 100644 stock_vertical_lift_storage_type/views/stock_location_views.xml diff --git a/stock_vertical_lift_storage_type/__init__.py b/stock_vertical_lift_storage_type/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/stock_vertical_lift_storage_type/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_vertical_lift_storage_type/__manifest__.py b/stock_vertical_lift_storage_type/__manifest__.py new file mode 100644 index 000000000..99d91fd3e --- /dev/null +++ b/stock_vertical_lift_storage_type/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + "name": "Vertical Lift - Storage Type", + "summary": "Compatibility layer for storage types on vertical lifts", + "version": "13.0.1.0.0", + "category": "Stock", + "author": "Camptocamp, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": [ + "stock_vertical_lift", # OCA/stock-logistics-warehouse + "stock_storage_type", # OCA/wms + ], + "website": "https://github.com/OCA/stock-logistics-warehouse", + "data": [ + "views/stock_location_tray_type_views.xml", + "views/stock_location_views.xml", + ], + "installable": True, + "development_status": "Alpha", +} diff --git a/stock_vertical_lift_storage_type/models/__init__.py b/stock_vertical_lift_storage_type/models/__init__.py new file mode 100644 index 000000000..029c1ede2 --- /dev/null +++ b/stock_vertical_lift_storage_type/models/__init__.py @@ -0,0 +1,3 @@ +from . import stock_location +from . import stock_location_tray_type +from . import vertical_lift_operation_put diff --git a/stock_vertical_lift_storage_type/models/stock_location.py b/stock_vertical_lift_storage_type/models/stock_location.py new file mode 100644 index 000000000..38b086abe --- /dev/null +++ b/stock_vertical_lift_storage_type/models/stock_location.py @@ -0,0 +1,50 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, exceptions, models + + +class StockLocation(models.Model): + _inherit = "stock.location" + + @api.model_create_multi + def create(self, vals_list): + if not self.env.context.get("_sync_tray_type"): + for vals in vals_list: + if vals.get("tray_type_id") and vals.get("location_storage_type_ids"): + raise exceptions.UserError( + _( + "Error creating '{}': Location storage" + " type must be set on the tray type" + ).format(vals.get("name")) + ) + + records = super().create(vals_list) + records._sync_tray_type_storage_types() + return records + + def write(self, values): + if not self.env.context.get("_sync_tray_type"): + if values.get("location_storage_type_ids"): + if values.get("tray_type_id"): + has_tray_type = self + else: + has_tray_type = self.filtered("tray_type_id") + if has_tray_type: + raise exceptions.UserError( + _( + "Error updating {}: Location storage" + " type must be set on the tray type" + ).format(", ".join(has_tray_type.mapped("name"))) + ) + res = super().write(values) + if values.get("tray_type_id"): + self._sync_tray_type_storage_types() + return res + + def _sync_tray_type_storage_types(self): + for location in self.with_context(_sync_tray_type=True): + if not location.tray_type_id: + continue + storage_types = location.tray_type_id.location_storage_type_ids + location.write({"location_storage_type_ids": [(6, 0, storage_types.ids)]}) diff --git a/stock_vertical_lift_storage_type/models/stock_location_tray_type.py b/stock_vertical_lift_storage_type/models/stock_location_tray_type.py new file mode 100644 index 000000000..15fbcff51 --- /dev/null +++ b/stock_vertical_lift_storage_type/models/stock_location_tray_type.py @@ -0,0 +1,29 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class StockLocationTrayType(models.Model): + _inherit = "stock.location.tray.type" + + location_storage_type_ids = fields.Many2many( + comodel_name="stock.location.storage.type", + help="Location storage types applied on the location using " "this tray type.", + ) + + def write(self, values): + res = super().write(values) + if values.get("location_storage_type_ids"): + self._sync_location_storage_type_ids() + return res + + def _sync_location_storage_type_ids(self): + for tray_type in self: + tray_type.location_ids.with_context(_sync_tray_type=True).write( + { + "location_storage_type_ids": [ + (6, 0, tray_type.location_storage_type_ids.ids) + ] + } + ) 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 new file mode 100644 index 000000000..79a081531 --- /dev/null +++ b/stock_vertical_lift_storage_type/models/vertical_lift_operation_put.py @@ -0,0 +1,87 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, models + + +class VerticalLiftOperationPut(models.Model): + _inherit = "vertical.lift.operation.put" + + # In the base module, when a good is scanned for a put-away, the user must + # then scan a tray type, which will be used to find an available cell of + # this type. When we use storage types (OCA/wms::stock_storage_type), we + # shouldn't need to scan a tray type if we already have a storage type, + # that could be used to find an available cell location for this type + # (applying all the possible restrictions from storage type). + + def _transitions(self): + transitions = super()._transitions() + updated_transitions = [] + for transition in transitions: + states = (transition.current_state, transition.next_state) + if states == ("scan_tray_type", "save"): + # insert new transitions just before the normal transition + # scanning the tray type, that will bypass it when we have + # a storage type + updated_transitions.append( + self.Transition( + "scan_tray_type", + "save", + lambda self: self._has_storage_type() + and self._putaway_with_storage_type(), + # this is the trick that makes the transition applies + # its function and directly jumps to save + direct_eval=True, + ) + ) + updated_transitions.append( + self.Transition( + "scan_tray_type", + "scan_source", + # the transition above returned False because it could + # not find a free space, in that case, abort the + # put-away for this line in this shuttle + lambda self: self._has_storage_type() + and self._put_away_with_storage_type_failed() + and self.clear_current_move_line(), + # this is the trick that makes the transition applies + # its function and directly jumps to save + direct_eval=True, + ) + ) + # if none of the 2 transitions above is applied (because + # self._has_storage_type() is False), the state remains + # `scan_tray_type`, for the base transition doesn't have + # `direct_eval=True` + updated_transitions.append(transition) + + return tuple(updated_transitions) + + def _has_storage_type(self): + move_line = self.current_move_line_id + storage_type = move_line.package_id.package_storage_type_id + return bool(storage_type) + + def _putaway_with_storage_type(self): + move_line = self.current_move_line_id + # Trigger the put-away application to place it somewhere inside + # the current shuttle's location. + new_destination = move_line.location_dest_id._get_pack_putaway_strategy( + self.location_id, move_line.package_id.quant_ids, move_line.product_id + ) + if new_destination and new_destination.vertical_lift_kind == "cell": + move_line.location_dest_id = new_destination + move_line.package_level_id.location_dest_id = new_destination + self.fetch_tray() + return True + return False + + def _put_away_with_storage_type_failed(self): + move_line = self.current_move_line_id + storage_type = move_line.package_id.package_storage_type_id + self.env.user.notify_warning( + _("No free space found for storage type '{}' in shuttle '{}'").format( + storage_type.name, self.name + ) + ) + return True diff --git a/stock_vertical_lift_storage_type/readme/CONTRIBUTORS.rst b/stock_vertical_lift_storage_type/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..48286263c --- /dev/null +++ b/stock_vertical_lift_storage_type/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Guewen Baconnier diff --git a/stock_vertical_lift_storage_type/readme/DESCRIPTION.rst b/stock_vertical_lift_storage_type/readme/DESCRIPTION.rst new file mode 100644 index 000000000..c8ee6394c --- /dev/null +++ b/stock_vertical_lift_storage_type/readme/DESCRIPTION.rst @@ -0,0 +1,14 @@ +Compatibility layer between Stock Vertical Lift and Putaway Storage Types (OCA/wms). + +In the vertical lift's Putaway screen, when a good is scanned for a putaway, the +user has to scan the tray type of the corresponding size, so an empty place in a +matching tray is found. When we use storage types, we should know what tray is +compatible with the storage type. + +Changes with this module: + +* The storage types of trays cannot be selected in the locations form, they have + to be set in the Tray types. +* In the lift put-away screen, when a package has a storage type, the user isn't + asked to scan a tray type, instead, the putaway of the Package Storage Type is + applied. diff --git a/stock_vertical_lift_storage_type/tests/__init__.py b/stock_vertical_lift_storage_type/tests/__init__.py new file mode 100644 index 000000000..91db0a261 --- /dev/null +++ b/stock_vertical_lift_storage_type/tests/__init__.py @@ -0,0 +1,3 @@ +from . import test_put +from . import test_stock_location +from . import test_tray_type diff --git a/stock_vertical_lift_storage_type/tests/common.py b/stock_vertical_lift_storage_type/tests/common.py new file mode 100644 index 000000000..8948f7c30 --- /dev/null +++ b/stock_vertical_lift_storage_type/tests/common.py @@ -0,0 +1,27 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.stock_vertical_lift.tests.common import VerticalLiftCase + + +class TrayTypeCommonCase(VerticalLiftCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.TrayType = cls.env["stock.location.tray.type"] + cls.location_2b = cls.env.ref( + "stock_vertical_lift." "stock_location_vertical_lift_demo_tray_2b" + ) + cls.location_2d = cls.env.ref( + "stock_vertical_lift." "stock_location_vertical_lift_demo_tray_2d" + ) + LocStorageType = cls.env["stock.location.storage.type"] + cls.location_storage_type_buffer = LocStorageType.create( + {"name": "VLift Buffer"} + ) + cls.location_storage_type_small_8x = LocStorageType.create( + {"name": "Small 8x", "only_empty": True} + ) + cls.storage_types = ( + cls.location_storage_type_small_8x | cls.location_storage_type_buffer + ) diff --git a/stock_vertical_lift_storage_type/tests/test_put.py b/stock_vertical_lift_storage_type/tests/test_put.py new file mode 100644 index 000000000..083266d12 --- /dev/null +++ b/stock_vertical_lift_storage_type/tests/test_put.py @@ -0,0 +1,120 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.stock_vertical_lift.tests.common import VerticalLiftCase + + +class TestPut(VerticalLiftCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.wh = cls.env.ref("stock.warehouse0") + cls.wh.wh_input_stock_loc_id.active = True + cls.wh.int_type_id.active = True + + # used on the vertical lift top level + LocStorageType = cls.env["stock.location.storage.type"] + cls.location_storage_type_buffer = LocStorageType.create( + {"name": "VLift Buffer"} + ) + cls.location_storage_type_small_8x = LocStorageType.create( + {"name": "Small 8x", "only_empty": True} + ) + + # storage type used for Tray 1A + PackageStorageType = cls.env["stock.package.storage.type"] + cls.package_storage_type_small_8x = PackageStorageType.create( + { + "name": "Small 8x", + "location_storage_type_ids": [ + (4, cls.location_storage_type_small_8x.id), + (4, cls.location_storage_type_buffer.id), + ], + } + ) + + cls.location_shuttle1 = cls.shuttle.location_id + cls.vertical_lift_loc.location_storage_type_ids = ( + cls.location_storage_type_buffer + ) + cls.vertical_lift_loc.pack_putaway_strategy = "none" + cls.location_shuttle1.location_storage_type_ids = ( + cls.location_storage_type_small_8x + ) + cls.location_shuttle1.pack_putaway_strategy = "ordered_locations" + + cls.env["stock.storage.location.sequence"].create( + { + "package_storage_type_id": cls.package_storage_type_small_8x.id, + "sequence": 1, + "location_id": cls.vertical_lift_loc.id, + } + ) + cls.env["stock.storage.location.sequence"].create( + { + "package_storage_type_id": cls.package_storage_type_small_8x.id, + "sequence": 2, + "location_id": cls.location_shuttle1.id, + } + ) + + cls.package = cls.env["stock.quant.package"].create( + {"package_storage_type_id": cls.package_storage_type_small_8x.id} + ) + cls._update_qty_in_location( + cls.wh.wh_input_stock_loc_id, cls.product_socks, 10, package=cls.package + ) + + cls.int_picking = cls._create_simple_picking_int( + cls.product_socks, 10, cls.vertical_lift_loc + ) + cls.int_picking.action_confirm() + cls.int_picking.action_assign() + + @classmethod + def _create_simple_picking_int(cls, product, quantity, dest_location): + return cls.env["stock.picking"].create( + { + "picking_type_id": cls.wh.int_type_id.id, + "location_id": cls.wh.wh_input_stock_loc_id.id, + "location_dest_id": dest_location.id, + "move_lines": [ + ( + 0, + 0, + { + "name": product.name, + "product_id": product.id, + "product_uom": product.uom_id.id, + "product_uom_qty": quantity, + "picking_type_id": cls.wh.int_type_id.id, + "location_id": cls.wh.wh_input_stock_loc_id.id, + "location_dest_id": dest_location.id, + }, + ) + ], + } + ) + + def test_storage_type_put_away(self): + 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) + # 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) + + # 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") diff --git a/stock_vertical_lift_storage_type/tests/test_stock_location.py b/stock_vertical_lift_storage_type/tests/test_stock_location.py new file mode 100644 index 000000000..082ac1e49 --- /dev/null +++ b/stock_vertical_lift_storage_type/tests/test_stock_location.py @@ -0,0 +1,90 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import exceptions + +from .common import TrayTypeCommonCase + + +class TestTrayTypeLocation(TrayTypeCommonCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.tray_type = cls.env.ref( + "stock_location_tray.stock_location_tray_type_small_8x" + ) + cls.tray_type.write( + {"location_storage_type_ids": [(6, 0, cls.storage_types.ids)]} + ) + cls.locations = cls.location_2a | cls.location_2b + + def test_location_create_sync(self): + locations = self.env["stock.location"].create( + [ + { + "name": "tray test 1", + "location_id": self.shuttle.location_id.id, + "usage": "internal", + "tray_type_id": self.tray_type.id, + }, + { + "name": "tray test 2", + "location_id": self.shuttle.location_id.id, + "usage": "internal", + "tray_type_id": self.tray_type.id, + }, + ] + ) + self.assertEqual(locations[0].location_storage_type_ids, self.storage_types) + self.assertEqual(locations[1].location_storage_type_ids, self.storage_types) + + def test_location_write_sync(self): + self.locations.tray_type_id = self.tray_type + self.assertEqual(self.location_2a.location_storage_type_ids, self.storage_types) + self.assertEqual(self.location_2b.location_storage_type_ids, self.storage_types) + + def test_location_create_error(self): + with self.assertRaisesRegex(exceptions.UserError, "Error creating.*"): + self.env["stock.location"].create( + [ + { + "name": "tray test 1", + "location_id": self.shuttle.location_id.id, + "usage": "internal", + "tray_type_id": self.tray_type.id, + "location_storage_type_ids": [ + (6, 0, self.location_storage_type_buffer.ids) + ], + }, + { + "name": "tray test 2", + "location_id": self.shuttle.location_id.id, + "usage": "internal", + "tray_type_id": self.tray_type.id, + "location_storage_type_ids": [ + (6, 0, self.location_storage_type_buffer.ids) + ], + }, + ] + ) + + def test_location_write_both_fields_error(self): + with self.assertRaisesRegex(exceptions.UserError, "Error updating.*"): + self.locations.write( + { + "tray_type_id": self.tray_type.id, + "location_storage_type_ids": [ + (6, 0, self.location_storage_type_buffer.ids) + ], + } + ) + + def test_location_write_storage_type_error(self): + with self.assertRaisesRegex(exceptions.UserError, "Error updating.*"): + self.locations.write( + { + "location_storage_type_ids": [ + (6, 0, self.location_storage_type_buffer.ids) + ], + } + ) diff --git a/stock_vertical_lift_storage_type/tests/test_tray_type.py b/stock_vertical_lift_storage_type/tests/test_tray_type.py new file mode 100644 index 000000000..81559dce1 --- /dev/null +++ b/stock_vertical_lift_storage_type/tests/test_tray_type.py @@ -0,0 +1,38 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from .common import TrayTypeCommonCase + + +class TestTrayType(TrayTypeCommonCase): + def test_tray_type_write_sync(self): + # both tray 1A and tray 2D use stock_location_tray_type_small_8x + tray_type = self.env.ref( + "stock_location_tray.stock_location_tray_type_small_8x" + ) + + tray_type.write({"location_storage_type_ids": [(6, 0, self.storage_types.ids)]}) + + self.assertEqual(self.location_1a.location_storage_type_ids, self.storage_types) + self.assertEqual(self.location_2d.location_storage_type_ids, self.storage_types) + + def test_location_create_sync(self): + # both tray 1A and tray 2D use stock_location_tray_type_small_8x + tray_type = self.env.ref( + "stock_location_tray.stock_location_tray_type_small_8x" + ) + tray_type.write({"location_storage_type_ids": [(6, 0, self.storage_types.ids)]}) + + locations = self.location_2a | self.location_2b + + self.assertNotEqual( + self.location_2a.location_storage_type_ids, self.storage_types + ) + self.assertNotEqual( + self.location_2b.location_storage_type_ids, self.storage_types + ) + + locations.tray_type_id = tray_type + + self.assertEqual(self.location_2a.location_storage_type_ids, self.storage_types) + self.assertEqual(self.location_2b.location_storage_type_ids, self.storage_types) diff --git a/stock_vertical_lift_storage_type/views/stock_location_tray_type_views.xml b/stock_vertical_lift_storage_type/views/stock_location_tray_type_views.xml new file mode 100644 index 000000000..008a213da --- /dev/null +++ b/stock_vertical_lift_storage_type/views/stock_location_tray_type_views.xml @@ -0,0 +1,18 @@ + + + + stock.location.tray.type.form + stock.location.tray.type + + + + + + + + + + diff --git a/stock_vertical_lift_storage_type/views/stock_location_views.xml b/stock_vertical_lift_storage_type/views/stock_location_views.xml new file mode 100644 index 000000000..8b1c5232d --- /dev/null +++ b/stock_vertical_lift_storage_type/views/stock_location_views.xml @@ -0,0 +1,15 @@ + + + + stock.location.form.vertical.lift.storage.type + stock.location + + + + {"readonly": [("vertical_lift_kind", "in", ("tray", "cell"))]} + + + + From e83ea033c261bca0e809905af9af7b4dec63cbfb Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 3 Jul 2020 08:13:20 +0200 Subject: [PATCH 2/5] 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") From 2381bf2edd63d4c53531569ebcf986f1d04337ae Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 3 Jul 2020 10:11:53 +0200 Subject: [PATCH 3/5] Ignore storage types constraints on quants in cells When the putaway selects a tray and a cell according to the storage type, we still want the user to be able to override the selection by scanning another tray type. --- stock_vertical_lift_storage_type/README.rst | 91 ++++ .../i18n/stock_vertical_lift_storage_type.pot | 68 +++ .../models/__init__.py | 1 + .../models/stock_quant.py | 18 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 437 ++++++++++++++++++ 6 files changed, 615 insertions(+) create mode 100644 stock_vertical_lift_storage_type/README.rst create mode 100644 stock_vertical_lift_storage_type/i18n/stock_vertical_lift_storage_type.pot create mode 100644 stock_vertical_lift_storage_type/models/stock_quant.py create mode 100644 stock_vertical_lift_storage_type/static/description/icon.png create mode 100644 stock_vertical_lift_storage_type/static/description/index.html diff --git a/stock_vertical_lift_storage_type/README.rst b/stock_vertical_lift_storage_type/README.rst new file mode 100644 index 000000000..e6c067846 --- /dev/null +++ b/stock_vertical_lift_storage_type/README.rst @@ -0,0 +1,91 @@ +============================ +Vertical Lift - Storage Type +============================ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github + :target: https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_vertical_lift_storage_type + :alt: OCA/stock-logistics-warehouse +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-13-0/stock-logistics-warehouse-13-0-stock_vertical_lift_storage_type + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/153/13.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Compatibility layer between Stock Vertical Lift and Putaway Storage Types (OCA/wms). + +In the vertical lift's Putaway screen, when a good is scanned for a putaway, the +user has to scan the tray type of the corresponding size, so an empty place in a +matching tray is found. When we use storage types, we should know what tray is +compatible with the storage type. + +Changes with this module: + +* The storage types of trays cannot be selected in the locations form, they have + to be set in the Tray types. +* In the lift put-away screen, when a package has a storage type, the user isn't + asked to scan a tray type, instead, the putaway of the Package Storage Type is + applied. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* Guewen Baconnier + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/stock-logistics-warehouse `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_vertical_lift_storage_type/i18n/stock_vertical_lift_storage_type.pot b/stock_vertical_lift_storage_type/i18n/stock_vertical_lift_storage_type.pot new file mode 100644 index 000000000..d4546841e --- /dev/null +++ b/stock_vertical_lift_storage_type/i18n/stock_vertical_lift_storage_type.pot @@ -0,0 +1,68 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_vertical_lift_storage_type +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: stock_vertical_lift_storage_type +#: code:addons/stock_vertical_lift_storage_type/models/stock_location.py:0 +#, python-format +msgid "" +"Error creating '{}': Location storage type must be set on the tray type" +msgstr "" + +#. module: stock_vertical_lift_storage_type +#: code:addons/stock_vertical_lift_storage_type/models/stock_location.py:0 +#, python-format +msgid "Error updating {}: Location storage type must be set on the tray type" +msgstr "" + +#. module: stock_vertical_lift_storage_type +#: model:ir.model,name:stock_vertical_lift_storage_type.model_stock_location +msgid "Inventory Locations" +msgstr "" + +#. module: stock_vertical_lift_storage_type +#: model:ir.model.fields,field_description:stock_vertical_lift_storage_type.field_stock_location_tray_type__location_storage_type_ids +msgid "Location Storage Type" +msgstr "" + +#. module: stock_vertical_lift_storage_type +#: model:ir.model.fields,help:stock_vertical_lift_storage_type.field_stock_location_tray_type__location_storage_type_ids +msgid "Location storage types applied on the location using this tray type." +msgstr "" + +#. module: stock_vertical_lift_storage_type +#: code:addons/stock_vertical_lift_storage_type/models/vertical_lift_operation_put.py:0 +#, python-format +msgid "No free space found for storage type '{}' in shuttle '{}'" +msgstr "" + +#. module: stock_vertical_lift_storage_type +#: model_terms:ir.ui.view,arch_db:stock_vertical_lift_storage_type.view_stock_location_tray_type_form +msgid "Put-Away" +msgstr "" + +#. module: stock_vertical_lift_storage_type +#: model:ir.model,name:stock_vertical_lift_storage_type.model_stock_quant +msgid "Quants" +msgstr "" + +#. module: stock_vertical_lift_storage_type +#: model:ir.model,name:stock_vertical_lift_storage_type.model_stock_location_tray_type +msgid "Stock Location Tray Type" +msgstr "" + +#. module: stock_vertical_lift_storage_type +#: model:ir.model,name:stock_vertical_lift_storage_type.model_vertical_lift_operation_put +msgid "Vertical Lift Operation Put" +msgstr "" diff --git a/stock_vertical_lift_storage_type/models/__init__.py b/stock_vertical_lift_storage_type/models/__init__.py index 029c1ede2..cb2169a09 100644 --- a/stock_vertical_lift_storage_type/models/__init__.py +++ b/stock_vertical_lift_storage_type/models/__init__.py @@ -1,3 +1,4 @@ from . import stock_location from . import stock_location_tray_type +from . import stock_quant from . import vertical_lift_operation_put diff --git a/stock_vertical_lift_storage_type/models/stock_quant.py b/stock_vertical_lift_storage_type/models/stock_quant.py new file mode 100644 index 000000000..90994ee81 --- /dev/null +++ b/stock_vertical_lift_storage_type/models/stock_quant.py @@ -0,0 +1,18 @@ +# Copyright 2020 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo import models + + +class StockQuant(models.Model): + + _inherit = "stock.quant" + + def _check_storage_type(self): + # disable the checks when placing goods in vertical lift cells: + # we still want to benefit of the storage type rules to select + # a destination, but we want to allow selecting a different tray + # type + self = self.filtered( + lambda quant: quant.location_id.vertical_lift_kind != "cell" + ) + super()._check_storage_type() diff --git a/stock_vertical_lift_storage_type/static/description/icon.png b/stock_vertical_lift_storage_type/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/stock_vertical_lift_storage_type/static/description/index.html b/stock_vertical_lift_storage_type/static/description/index.html new file mode 100644 index 000000000..c1519eb58 --- /dev/null +++ b/stock_vertical_lift_storage_type/static/description/index.html @@ -0,0 +1,437 @@ + + + + + + +Vertical Lift - Storage Type + + + +
+

Vertical Lift - Storage Type

+ + +

Alpha License: AGPL-3 OCA/stock-logistics-warehouse Translate me on Weblate Try me on Runbot

+

Compatibility layer between Stock Vertical Lift and Putaway Storage Types (OCA/wms).

+

In the vertical lift’s Putaway screen, when a good is scanned for a putaway, the +user has to scan the tray type of the corresponding size, so an empty place in a +matching tray is found. When we use storage types, we should know what tray is +compatible with the storage type.

+

Changes with this module:

+
    +
  • The storage types of trays cannot be selected in the locations form, they have +to be set in the Tray types.
  • +
  • In the lift put-away screen, when a package has a storage type, the user isn’t +asked to scan a tray type, instead, the putaway of the Package Storage Type is +applied.
  • +
+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/stock-logistics-warehouse project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + From aa3ab567661843d139f66f14f8807ccf2d5f86f2 Mon Sep 17 00:00:00 2001 From: Hai Lang Date: Mon, 20 Sep 2021 22:25:17 +0700 Subject: [PATCH 4/5] [IMP] stock_vertical_lift_storage_type: black, isort, prettier --- .../odoo/addons/stock_vertical_lift_storage_type | 1 + setup/stock_vertical_lift_storage_type/setup.py | 6 ++++++ 2 files changed, 7 insertions(+) create mode 120000 setup/stock_vertical_lift_storage_type/odoo/addons/stock_vertical_lift_storage_type create mode 100644 setup/stock_vertical_lift_storage_type/setup.py diff --git a/setup/stock_vertical_lift_storage_type/odoo/addons/stock_vertical_lift_storage_type b/setup/stock_vertical_lift_storage_type/odoo/addons/stock_vertical_lift_storage_type new file mode 120000 index 000000000..fc17491a1 --- /dev/null +++ b/setup/stock_vertical_lift_storage_type/odoo/addons/stock_vertical_lift_storage_type @@ -0,0 +1 @@ +../../../../stock_vertical_lift_storage_type \ No newline at end of file diff --git a/setup/stock_vertical_lift_storage_type/setup.py b/setup/stock_vertical_lift_storage_type/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/stock_vertical_lift_storage_type/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From bbbfb6c1afd6d525a5d12056d432d0be9d5288ef Mon Sep 17 00:00:00 2001 From: Hai Lang Date: Tue, 21 Sep 2021 05:57:58 +0700 Subject: [PATCH 5/5] [MIG] stock_vertical_lift_storage_type: Migration to 14.0 --- stock_vertical_lift_storage_type/README.rst | 16 +++++++++++----- .../__manifest__.py | 2 +- .../readme/CONTRIBUTORS.rst | 1 + .../readme/CREDITS.rst | 1 + .../static/description/index.html | 18 ++++++++++++------ 5 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 stock_vertical_lift_storage_type/readme/CREDITS.rst diff --git a/stock_vertical_lift_storage_type/README.rst b/stock_vertical_lift_storage_type/README.rst index e6c067846..7e11f81dd 100644 --- a/stock_vertical_lift_storage_type/README.rst +++ b/stock_vertical_lift_storage_type/README.rst @@ -14,13 +14,13 @@ Vertical Lift - Storage Type :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github - :target: https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_vertical_lift_storage_type + :target: https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_vertical_lift_storage_type :alt: OCA/stock-logistics-warehouse .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-13-0/stock-logistics-warehouse-13-0-stock_vertical_lift_storage_type + :target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-14-0/stock-logistics-warehouse-14-0-stock_vertical_lift_storage_type :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/153/13.0 + :target: https://runbot.odoo-community.org/runbot/153/14.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -56,7 +56,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -72,6 +72,12 @@ Contributors ~~~~~~~~~~~~ * Guewen Baconnier +* `Trobz `_: + +Other credits +~~~~~~~~~~~~~ + +The migration of this module from 13.0 to 14.0 was financially supported by Camptocamp Maintainers ~~~~~~~~~~~ @@ -86,6 +92,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -This module is part of the `OCA/stock-logistics-warehouse `_ project on GitHub. +This module is part of the `OCA/stock-logistics-warehouse `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_vertical_lift_storage_type/__manifest__.py b/stock_vertical_lift_storage_type/__manifest__.py index 99d91fd3e..e91ffacd9 100644 --- a/stock_vertical_lift_storage_type/__manifest__.py +++ b/stock_vertical_lift_storage_type/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Vertical Lift - Storage Type", "summary": "Compatibility layer for storage types on vertical lifts", - "version": "13.0.1.0.0", + "version": "14.0.1.0.0", "category": "Stock", "author": "Camptocamp, Odoo Community Association (OCA)", "license": "AGPL-3", diff --git a/stock_vertical_lift_storage_type/readme/CONTRIBUTORS.rst b/stock_vertical_lift_storage_type/readme/CONTRIBUTORS.rst index 48286263c..dc8669b25 100644 --- a/stock_vertical_lift_storage_type/readme/CONTRIBUTORS.rst +++ b/stock_vertical_lift_storage_type/readme/CONTRIBUTORS.rst @@ -1 +1,2 @@ * Guewen Baconnier +* `Trobz `_: diff --git a/stock_vertical_lift_storage_type/readme/CREDITS.rst b/stock_vertical_lift_storage_type/readme/CREDITS.rst new file mode 100644 index 000000000..f37ebe757 --- /dev/null +++ b/stock_vertical_lift_storage_type/readme/CREDITS.rst @@ -0,0 +1 @@ +The migration of this module from 13.0 to 14.0 was financially supported by Camptocamp diff --git a/stock_vertical_lift_storage_type/static/description/index.html b/stock_vertical_lift_storage_type/static/description/index.html index c1519eb58..ecbc37765 100644 --- a/stock_vertical_lift_storage_type/static/description/index.html +++ b/stock_vertical_lift_storage_type/static/description/index.html @@ -3,7 +3,7 @@ - + Vertical Lift - Storage Type