From e48963de9f34eee4e5e3f085febf642453e730c0 Mon Sep 17 00:00:00 2001 From: angelinaanaki Date: Wed, 15 Nov 2023 08:49:33 +0100 Subject: [PATCH] [16.0][ADD] stock_quant_package_fast_move --- .../odoo/addons/stock_quant_package_fast_move | 1 + setup/stock_quant_package_fast_move/setup.py | 6 + stock_quant_package_fast_move/README.rst | 152 ++++++++ stock_quant_package_fast_move/__init__.py | 5 + stock_quant_package_fast_move/__manifest__.py | 19 + .../models/__init__.py | 6 + .../models/res_company.py | 12 + .../models/res_config_settings.py | 12 + .../models/stock_quant_package.py | 82 ++++ .../readme/CONFIGURE.md | 5 + .../readme/CONTEXT.md | 21 + .../readme/CONTRIBUTORS.md | 1 + .../readme/DESCRIPTION.md | 3 + .../readme/ROADMAP.md | 1 + stock_quant_package_fast_move/readme/USAGE.md | 11 + .../security/ir.model.access.csv | 2 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/icon.svg | 79 ++++ .../static/description/index.html | 124 ++++++ .../tests/__init__.py | 4 + .../test_stock_quant_package_fast_move.py | 361 ++++++++++++++++++ .../views/res_config_settings.xml | 33 ++ .../wizard/__init__.py | 4 + .../stock_quant_package_fast_move_wizard.py | 29 ++ .../stock_quant_package_fast_move_wizard.xml | 36 ++ test-requirements.txt | 1 + 26 files changed, 1010 insertions(+) create mode 120000 setup/stock_quant_package_fast_move/odoo/addons/stock_quant_package_fast_move create mode 100644 setup/stock_quant_package_fast_move/setup.py create mode 100644 stock_quant_package_fast_move/README.rst create mode 100644 stock_quant_package_fast_move/__init__.py create mode 100644 stock_quant_package_fast_move/__manifest__.py create mode 100644 stock_quant_package_fast_move/models/__init__.py create mode 100644 stock_quant_package_fast_move/models/res_company.py create mode 100644 stock_quant_package_fast_move/models/res_config_settings.py create mode 100644 stock_quant_package_fast_move/models/stock_quant_package.py create mode 100644 stock_quant_package_fast_move/readme/CONFIGURE.md create mode 100644 stock_quant_package_fast_move/readme/CONTEXT.md create mode 100644 stock_quant_package_fast_move/readme/CONTRIBUTORS.md create mode 100644 stock_quant_package_fast_move/readme/DESCRIPTION.md create mode 100644 stock_quant_package_fast_move/readme/ROADMAP.md create mode 100644 stock_quant_package_fast_move/readme/USAGE.md create mode 100644 stock_quant_package_fast_move/security/ir.model.access.csv create mode 100644 stock_quant_package_fast_move/static/description/icon.png create mode 100644 stock_quant_package_fast_move/static/description/icon.svg create mode 100644 stock_quant_package_fast_move/static/description/index.html create mode 100644 stock_quant_package_fast_move/tests/__init__.py create mode 100644 stock_quant_package_fast_move/tests/test_stock_quant_package_fast_move.py create mode 100644 stock_quant_package_fast_move/views/res_config_settings.xml create mode 100644 stock_quant_package_fast_move/wizard/__init__.py create mode 100644 stock_quant_package_fast_move/wizard/stock_quant_package_fast_move_wizard.py create mode 100644 stock_quant_package_fast_move/wizard/stock_quant_package_fast_move_wizard.xml diff --git a/setup/stock_quant_package_fast_move/odoo/addons/stock_quant_package_fast_move b/setup/stock_quant_package_fast_move/odoo/addons/stock_quant_package_fast_move new file mode 120000 index 000000000000..cdad94666caa --- /dev/null +++ b/setup/stock_quant_package_fast_move/odoo/addons/stock_quant_package_fast_move @@ -0,0 +1 @@ +../../../../stock_quant_package_fast_move \ No newline at end of file diff --git a/setup/stock_quant_package_fast_move/setup.py b/setup/stock_quant_package_fast_move/setup.py new file mode 100644 index 000000000000..28c57bb64031 --- /dev/null +++ b/setup/stock_quant_package_fast_move/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_quant_package_fast_move/README.rst b/stock_quant_package_fast_move/README.rst new file mode 100644 index 000000000000..5277b46c7e3d --- /dev/null +++ b/stock_quant_package_fast_move/README.rst @@ -0,0 +1,152 @@ +=================================== +Fast Package Move Between Locations +=================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:652bbb005309712f6a57d643565339e74880f1520033bac69c22e8133a165f3c + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |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--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/stock-logistics-workflow/tree/16.0/stock_quant_package_fast_move + :alt: OCA/stock-logistics-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/stock-logistics-workflow-16-0/stock-logistics-workflow-16-0-stock_quant_package_fast_move + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-workflow&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module simplifies package movement between different warehouse +locations. + +You can simply select several packages and use a wizard that will +perform all the underlying inventory operations. + +**Table of contents** + +.. contents:: + :local: + +Use Cases / Context +=================== + +Imagine that you need to transfer different packages between different +locations in your warehouse. + +Regular Odoo flow looks like this: + +- Go to the "Inventory -> Overview" and select an operation type (eg + 'Internal Transfer') + +- Create a corresponding picking + +- Add packages if 'Move Entire Packages' is enabled or add all the + package products + +- Set quantities done + +- Validate + +However if you are working with packages you will primary operate from +the "Packages" menu. + +And it would be much convenient if you could move packages directly from +this menu. + +This is what this module is designed for. + +Configuration +============= + +- Go to the "Inventory -> Configuration -> Settings" + +- Activate the "Packages" checkbox + +- In the "Package Move Operation" select the operation type that will + be used for the easy package transfers. This setting is configured + for each company separately + +Usage +===== + +- Go to the "Inventory -> Products -> Packages" + +- Open a package or select several packages in the list vies. + Important: all selected packages must be located in the same location + +- Open the "Action" menu and select "Move packages" + +- In the opened wizard select the destination location where you want + to move the packages + +- You can select an optional destination package in the "Destination + Package" field. If selected the content of the selected packages will + be moved into the destination package located at the destination + location. + +- Click the "Move" button + +Known issues / Roadmap +====================== + +Only packages residing in the same source location can be moved. + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* Cetmix + +Contributors +------------ + +- Cetmix https://cetmix.com/ + +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. + +.. |maintainer-CetmixGitDrone| image:: https://github.com/CetmixGitDrone.png?size=40px + :target: https://github.com/CetmixGitDrone + :alt: CetmixGitDrone + +Current `maintainer `__: + +|maintainer-CetmixGitDrone| + +This module is part of the `OCA/stock-logistics-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_quant_package_fast_move/__init__.py b/stock_quant_package_fast_move/__init__.py new file mode 100644 index 000000000000..e91423ead140 --- /dev/null +++ b/stock_quant_package_fast_move/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import models +from . import wizard diff --git a/stock_quant_package_fast_move/__manifest__.py b/stock_quant_package_fast_move/__manifest__.py new file mode 100644 index 000000000000..d55a54e2a6f7 --- /dev/null +++ b/stock_quant_package_fast_move/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Fast Package Move Between Locations", + "version": "16.0.1.0.0", + "category": "Inventory/Inventory", + "summary": "Move packages between locations directly from the 'Package' menu", + "depends": ["stock_picking_move_package_to_package"], + "website": "https://github.com/OCA/stock-logistics-workflow", + "author": "Cetmix, Odoo Community Association (OCA)", + "maintainers": ["CetmixGitDrone"], + "installable": True, + "data": [ + "security/ir.model.access.csv", + "wizard/stock_quant_package_fast_move_wizard.xml", + "views/res_config_settings.xml", + ], + "license": "AGPL-3", +} diff --git a/stock_quant_package_fast_move/models/__init__.py b/stock_quant_package_fast_move/models/__init__.py new file mode 100644 index 000000000000..a9f8b0489c77 --- /dev/null +++ b/stock_quant_package_fast_move/models/__init__.py @@ -0,0 +1,6 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import stock_quant_package +from . import res_company +from . import res_config_settings diff --git a/stock_quant_package_fast_move/models/res_company.py b/stock_quant_package_fast_move/models/res_company.py new file mode 100644 index 000000000000..5c44ea5d82c7 --- /dev/null +++ b/stock_quant_package_fast_move/models/res_company.py @@ -0,0 +1,12 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + picking_type_id = fields.Many2one( + "stock.picking.type", string="Package Move Operation" + ) diff --git a/stock_quant_package_fast_move/models/res_config_settings.py b/stock_quant_package_fast_move/models/res_config_settings.py new file mode 100644 index 000000000000..1acc5a20bd61 --- /dev/null +++ b/stock_quant_package_fast_move/models/res_config_settings.py @@ -0,0 +1,12 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + picking_type_id = fields.Many2one( + related="company_id.picking_type_id", readonly=False + ) diff --git a/stock_quant_package_fast_move/models/stock_quant_package.py b/stock_quant_package_fast_move/models/stock_quant_package.py new file mode 100644 index 000000000000..2bef8f93e1b3 --- /dev/null +++ b/stock_quant_package_fast_move/models/stock_quant_package.py @@ -0,0 +1,82 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import _, exceptions, models + + +class StockQuantPackage(models.Model): + _inherit = "stock.quant.package" + + def action_show_package_fast_move_wizard(self): + """Open wizard for fast package movement.""" + return { + "type": "ir.actions.act_window", + "name": _("Move Packages"), + "res_model": "stock.quant.package.fast.move.wizard", + "target": "new", + "view_id": self.env.ref( + "stock_quant_package_fast_move.stock_quant_package_fast_move_wizard_view_form" + ).id, + "view_mode": "form", + "context": self.env.context, + } + + def _move_to_location(self, location, destination_package=None): + """ + Move packages to a specified location. + + Parameters: + - location (recordset of stock.location): The destination location. + - destination_package (recordset of stock.quant.package, optional): + Optional destination package. If provided, it must belong to the specified location. + + Returns: + - bool: True if the move is successful. + + Raises: + - exceptions.UserError: If the destination location is the same as the current location + or if the destination package does not belong to the specified location. + """ + # Check if the location is different from the current location + if location == self[0].location_id: + raise exceptions.UserError( + _("The destination location is the same as the current location.") + ) + + # Check if the destination package belongs to the same location + if destination_package and destination_package.location_id != location: + raise exceptions.UserError( + _("The destination package does not belong to the specified location.") + ) + + active_company = self.env.company + picking_type_id = active_company.picking_type_id + + # Create a picking + picking_vals = { + "location_id": self[0].location_id.id, + "location_dest_id": location.id, + "picking_type_id": picking_type_id.id, + } + picking = self.env["stock.picking"].create(picking_vals) + + for package in self: + # Create a package_level record for each package + package_level_vals = { + "package_id": package.id, + "package_dest_id": destination_package.id + if destination_package + else False, + "picking_id": picking.id, + "company_id": active_company.id, + "location_id": package.location_id.id, + "location_dest_id": location.id, + } + package_level = self.env["stock.package_level"].create(package_level_vals) + package_level.write({"is_done": True}) + + # Validate the picking + picking.action_confirm() + picking.button_validate() + + return True diff --git a/stock_quant_package_fast_move/readme/CONFIGURE.md b/stock_quant_package_fast_move/readme/CONFIGURE.md new file mode 100644 index 000000000000..3afcf0b4269d --- /dev/null +++ b/stock_quant_package_fast_move/readme/CONFIGURE.md @@ -0,0 +1,5 @@ +- Go to the "Inventory -> Configuration -> Settings" + +- Activate the "Packages" checkbox + +- In the "Package Move Operation" select the operation type that will be used for the easy package transfers. This setting is configured for each company separately \ No newline at end of file diff --git a/stock_quant_package_fast_move/readme/CONTEXT.md b/stock_quant_package_fast_move/readme/CONTEXT.md new file mode 100644 index 000000000000..ceb34eac3c7b --- /dev/null +++ b/stock_quant_package_fast_move/readme/CONTEXT.md @@ -0,0 +1,21 @@ +Imagine that you need to transfer different packages between different locations in your warehouse. + +Regular Odoo flow looks like this: + +- Go to the "Inventory -> Overview" and select an operation type (eg 'Internal Transfer') + +- Create a corresponding picking + +- Add packages if 'Move Entire Packages' is enabled or add all the package products + +- Set quantities done + +- Validate + + + +However if you are working with packages you will primary operate from the "Packages" menu. + +And it would be much convenient if you could move packages directly from this menu. + +This is what this module is designed for. \ No newline at end of file diff --git a/stock_quant_package_fast_move/readme/CONTRIBUTORS.md b/stock_quant_package_fast_move/readme/CONTRIBUTORS.md new file mode 100644 index 000000000000..9d697ca0a31b --- /dev/null +++ b/stock_quant_package_fast_move/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +* Cetmix diff --git a/stock_quant_package_fast_move/readme/DESCRIPTION.md b/stock_quant_package_fast_move/readme/DESCRIPTION.md new file mode 100644 index 000000000000..27d4ec4d8793 --- /dev/null +++ b/stock_quant_package_fast_move/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +This module simplifies package movement between different warehouse locations. + +You can simply select several packages and use a wizard that will perform all the underlying inventory operations. \ No newline at end of file diff --git a/stock_quant_package_fast_move/readme/ROADMAP.md b/stock_quant_package_fast_move/readme/ROADMAP.md new file mode 100644 index 000000000000..d42f46ef5d2c --- /dev/null +++ b/stock_quant_package_fast_move/readme/ROADMAP.md @@ -0,0 +1 @@ +Only packages residing in the same source location can be moved. \ No newline at end of file diff --git a/stock_quant_package_fast_move/readme/USAGE.md b/stock_quant_package_fast_move/readme/USAGE.md new file mode 100644 index 000000000000..bf8414244ae0 --- /dev/null +++ b/stock_quant_package_fast_move/readme/USAGE.md @@ -0,0 +1,11 @@ +- Go to the "Inventory -> Products -> Packages" + +- Open a package or select several packages in the list vies. Important: all selected packages must be located in the same location + +- Open the "Action" menu and select "Move packages" + +- In the opened wizard select the destination location where you want to move the packages + +- You can select an optional destination package in the "Destination Package" field. If selected the content of the selected packages will be moved into the destination package located at the destination location. + +- Click the "Move" button \ No newline at end of file diff --git a/stock_quant_package_fast_move/security/ir.model.access.csv b/stock_quant_package_fast_move/security/ir.model.access.csv new file mode 100644 index 000000000000..0b67743a94ff --- /dev/null +++ b/stock_quant_package_fast_move/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +stock_quant_package_fast_move.access_stock_quant_package_fast_move_wizard,access_stock_quant_package_fast_move_wizard,stock_quant_package_fast_move.model_stock_quant_package_fast_move_wizard,stock.group_stock_user,1,1,1,1 diff --git a/stock_quant_package_fast_move/static/description/icon.png b/stock_quant_package_fast_move/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_quant_package_fast_move/static/description/icon.svg b/stock_quant_package_fast_move/static/description/icon.svg new file mode 100644 index 000000000000..a7a26d0932ab --- /dev/null +++ b/stock_quant_package_fast_move/static/description/icon.svg @@ -0,0 +1,79 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/stock_quant_package_fast_move/static/description/index.html b/stock_quant_package_fast_move/static/description/index.html new file mode 100644 index 000000000000..f92d18ab8d60 --- /dev/null +++ b/stock_quant_package_fast_move/static/description/index.html @@ -0,0 +1,124 @@ +
+
+
+

