From 310337371fa8fb1bbf801fb6dc30d4c8600635e6 Mon Sep 17 00:00:00 2001 From: Carlos Serra-Toro Date: Thu, 11 Mar 2021 18:02:50 +0100 Subject: [PATCH] [ADD] stock_vertical_lift_empty_tray_check: is the tray empty? MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A vertical lift retrieves a tray and places it in front of the user, and depending on the quantity the user takes from it, it adapts the pending quantity in the tray. However, because of errors, it could be that the system thinks the tray is empty while it is not. With this module, when the system thinks the tray is empty, while in the step for the release of the tray the operator is asked explicitly to check if the tray is actually empty. Depending on his/her answer (yes/no) an inventory adjustment is created stating the situation. To activate this optional feature, a new configuration setting has been added to Inventory > Configuration > Settings, named 'Check Empty Tray'. It is deactivated by default. Developing decisions: - The screens shown to the operator are actually wizards, but since in the original module (`stock_vertical_lift`) they were considered (on the source tree) as views, this has been continued here. - It has been decided, to not change the current workflow of the operators, to embed the new check inside the step for the 'release'. So, a new screen is shown to ask for the visual inspection of whether the tray is empty. In order to test this easily, the method `button_release` of the module `stock_vertical_lift` has been slightly modified so that it always returns. This way we can check easily in the unit-tests for the outcome of the intermediate screen (i.e. wizard) ─ similarly to how it is done when validating a picking that can result in a backorder. --- .../README.rst | 90 ++++ .../__init__.py | 3 + .../__manifest__.py | 18 + .../stock_vertical_lift_empty_tray_check.pot | 94 ++++ .../models/__init__.py | 5 + .../models/res_config_settings.py | 11 + .../models/vertical_lift_operation_pick.py | 46 ++ ...vertical_lift_operation_pick_zero_check.py | 87 ++++ .../readme/CONFIGURE.rst | 4 + .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 5 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 440 ++++++++++++++++++ .../tests/__init__.py | 3 + .../tests/test_pick.py | 84 ++++ .../views/res_config_setting_views.xml | 31 ++ ...l_lift_operation_pick_zero_check_views.xml | 34 ++ 17 files changed, 956 insertions(+) create mode 100644 stock_vertical_lift_empty_tray_check/README.rst create mode 100644 stock_vertical_lift_empty_tray_check/__init__.py create mode 100644 stock_vertical_lift_empty_tray_check/__manifest__.py create mode 100644 stock_vertical_lift_empty_tray_check/i18n/stock_vertical_lift_empty_tray_check.pot create mode 100644 stock_vertical_lift_empty_tray_check/models/__init__.py create mode 100644 stock_vertical_lift_empty_tray_check/models/res_config_settings.py create mode 100644 stock_vertical_lift_empty_tray_check/models/vertical_lift_operation_pick.py create mode 100644 stock_vertical_lift_empty_tray_check/models/vertical_lift_operation_pick_zero_check.py create mode 100644 stock_vertical_lift_empty_tray_check/readme/CONFIGURE.rst create mode 100644 stock_vertical_lift_empty_tray_check/readme/CONTRIBUTORS.rst create mode 100644 stock_vertical_lift_empty_tray_check/readme/DESCRIPTION.rst create mode 100644 stock_vertical_lift_empty_tray_check/static/description/icon.png create mode 100644 stock_vertical_lift_empty_tray_check/static/description/index.html create mode 100644 stock_vertical_lift_empty_tray_check/tests/__init__.py create mode 100644 stock_vertical_lift_empty_tray_check/tests/test_pick.py create mode 100644 stock_vertical_lift_empty_tray_check/views/res_config_setting_views.xml create mode 100644 stock_vertical_lift_empty_tray_check/views/vertical_lift_operation_pick_zero_check_views.xml diff --git a/stock_vertical_lift_empty_tray_check/README.rst b/stock_vertical_lift_empty_tray_check/README.rst new file mode 100644 index 000000000..4a5d94e60 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/README.rst @@ -0,0 +1,90 @@ +============================== +Vertical Lift Empty Tray Check +============================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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_empty_tray_check + :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_empty_tray_check + :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| + +When a tray is released, and the system thinks it is empty, +it prompts the user to actually check that it is empty or not. +In any case, an inventory adjustment is done stating the +situation: posted to zero if the tray is actually empty, and +set to draft is it is not empty. + +.. 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: + +Configuration +============= + +General +~~~~~~~ + +In Inventory Settings, you must have activated the option: *Check Empty Tray* + +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 +~~~~~~~~~~~~ + +* Carlos Serra-Toro + +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_empty_tray_check/__init__.py b/stock_vertical_lift_empty_tray_check/__init__.py new file mode 100644 index 000000000..d4b7188d6 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +from . import models diff --git a/stock_vertical_lift_empty_tray_check/__manifest__.py b/stock_vertical_lift_empty_tray_check/__manifest__.py new file mode 100644 index 000000000..3e3c2930c --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +{ + "name": "Vertical Lift Empty Tray Check", + "summary": "Checks if the tray is actually empty.", + "version": "13.0.1.1.0", + "category": "Stock", + "author": "Camptocamp, Odoo Community Association (OCA)", + "license": "AGPL-3", + "depends": ["stock", "stock_vertical_lift"], + "website": "https://github.com/OCA/stock-logistics-warehouse", + "data": [ + "views/res_config_setting_views.xml", + "views/vertical_lift_operation_pick_zero_check_views.xml", + ], + "installable": True, + "development_status": "Alpha", +} diff --git a/stock_vertical_lift_empty_tray_check/i18n/stock_vertical_lift_empty_tray_check.pot b/stock_vertical_lift_empty_tray_check/i18n/stock_vertical_lift_empty_tray_check.pot new file mode 100644 index 000000000..eeb8ca461 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/i18n/stock_vertical_lift_empty_tray_check.pot @@ -0,0 +1,94 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_vertical_lift_empty_tray_check +# +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_empty_tray_check +#: model:ir.model,name:stock_vertical_lift_empty_tray_check.model_res_config_settings +msgid "Config Settings" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model.fields,field_description:stock_vertical_lift_empty_tray_check.field_vertical_lift_operation_pick_zero_check__create_uid +msgid "Created by" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model.fields,field_description:stock_vertical_lift_empty_tray_check.field_vertical_lift_operation_pick_zero_check__create_date +msgid "Created on" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model.fields,field_description:stock_vertical_lift_empty_tray_check.field_vertical_lift_operation_pick_zero_check__display_name +msgid "Display Name" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model.fields,field_description:stock_vertical_lift_empty_tray_check.field_vertical_lift_operation_pick_zero_check__id +msgid "ID" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model_terms:ir.ui.view,arch_db:stock_vertical_lift_empty_tray_check.res_config_settings_view_form +msgid "" +"If checked and the system thinks the vertical tray is\n" +" empty, the operator will be asked to explicitly check\n" +" if this is the case or not" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: code:addons/stock_vertical_lift_empty_tray_check/models/vertical_lift_operation_pick.py:0 +#, python-format +msgid "Is the tray empty?" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model.fields,field_description:stock_vertical_lift_empty_tray_check.field_vertical_lift_operation_pick_zero_check____last_update +msgid "Last Modified on" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model.fields,field_description:stock_vertical_lift_empty_tray_check.field_vertical_lift_operation_pick_zero_check__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model.fields,field_description:stock_vertical_lift_empty_tray_check.field_vertical_lift_operation_pick_zero_check__write_date +msgid "Last Updated on" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model,name:stock_vertical_lift_empty_tray_check.model_vertical_lift_operation_pick_zero_check +msgid "Make sure the tray location is empty" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model_terms:ir.ui.view,arch_db:stock_vertical_lift_empty_tray_check.vertical_lift_operation_pick_zero_check_view_form +msgid "Tray Empty" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model_terms:ir.ui.view,arch_db:stock_vertical_lift_empty_tray_check.vertical_lift_operation_pick_zero_check_view_form +msgid "Tray Not Empty" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model,name:stock_vertical_lift_empty_tray_check.model_vertical_lift_operation_pick +#: model:ir.model.fields,field_description:stock_vertical_lift_empty_tray_check.field_vertical_lift_operation_pick_zero_check__vertical_lift_operation_pick_id +msgid "Vertical Lift Operation Pick" +msgstr "" + +#. module: stock_vertical_lift_empty_tray_check +#: model:ir.model.fields,field_description:stock_vertical_lift_empty_tray_check.field_res_config_settings__vertical_lift_empty_tray_check +msgid "Vertical lift: Check Empty Tray" +msgstr "" diff --git a/stock_vertical_lift_empty_tray_check/models/__init__.py b/stock_vertical_lift_empty_tray_check/models/__init__.py new file mode 100644 index 000000000..18af90d40 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/models/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from . import res_config_settings +from . import vertical_lift_operation_pick +from . import vertical_lift_operation_pick_zero_check diff --git a/stock_vertical_lift_empty_tray_check/models/res_config_settings.py b/stock_vertical_lift_empty_tray_check/models/res_config_settings.py new file mode 100644 index 000000000..b780f51f1 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/models/res_config_settings.py @@ -0,0 +1,11 @@ +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + vertical_lift_empty_tray_check = fields.Boolean( + "Vertical lift: Check Empty Tray", + default=False, + config_parameter="vertical_lift_empty_tray_check", + ) diff --git a/stock_vertical_lift_empty_tray_check/models/vertical_lift_operation_pick.py b/stock_vertical_lift_empty_tray_check/models/vertical_lift_operation_pick.py new file mode 100644 index 000000000..ace54dc24 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/models/vertical_lift_operation_pick.py @@ -0,0 +1,46 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, models +from odoo.tools import float_is_zero + + +class VerticalLiftOperationPick(models.Model): + _inherit = "vertical.lift.operation.pick" + + def button_release(self): + """Release the operation, go to the next + + By default it asks the user to inspect visually if the tray is empty. + """ + icp = self.env["ir.config_parameter"].sudo() + tray_check = icp.get_param("vertical_lift_empty_tray_check") + skip_zero_quantity_check = self.env.context.get("skip_zero_quantity_check") + if not skip_zero_quantity_check and tray_check: + uom_rounding = self.product_id.uom_id.rounding + if float_is_zero(self.tray_qty, precision_rounding=uom_rounding): + return self._check_zero_quantity() + + return super().button_release() + + def _check_zero_quantity(self): + """Show the wizard to check for real-zero quantity.""" + view = self.env.ref( + "stock_vertical_lift_empty_tray_check." + "vertical_lift_operation_pick_zero_check_view_form" + ) + wizard_model = "vertical.lift.operation.pick.zero.check" + wizard = self.env[wizard_model].create( + {"vertical_lift_operation_pick_id": self.id} + ) + return { + "name": _("Is the tray empty?"), + "type": "ir.actions.act_window", + "view_mode": "form", + "target": "new", + "views": [(view.id, "form")], + "view_id": view.id, + "res_model": wizard_model, + "res_id": wizard.id, + "context": self.env.context, + } diff --git a/stock_vertical_lift_empty_tray_check/models/vertical_lift_operation_pick_zero_check.py b/stock_vertical_lift_empty_tray_check/models/vertical_lift_operation_pick_zero_check.py new file mode 100644 index 000000000..ff34ffcf8 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/models/vertical_lift_operation_pick_zero_check.py @@ -0,0 +1,87 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +from odoo import _, fields, models + + +class VerticalLiftOperationPickZeroCheck(models.TransientModel): + _name = "vertical.lift.operation.pick.zero.check" + _description = "Make sure the tray location is empty" + + vertical_lift_operation_pick_id = fields.Many2one("vertical.lift.operation.pick") + + def _get_data_from_operation(self): + """Return picking, location and product from the operation shuttle""" + operation = self.vertical_lift_operation_pick_id + + # If the move is split into several move lines, it is + # moved to another picking, being a backorder of the + # original one. We are always interested in the original + # picking that was processed at first, so if the picking + # is a backorder of another picking, we take that other one. + picking = operation.picking_id.backorder_id or operation.picking_id + location = operation.current_move_line_id.location_id + product = operation.product_id + return operation, picking, location, product + + def button_confirm_empty(self): + """User confirms the tray location is empty + + This is in accordance with what we expected, because we only + call this action if we think the location is empty. We create + an inventory adjustment that states that a zero-check was + done for this location.""" + operation, picking, location, product = self._get_data_from_operation() + inventory_name = _(f"Zero check in location: {location.complete_name}") + inventory = ( + self.env["stock.inventory"] + .sudo() + .create( + { + "name": inventory_name, + "product_ids": [(4, product.id)], + "location_ids": [(4, location.id)], + "line_ids": [ + ( + 0, + 0, + { + "product_id": product.id, + "product_qty": 0, + "theoretical_qty": 0, + "location_id": location.id, + }, + ), + ], + } + ) + ) + inventory.action_start() + inventory.action_validate() + + # Return to the execution of the release, + # but without checking again if the tray is empty. + return operation.with_context(skip_zero_quantity_check=True).button_release() + + def button_confirm_not_empty(self): + """User confirms the tray location is not empty + + This contradicts what we expected, because we only call this + action if we think the location is empty. We create a draft + inventory adjustment stating the mismatch. + """ + operation, picking, location, product = self._get_data_from_operation() + inventory_name = _( + f"{picking.name} zero check issue on location {location.complete_name}" + ) + self.env["stock.inventory"].sudo().create( + { + "name": inventory_name, + "product_ids": [(4, product.id)], + "location_ids": [(4, location.id)], + } + ) + + # Return to the execution of the release, + # but without checking again if the tray is empty. + return operation.with_context(skip_zero_quantity_check=True).button_release() diff --git a/stock_vertical_lift_empty_tray_check/readme/CONFIGURE.rst b/stock_vertical_lift_empty_tray_check/readme/CONFIGURE.rst new file mode 100644 index 000000000..546cb6842 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +General +~~~~~~~ + +In Inventory Settings, you must have activated the option: *Check Empty Tray* diff --git a/stock_vertical_lift_empty_tray_check/readme/CONTRIBUTORS.rst b/stock_vertical_lift_empty_tray_check/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..1d2f3d485 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Carlos Serra-Toro diff --git a/stock_vertical_lift_empty_tray_check/readme/DESCRIPTION.rst b/stock_vertical_lift_empty_tray_check/readme/DESCRIPTION.rst new file mode 100644 index 000000000..858f08c0b --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +When a tray is released, and the system thinks it is empty, +it prompts the user to actually check that it is empty or not. +In any case, an inventory adjustment is done stating the +situation: posted to zero if the tray is actually empty, and +set to draft is it is not empty. diff --git a/stock_vertical_lift_empty_tray_check/static/description/icon.png b/stock_vertical_lift_empty_tray_check/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_empty_tray_check/static/description/index.html b/stock_vertical_lift_empty_tray_check/static/description/index.html new file mode 100644 index 000000000..1ab8336c0 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/static/description/index.html @@ -0,0 +1,440 @@ + + + + + + +Vertical Lift Empty Tray Check + + + +
+