Module name

+

This module was written to extend the functionality of ... to support ... and allow you to ...

+
+
+
+ +
+
+
+

Installation

+
+
+

To install this module, you need to: +

    +
  • ...
  • +
+

+
+
+
+ + + +
+
+
+
+ +
+
+
+

Configuration

+
+
+

To configure this module, you need to: +

    +
  • ...
  • +
+

+
+
+
+ + + +
+
+
+
+ +
+
+
+

Usage

+
+
+

To use this module, you need to: +

    +
  • ...
  • +
+

+

For further information, please visit: +

+

+
+
+
+ + + +
+
+
+
+ +
+
+
+

Known issues / Roadmap

+
+
+

+

    +
  • ...
  • +
+

+
+
+
+ + + +
+
+
+
+ +
+
+
+

Credits

+
+
+

Contributors

+ +
+
+

Maintainer

+

+ This module is maintained by the OCA.
+ 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.
+ To contribute to this module, please visit http://odoo-community.org.
+ +

+
+
+
diff --git a/stock_quant_package_fast_move/tests/__init__.py b/stock_quant_package_fast_move/tests/__init__.py new file mode 100644 index 000000000000..89cfcb4ed360 --- /dev/null +++ b/stock_quant_package_fast_move/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import test_stock_quant_package_fast_move diff --git a/stock_quant_package_fast_move/tests/test_stock_quant_package_fast_move.py b/stock_quant_package_fast_move/tests/test_stock_quant_package_fast_move.py new file mode 100644 index 000000000000..9b2a27f2c5f9 --- /dev/null +++ b/stock_quant_package_fast_move/tests/test_stock_quant_package_fast_move.py @@ -0,0 +1,361 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo.exceptions import UserError +from odoo.tests import TransactionCase +from odoo.tests.common import Form + + +class TestStockQuantPackageFastMove(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.ResCompany = cls.env["res.company"] + cls.StockPicking = cls.env["stock.picking"] + cls.StockPackage = cls.env["stock.quant.package"] + cls.StockQuant = cls.env["stock.quant"] + cls.Product = cls.env["product.product"] + cls.PackageLevel = cls.env["stock.package_level"] + cls.StockMoveLine = cls.env["stock.move.line"] + cls.company = cls.ResCompany.create({"name": "Company A"}) + cls.user_demo = cls.env["res.users"].create( + { + "login": "firstnametest", + "name": "User Demo", + "email": "firstnametest@example.org", + "groups_id": [ + (4, cls.env.ref("base.group_user").id), + (4, cls.env.ref("stock.group_stock_user").id), + ], + } + ) + group_stock_multi_locations = cls.env.ref("stock.group_stock_multi_locations") + group_stock_adv_location = cls.env.ref("stock.group_adv_location") + group_tracking_lot = cls.env.ref("stock.group_tracking_lot") + cls.user_demo.write( + { + "company_id": cls.company.id, + "company_ids": [(4, cls.company.id)], + "groups_id": [ + (4, group_stock_multi_locations.id, 0), + (4, group_stock_adv_location.id, 0), + (4, group_tracking_lot.id, 0), + ], + } + ) + cls.stock_location = ( + cls.env["stock.location"] + .sudo() + .search( + [("name", "=", "Stock"), ("company_id", "=", cls.company.id)], limit=1 + ) + ) + cls.warehouse = cls.stock_location.warehouse_id + cls.warehouse.write({"reception_steps": "two_steps"}) + cls.input_location = cls.warehouse.wh_input_stock_loc_id + cls.in_type = cls.warehouse.in_type_id + cls.in_type.write({"show_entire_packs": True}) + cls.int_type = cls.warehouse.int_type_id + cls.int_type.write({"show_entire_packs": True}) + cls.uom_unit = cls.env.ref("uom.product_uom_unit") + cls.company.write({"picking_type_id": cls.int_type.id}) + cls.product1 = cls.Product.create( + { + "name": "Test Product 1", + "type": "product", + "default_code": "TEST_PROD1", + "tracking": "none", + } + ) + cls.product2 = cls.Product.create( + { + "name": "Test Product 2", + "type": "product", + "default_code": "TEST_PROD2", + "tracking": "none", + } + ) + cls.product3 = cls.Product.create( + { + "name": "Test Product 3", + "type": "product", + "default_code": "TEST_PROD3", + "tracking": "lot", + } + ) + cls.product4 = cls.Product.create( + { + "name": "Test Product 4", + "type": "product", + "default_code": "TEST_PROD4", + "tracking": "serial", + } + ) + # Create a IN picking with product 1, confirm it and move it in Stock location + incoming_picking1 = ( + cls.StockPicking.with_context(default_company_id=cls.company.id) + .with_user(cls.user_demo) + .create( + { + "location_dest_id": cls.input_location.id, + "picking_type_id": cls.in_type.id, + } + ) + ) + cls.StockMoveLine.create( + { + "product_id": cls.product1.id, + "product_uom_id": cls.uom_unit.id, + "qty_done": 3.0, + "picking_id": incoming_picking1.id, + } + ) + incoming_picking1.action_put_in_pack() + incoming_picking1.action_confirm() + incoming_picking1.button_validate() + + internal_picking1 = cls.StockPicking.search( + [("origin", "=", incoming_picking1.name)] + ) + package_level1 = cls.PackageLevel.search( + [("picking_id", "=", internal_picking1.id)] + ) + package_level1.write({"is_done": True}) + internal_picking1.action_confirm() + internal_picking1.button_validate() + + cls.package1 = package_level1.package_id + + # Create a IN picking with product 2 and product 3, + # put in pack and leave it in Input location + incoming_picking2 = ( + cls.StockPicking.with_context(default_company_id=cls.company.id) + .with_user(cls.user_demo) + .create( + { + "location_dest_id": cls.input_location.id, + "picking_type_id": cls.in_type.id, + } + ) + ) + cls.StockMoveLine.create( + [ + { + "product_id": cls.product2.id, + "product_uom_id": cls.uom_unit.id, + "qty_done": 2.0, + "picking_id": incoming_picking2.id, + }, + { + "product_id": cls.product3.id, + "product_uom_id": cls.uom_unit.id, + "lot_name": "LOT/1", + "qty_done": 2.0, + "picking_id": incoming_picking2.id, + }, + ] + ) + incoming_picking2.action_put_in_pack() + incoming_picking2.action_confirm() + incoming_picking2.button_validate() + + internal_picking2 = cls.StockPicking.search( + [("origin", "=", incoming_picking2.name)] + ) + + package_level2 = cls.PackageLevel.search( + [("picking_id", "=", internal_picking2.id)] + ) + cls.package2 = package_level2.package_id + + # Unreserve + internal_picking2.do_unreserve() + internal_picking2.unlink() + + # Create a IN picking with product 4, put in pack and leave it in Input location + incoming_picking3 = ( + cls.StockPicking.with_context(default_company_id=cls.company.id) + .with_user(cls.user_demo) + .create( + { + "location_dest_id": cls.input_location.id, + "picking_type_id": cls.in_type.id, + } + ) + ) + cls.StockMoveLine.create( + [ + { + "product_id": cls.product4.id, + "product_uom_id": cls.uom_unit.id, + "qty_done": 1.0, + "lot_name": "SERIAL/1", + "picking_id": incoming_picking3.id, + } + ] + ) + incoming_picking3.action_put_in_pack() + incoming_picking3.action_confirm() + incoming_picking3.button_validate() + + internal_picking3 = cls.StockPicking.search( + [("origin", "=", incoming_picking3.name)] + ) + + package_level3 = cls.PackageLevel.search( + [("picking_id", "=", internal_picking3.id)] + ) + cls.package3 = package_level3.package_id + # Unreserve + internal_picking3.do_unreserve() + internal_picking3.unlink() + + def test_stock_quant_package_fast_move(self): + # Test if the optional field destination package is empty + with Form( + self.env["stock.quant.package.fast.move.wizard"] + .with_company(self.company.id) + .with_context(active_ids=[self.package2.id, self.package3.id]) + ) as f1: + f1.location_dest_id = self.int_type.default_location_dest_id + + wizard = f1.save() + wizard.action_move() + + # Check the available product qty of product1 product2 + # product3 and product 4 after the operations + self.assertEqual( + self.product1.qty_available, + 3.0, + "Total product quantity is not as expected.", + ) + self.assertEqual( + self.product2.qty_available, + 2.0, + "Total product quantity is not as expected.", + ) + self.assertEqual( + self.product3.qty_available, + 2.0, + "Total product quantity is not as expected.", + ) + self.assertEqual( + self.product4.qty_available, + 1.0, + "Total product quantity is not as expected.", + ) + + # Test if the destination package contains the content from the source package + self.assertNotIn( + self.product2, + self.package1.quant_ids.mapped("product_id"), + msg="Product not found in the destination package.", + ) + self.assertNotIn( + self.product3, + self.package1.quant_ids.mapped("product_id"), + msg="Product not found in the destination package.", + ) + self.assertNotIn( + self.product4, + self.package1.quant_ids.mapped("product_id"), + msg="Product not found in the destination package.", + ) + + # Check the location_id of the moved packages + self.assertEqual( + self.package2.location_id.id, + self.int_type.default_location_dest_id.id, + "Wrong location on the package.", + ) + self.assertEqual( + self.package3.location_id.id, + self.int_type.default_location_dest_id.id, + "Wrong location on the package.", + ) + + def test_stock_quant_package_fast_move_package(self): + # Test if the optional field destination package is set + with Form( + self.env["stock.quant.package.fast.move.wizard"] + .with_company(self.company.id) + .with_context(active_ids=[self.package2.id, self.package3.id]) + ) as f1: + f1.location_dest_id = self.int_type.default_location_dest_id + f1.package_dest_id = self.package1 + + wizard = f1.save() + wizard.action_move() + + # Check the available product qty of product1 product2 + # product3 and product 4 after the operations + self.assertEqual( + self.product1.qty_available, + 3.0, + "Total product quantity is not as expected.", + ) + self.assertEqual( + self.product2.qty_available, + 2.0, + "Total product quantity is not as expected.", + ) + self.assertEqual( + self.product3.qty_available, + 2.0, + "Total product quantity is not as expected.", + ) + self.assertEqual( + self.product4.qty_available, + 1.0, + "Total product quantity is not as expected.", + ) + + # Test if the destination package contains the content from the source packages + self.assertIn( + self.product1, + self.package1.quant_ids.mapped("product_id"), + msg="Product not found in the destination package.", + ) + self.assertIn( + self.product2, + self.package1.quant_ids.mapped("product_id"), + msg="Product not found in the destination package.", + ) + self.assertIn( + self.product3, + self.package1.quant_ids.mapped("product_id"), + msg="Product not found in the destination package.", + ) + self.assertIn( + self.product4, + self.package1.quant_ids.mapped("product_id"), + msg="Product not found in the destination package.", + ) + + def test_stock_quant_package_fast_move_package_error_same_location(self): + # Check if the location is different from the current location + with Form( + self.env["stock.quant.package.fast.move.wizard"] + .with_company(self.company.id) + .with_context(active_ids=[self.package2.id, self.package3.id]) + ) as f1: + f1.location_dest_id = self.in_type.default_location_dest_id + + wizard = f1.save() + with self.assertRaises(UserError): + wizard.action_move() + + def test_stock_quant_package_fast_move_package_error_package_same_location(self): + # Check if the destination package belongs to the same location + with Form( + self.env["stock.quant.package.fast.move.wizard"] + .with_company(self.company.id) + .with_context(active_ids=[self.package2.id]) + ) as f1: + f1.location_dest_id = self.int_type.default_location_dest_id + f1.package_dest_id = self.package3 + + wizard = f1.save() + with self.assertRaises(UserError): + wizard.action_move() diff --git a/stock_quant_package_fast_move/views/res_config_settings.xml b/stock_quant_package_fast_move/views/res_config_settings.xml new file mode 100644 index 000000000000..590619ef7ce1 --- /dev/null +++ b/stock_quant_package_fast_move/views/res_config_settings.xml @@ -0,0 +1,33 @@ + + + + stock.quant.package.fast.move.res.config.settings.view.form + res.config.settings + + + +
+
+
+
+
+
+
+
diff --git a/stock_quant_package_fast_move/wizard/__init__.py b/stock_quant_package_fast_move/wizard/__init__.py new file mode 100644 index 000000000000..0c8aeb504d5a --- /dev/null +++ b/stock_quant_package_fast_move/wizard/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from . import stock_quant_package_fast_move_wizard diff --git a/stock_quant_package_fast_move/wizard/stock_quant_package_fast_move_wizard.py b/stock_quant_package_fast_move/wizard/stock_quant_package_fast_move_wizard.py new file mode 100644 index 000000000000..a504395afab9 --- /dev/null +++ b/stock_quant_package_fast_move/wizard/stock_quant_package_fast_move_wizard.py @@ -0,0 +1,29 @@ +# Copyright (C) 2023 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import _, exceptions, fields, models + + +class StockQuantPackageFastMoveWizard(models.TransientModel): + _name = "stock.quant.package.fast.move.wizard" + _description = "Stock Quant Package Fast Move Wizard" + + package_dest_id = fields.Many2one("stock.quant.package", "Destination Package") + location_dest_id = fields.Many2one( + "stock.location", "Destination Location", required=True + ) + + def action_move(self): + """ + Execute the package movement based on the specified parameters. + """ + package_ids = self._context.get("active_ids") + packages = self.env["stock.quant.package"].browse(package_ids) + if any(package.location_id != packages[0].location_id for package in packages): + raise exceptions.UserError( + _("All packages must have the same source location.") + ) + + packages._move_to_location(self.location_dest_id, self.package_dest_id) + + return {"type": "ir.actions.act_window_close"} diff --git a/stock_quant_package_fast_move/wizard/stock_quant_package_fast_move_wizard.xml b/stock_quant_package_fast_move/wizard/stock_quant_package_fast_move_wizard.xml new file mode 100644 index 000000000000..45fd140680c1 --- /dev/null +++ b/stock_quant_package_fast_move/wizard/stock_quant_package_fast_move_wizard.xml @@ -0,0 +1,36 @@ + + + + + stock.quant.package.fast.move.wizard.form + stock.quant.package.fast.move.wizard + +
+ + + + +
+
+
+
+
+ + + Move Packages + ir.actions.server + + + code + + action = model.action_show_package_fast_move_wizard() + + +
diff --git a/test-requirements.txt b/test-requirements.txt index 4ad8e0eceaa8..d4755be7b0e6 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1,2 @@ odoo-test-helper +odoo-addon-stock_picking_move_package_to_package @ git+https://github.com/OCA/stock-logistics-workflow@refs/pull/1422/head#subdirectory=setup/stock_picking_move_package_to_package