Vertical Lift Empty Tray Check

+ + +

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

+

When a tray is released, and the system thinks it is empty, +it prompts the user to actually check that it is empty or not. +In any case, an inventory adjustment is done stating the +situation: posted to zero if the tray is actually empty, and +set to draft is it is not empty.

+
+

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

+ +
+

Configuration

+
+

General

+

In Inventory Settings, you must have activated the option: Check Empty Tray

+
+
+
+

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.

+
+
+
+ + diff --git a/stock_vertical_lift_empty_tray_check/tests/__init__.py b/stock_vertical_lift_empty_tray_check/tests/__init__.py new file mode 100644 index 000000000..b4a9152b6 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) +from . import test_pick diff --git a/stock_vertical_lift_empty_tray_check/tests/test_pick.py b/stock_vertical_lift_empty_tray_check/tests/test_pick.py new file mode 100644 index 000000000..b0fed13d6 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/tests/test_pick.py @@ -0,0 +1,84 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + +from odoo.addons.stock_vertical_lift.tests.common import VerticalLiftCase + + +class TestPick(VerticalLiftCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.picking_out = cls.env.ref( + "stock_vertical_lift.stock_picking_out_demo_vertical_lift_1" + ) + cls.env["ir.config_parameter"].sudo().set_param( + "vertical_lift_empty_tray_check", True + ) + + def _test_location_empty_common(self, operation, tray_is_empty): + """Common part for tests checking the tray location is empty + + Returns the new inventory adjustment created.""" + self.assertEqual(operation.state, "scan_destination") + move_line = operation.current_move_line_id + customers_location = self.env.ref("stock.stock_location_customers") + customers_location.barcode = "CUSTOMERS" + operation.on_barcode_scanned(customers_location.barcode) + self.assertEqual(move_line.location_dest_id, customers_location) + self.assertEqual(operation.state, "save") + operation.button_save() + self.assertEqual(operation.state, "release") + self.assertEqual(operation.tray_qty, 0) + + old_inventories = self.env["stock.inventory"].search([]) + + res_dict = operation.button_release() + wizard = self.env[(res_dict.get("res_model"))].browse(res_dict.get("res_id")) + wizard = wizard.with_context( + active_id=operation.id, active_model=operation._name + ) + if tray_is_empty: + wizard.button_confirm_empty() + else: + wizard.button_confirm_not_empty() + + new_inventory = self.env["stock.inventory"].search([]) - old_inventories + return new_inventory + + def test_location_empty_is_empty(self): + """ Location is indicated as being empty, and it is""" + operation = self._open_screen("pick") + tray_location = operation.tray_location_id + tray_product = operation.current_move_line_id.product_id + inventory = self._test_location_empty_common(operation, tray_is_empty=True) + + self.assertEqual(len(inventory), 1) + self.assertEqual(inventory.state, "done") + self.assertEqual( + inventory.name, + "Zero check in location: {}".format(tray_location.complete_name), + ) + self.assertEqual(len(inventory.line_ids), 1) + self.assertEqual(inventory.line_ids[0].product_id, tray_product) + self.assertEqual(inventory.line_ids[0].location_id, tray_location) + self.assertEqual(inventory.line_ids[0].product_qty, 0) + self.assertEqual(inventory.line_ids[0].theoretical_qty, 0) + + def test_location_empty_is_not_empty(self): + """ Location is indicated as being empty, but it is not. + """ + operation = self._open_screen("pick") + tray_location = operation.tray_location_id + tray_product = operation.current_move_line_id.product_id + inventory = self._test_location_empty_common(operation, tray_is_empty=False) + self.assertEqual(len(inventory), 1) + self.assertEqual(inventory.state, "draft") + self.assertEqual( + inventory.name, + "{} zero check issue on location {}".format( + self.picking_out.name, tray_location.complete_name, + ), + ) + self.assertEqual(inventory.product_ids, tray_product) + self.assertEqual(inventory.location_ids, tray_location) diff --git a/stock_vertical_lift_empty_tray_check/views/res_config_setting_views.xml b/stock_vertical_lift_empty_tray_check/views/res_config_setting_views.xml new file mode 100644 index 000000000..06b0e2c86 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/views/res_config_setting_views.xml @@ -0,0 +1,31 @@ + + + + + res.config.settings.view.form.inherit.stock + res.config.settings + + + + +
+
+ +
+
+
+
+
+
+
+
+
diff --git a/stock_vertical_lift_empty_tray_check/views/vertical_lift_operation_pick_zero_check_views.xml b/stock_vertical_lift_empty_tray_check/views/vertical_lift_operation_pick_zero_check_views.xml new file mode 100644 index 000000000..12395b266 --- /dev/null +++ b/stock_vertical_lift_empty_tray_check/views/vertical_lift_operation_pick_zero_check_views.xml @@ -0,0 +1,34 @@ + + + + + vertical.lift.operation.pick.zero.check.view.form + vertical.lift.operation.pick.zero.check + +
+
+
+
+
+
+
+