diff --git a/repair_stock_dest/README.rst b/repair_stock_dest/README.rst new file mode 100644 index 00000000..6b0c0f41 --- /dev/null +++ b/repair_stock_dest/README.rst @@ -0,0 +1,116 @@ +======================== +Repair Stock Destination +======================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:9587e8affbb8d43fccc9913b182c9315239eae5744525598bbc4617740afa122 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Frepair-lightgray.png?logo=github + :target: https://github.com/OCA/repair/tree/17.0/repair_stock_dest + :alt: OCA/repair +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/repair-17-0/repair-17-0-repair_stock_dest + :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/repair&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module enhances the repair process by adding the ability to +transfer a repaired product to a specified destination location upon +completion of the repair. It introduces a Product Destination Location +field in the repair order, which defines where the repaired product will +be transferred. The default value for this field is set from the Default +Product Destination Location field of the associated Operation Type. + +Upon the completion of a repair order, an additional stock move is +automatically created to transfer the repaired product to the specified +destination location. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +How to use this module: + +1. Create a Repair Order: - Navigate to Repairs > Orders. - Click to + create a new repair order. - Before confirming the repair, go to the + Miscellaneous tab to view or modify the Product Destination Location + field. This is the location where the repaired product will be + transferred after the repair is completed. By default, this field is + populated from the Default Product Destination Location field of the + associated Operation Type. +2. After confirming the Repair Order: - Click on the Product Moves + action button to view the stock movements. You will see two stock + moves: one related to the repair itself, and another related to the + transfer of the repaired product to the specified destination + location. + +To ensure visibility of the location-related fields, make sure your user +is added to the Technical / Manage Multiple Stock Locations group. + +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 +------- + +* Patryk Pyczko + +Contributors +------------ + +- `APSL-Nagarro `__: + + - Patryk Pyczko + +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-ppyczko| image:: https://github.com/ppyczko.png?size=40px + :target: https://github.com/ppyczko + :alt: ppyczko + +Current `maintainer `__: + +|maintainer-ppyczko| + +This module is part of the `OCA/repair `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/repair_stock_dest/__init__.py b/repair_stock_dest/__init__.py new file mode 100644 index 00000000..31660d6a --- /dev/null +++ b/repair_stock_dest/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/repair_stock_dest/__manifest__.py b/repair_stock_dest/__manifest__.py new file mode 100644 index 00000000..4e676193 --- /dev/null +++ b/repair_stock_dest/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2024 Patryk Pyczko (APSL-Nagarro) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Repair Stock Destination", + "summary": "Add destination field to a repair order", + "version": "17.0.1.0.0", + "category": "Repair", + "website": "https://github.com/OCA/repair", + "author": "Patryk Pyczko, Odoo Community Association (OCA)", + "maintainers": ["ppyczko"], + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": ["repair"], + "data": ["views/repair_views.xml", "views/stock_picking_type_views.xml"], +} diff --git a/repair_stock_dest/i18n/ca_ES.po b/repair_stock_dest/i18n/ca_ES.po new file mode 100644 index 00000000..e212cc14 --- /dev/null +++ b/repair_stock_dest/i18n/ca_ES.po @@ -0,0 +1,50 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * repair_stock_dest +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-09-23 10:45+0000\n" +"PO-Revision-Date: 2024-09-23 10:45+0000\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: repair_stock_dest +#: model:ir.model.fields,field_description:repair_stock_dest.field_stock_picking_type__default_product_location_dest_id +msgid "Default Product Destination Location" +msgstr "Ubicació de destí del producte per defecte" + +#. module: repair_stock_dest +#: model:ir.model,name:repair_stock_dest.model_stock_picking_type +msgid "Picking Type" +msgstr "Tipus d'operació" + +#. module: repair_stock_dest +#: model:ir.model.fields,field_description:repair_stock_dest.field_repair_order__product_location_dest_id +msgid "Product Destination Location" +msgstr "Ubicació de destí del producte" + +#. module: repair_stock_dest +#: model:ir.model,name:repair_stock_dest.model_repair_order +msgid "Repair Order" +msgstr "Ordre de reparació" + +#. module: repair_stock_dest +#: model:ir.model.fields,help:repair_stock_dest.field_stock_picking_type__default_product_location_dest_id +msgid "" +"This is the default product destination location when you create a repair " +"order with this operation type." +msgstr "" +"Aquesta és la ubicació de destí del producte per defecte quan crees una " +"ordre de reparació amb aquest tipus d'operació." + +#. module: repair_stock_dest +#: model:ir.model.fields,help:repair_stock_dest.field_repair_order__product_location_dest_id +msgid "This is the location where the repaired product will be stored." +msgstr "Aquesta és la ubicació on s'emmagatzemarà el producte reparat." diff --git a/repair_stock_dest/i18n/es.po b/repair_stock_dest/i18n/es.po new file mode 100644 index 00000000..41832a0c --- /dev/null +++ b/repair_stock_dest/i18n/es.po @@ -0,0 +1,50 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * repair_stock_dest +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-09-23 10:44+0000\n" +"PO-Revision-Date: 2024-09-23 10:44+0000\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: repair_stock_dest +#: model:ir.model.fields,field_description:repair_stock_dest.field_stock_picking_type__default_product_location_dest_id +msgid "Default Product Destination Location" +msgstr "Ubicación de destino del producto por defecto" + +#. module: repair_stock_dest +#: model:ir.model,name:repair_stock_dest.model_stock_picking_type +msgid "Picking Type" +msgstr "Tipo de operación" + +#. module: repair_stock_dest +#: model:ir.model.fields,field_description:repair_stock_dest.field_repair_order__product_location_dest_id +msgid "Product Destination Location" +msgstr "Ubicación de destino del producto" + +#. module: repair_stock_dest +#: model:ir.model,name:repair_stock_dest.model_repair_order +msgid "Repair Order" +msgstr "Orden de reparación" + +#. module: repair_stock_dest +#: model:ir.model.fields,help:repair_stock_dest.field_stock_picking_type__default_product_location_dest_id +msgid "" +"This is the default product destination location when you create a repair " +"order with this operation type." +msgstr "" +"Esta es la ubicación de destino del producto por defecto cuando creas una " +"orden de reparación con este tipo de operación." + +#. module: repair_stock_dest +#: model:ir.model.fields,help:repair_stock_dest.field_repair_order__product_location_dest_id +msgid "This is the location where the repaired product will be stored." +msgstr "Esta es la ubicación donde se almacenará el producto reparado." diff --git a/repair_stock_dest/models/__init__.py b/repair_stock_dest/models/__init__.py new file mode 100644 index 00000000..945de8bc --- /dev/null +++ b/repair_stock_dest/models/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import repair +from . import stock_picking_type diff --git a/repair_stock_dest/models/repair.py b/repair_stock_dest/models/repair.py new file mode 100644 index 00000000..0520e6b4 --- /dev/null +++ b/repair_stock_dest/models/repair.py @@ -0,0 +1,99 @@ +# Copyright 2024 Patryk Pyczko (APSL-Nagarro) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models +from odoo.tools import float_compare + + +class RepairOrder(models.Model): + _inherit = "repair.order" + + product_location_dest_id = fields.Many2one( + "stock.location", + "Product Destination Location", + compute="_compute_product_location_dest_id", + store=True, + readonly=False, + required=True, + precompute=True, + index=True, + check_company=True, + help="This is the location where the repaired product will be stored.", + ) + + @api.depends("picking_type_id") + def _compute_product_location_dest_id(self): + for repair in self: + repair.product_location_dest_id = ( + repair.picking_type_id.default_product_location_dest_id + ) + + def action_repair_done(self): + res = super().action_repair_done() + + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + + for repair in self: + # Ensure the product has been repaired and stock move is done + if repair.product_location_dest_id and repair.move_id.state == "done": + owner_id = False + available_qty_owner = self.env["stock.quant"]._get_available_quantity( + repair.product_id, + repair.location_id, + repair.lot_id, + owner_id=repair.partner_id, + strict=True, + ) + if ( + float_compare( + available_qty_owner, + repair.product_qty, + precision_digits=precision, + ) + >= 0 + ): + owner_id = repair.partner_id.id + + transfer_move_vals = { + "name": f"Transfer After Repair - {repair.name}", + "product_id": repair.product_id.id, + "product_uom": repair.product_uom.id or repair.product_id.uom_id.id, + "product_uom_qty": repair.product_qty, + "partner_id": repair.partner_id.id, + "location_id": repair.location_id.id, + "location_dest_id": repair.product_location_dest_id.id, + "picked": True, + "move_line_ids": [ + ( + 0, + 0, + { + "product_id": repair.product_id.id, + "lot_id": repair.lot_id.id, + "product_uom_id": repair.product_uom.id + or repair.product_id.uom_id.id, + "quantity": repair.product_qty, + "package_id": False, + "result_package_id": False, + "owner_id": owner_id, + "location_id": repair.location_id.id, + "company_id": repair.company_id.id, + "location_dest_id": repair.product_location_dest_id.id, + "consume_line_ids": [ + (6, 0, repair.move_ids.move_line_ids.ids) + ], + }, + ) + ], + "repair_id": repair.id, + "origin": repair.name, + "company_id": repair.company_id.id, + } + + # Create new stock move to transfer the repaired product + transfer_move = self.env["stock.move"].create(transfer_move_vals) + transfer_move._action_done(cancel_backorder=True) + + return res diff --git a/repair_stock_dest/models/stock_picking_type.py b/repair_stock_dest/models/stock_picking_type.py new file mode 100644 index 00000000..8dd92266 --- /dev/null +++ b/repair_stock_dest/models/stock_picking_type.py @@ -0,0 +1,27 @@ +# Copyright 2024 Patryk Pyczko (APSL-Nagarro) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class PickingType(models.Model): + _inherit = "stock.picking.type" + + default_product_location_dest_id = fields.Many2one( + "stock.location", + "Default Product Destination Location", + compute="_compute_default_product_location_dest_id", + check_company=True, + store=True, + readonly=False, + precompute=True, + help="This is the default product destination location when you create a " + "repair order with this operation type.", + ) + + @api.depends("code") + def _compute_default_product_location_dest_id(self): + for picking_type in self: + stock_location = picking_type.warehouse_id.lot_stock_id + if picking_type.code == "repair_operation": + picking_type.default_product_location_dest_id = stock_location.id diff --git a/repair_stock_dest/pyproject.toml b/repair_stock_dest/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/repair_stock_dest/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/repair_stock_dest/readme/CONTRIBUTORS.md b/repair_stock_dest/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..f413e101 --- /dev/null +++ b/repair_stock_dest/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- [APSL-Nagarro](https://apsl.tech): + + > - Patryk Pyczko \<\> diff --git a/repair_stock_dest/readme/DESCRIPTION.md b/repair_stock_dest/readme/DESCRIPTION.md new file mode 100644 index 00000000..ec716bcb --- /dev/null +++ b/repair_stock_dest/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +This module enhances the repair process by adding the ability to transfer a repaired product to a specified destination location upon completion of the repair. It introduces a Product Destination Location field in the repair order, which defines where the repaired product will be transferred. The default value for this field is set from the Default Product Destination Location field of the associated Operation Type. + +Upon the completion of a repair order, an additional stock move is automatically created to transfer the repaired product to the specified destination location. \ No newline at end of file diff --git a/repair_stock_dest/readme/USAGE.md b/repair_stock_dest/readme/USAGE.md new file mode 100644 index 00000000..8778ab18 --- /dev/null +++ b/repair_stock_dest/readme/USAGE.md @@ -0,0 +1,10 @@ +How to use this module: + + 1. Create a Repair Order: + - Navigate to Repairs > Orders. + - Click to create a new repair order. + - Before confirming the repair, go to the Miscellaneous tab to view or modify the Product Destination Location field. This is the location where the repaired product will be transferred after the repair is completed. By default, this field is populated from the Default Product Destination Location field of the associated Operation Type. + 2. After confirming the Repair Order: + - Click on the Product Moves action button to view the stock movements. You will see two stock moves: one related to the repair itself, and another related to the transfer of the repaired product to the specified destination location. + +To ensure visibility of the location-related fields, make sure your user is added to the Technical / Manage Multiple Stock Locations group. \ No newline at end of file diff --git a/repair_stock_dest/static/description/icon.png b/repair_stock_dest/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/repair_stock_dest/static/description/icon.png differ diff --git a/repair_stock_dest/static/description/index.html b/repair_stock_dest/static/description/index.html new file mode 100644 index 00000000..421f4b2d --- /dev/null +++ b/repair_stock_dest/static/description/index.html @@ -0,0 +1,460 @@ + + + + + +Repair Stock Destination + + + +
+

Repair Stock Destination

+ + +

Beta License: AGPL-3 OCA/repair Translate me on Weblate Try me on Runboat

+

This module enhances the repair process by adding the ability to +transfer a repaired product to a specified destination location upon +completion of the repair. It introduces a Product Destination Location +field in the repair order, which defines where the repaired product will +be transferred. The default value for this field is set from the Default +Product Destination Location field of the associated Operation Type.

+

Upon the completion of a repair order, an additional stock move is +automatically created to transfer the repaired product to the specified +destination location.

+

Table of contents

+ +
+

Usage

+

How to use this module:

+
    +
  1. Create a Repair Order: - Navigate to Repairs > Orders. - Click to +create a new repair order. - Before confirming the repair, go to the +Miscellaneous tab to view or modify the Product Destination Location +field. This is the location where the repaired product will be +transferred after the repair is completed. By default, this field is +populated from the Default Product Destination Location field of the +associated Operation Type.
  2. +
  3. After confirming the Repair Order: - Click on the Product Moves +action button to view the stock movements. You will see two stock +moves: one related to the repair itself, and another related to the +transfer of the repaired product to the specified destination +location.
  4. +
+

To ensure visibility of the location-related fields, make sure your user +is added to the Technical / Manage Multiple Stock Locations group.

+
+
+

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

+
    +
  • Patryk Pyczko
  • +
+
+
+

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.

+

Current maintainer:

+

ppyczko

+

This module is part of the OCA/repair project on GitHub.

+

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

+
+
+
+ + diff --git a/repair_stock_dest/tests/__init__.py b/repair_stock_dest/tests/__init__.py new file mode 100644 index 00000000..0f4e1786 --- /dev/null +++ b/repair_stock_dest/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_repair_stock_dest diff --git a/repair_stock_dest/tests/test_repair_stock_dest.py b/repair_stock_dest/tests/test_repair_stock_dest.py new file mode 100644 index 00000000..d69f9ac0 --- /dev/null +++ b/repair_stock_dest/tests/test_repair_stock_dest.py @@ -0,0 +1,108 @@ +# Copyright 2024 Patryk Pyczko (APSL-Nagarro) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestRepairStockDest(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.stock_location = cls.env["stock.location"].create( + { + "name": "Stock Location", + "usage": "internal", + } + ) + + cls.product_destination_location = cls.env["stock.location"].create( + { + "name": "Product Destination Location", + "usage": "internal", + } + ) + + cls.picking_type = cls.env["stock.picking.type"].create( + { + "name": "Repair Operation Type", + "code": "repair_operation", + "default_location_src_id": cls.stock_location.id, + "default_product_location_dest_id": cls.product_destination_location.id, + "sequence_code": "RO", + } + ) + + cls.product = cls.env["product.product"].create( + { + "name": "Test Product", + "type": "product", + } + ) + + cls.repair_order = cls.env["repair.order"].create( + { + "name": "Test Repair Order", + "picking_type_id": cls.picking_type.id, + "product_id": cls.product.id, + } + ) + + # Create stock move linked to the repair order + cls.stock_move = cls.env["stock.move"].create( + { + "name": "Test Stock Move", + "product_id": cls.product.id, + "product_uom_qty": 1, + "location_id": cls.stock_location.id, + "location_dest_id": cls.stock_location.id, + "picking_type_id": cls.picking_type.id, + "repair_id": cls.repair_order.id, + } + ) + cls.repair_order.move_id = cls.stock_move + + def test_product_location_dest_id_computation(self): + """Test that product_location_dest_id is correctly computed.""" + self.repair_order._compute_product_location_dest_id() + self.assertEqual( + self.repair_order.product_location_dest_id, + self.picking_type.default_product_location_dest_id, + "The product_location_dest_id should be set to the default location.", + ) + + def test_product_location_dest_id_modification(self): + """Test that product_location_dest_id can be manually modified.""" + custom_location = self.env["stock.location"].create( + {"name": "Custom Location", "usage": "internal"} + ) + + self.repair_order.product_location_dest_id = custom_location.id + self.assertEqual( + self.repair_order.product_location_dest_id, + custom_location, + "The product_location_dest_id should be modifiable by the user.", + ) + + def test_action_repair_done_creates_new_move(self): + """Test that action_repair_done creates a new + stock move for the repaired product.""" + self.stock_move.state = "done" + self.repair_order._compute_product_location_dest_id() + + self.repair_order.action_repair_done() + + transfer_move_name = f"Transfer After Repair - {self.repair_order.name}" + new_move = self.env["stock.move"].search([("name", "=", transfer_move_name)]) + + self.assertTrue( + new_move, + "A new stock move should be created for the " + "transfer of the repaired product.", + ) + self.assertEqual( + new_move.location_dest_id, + self.repair_order.product_location_dest_id, + "The new stock move's location_dest_id should match " + "the repair order's product_location_dest_id.", + ) diff --git a/repair_stock_dest/views/repair_views.xml b/repair_stock_dest/views/repair_views.xml new file mode 100644 index 00000000..d77ec12a --- /dev/null +++ b/repair_stock_dest/views/repair_views.xml @@ -0,0 +1,33 @@ + + + + + repair.order.tree.inherit + repair.order + + + + + + + + + + repair.form.form.inherit + repair.order + + + + + + + + + diff --git a/repair_stock_dest/views/stock_picking_type_views.xml b/repair_stock_dest/views/stock_picking_type_views.xml new file mode 100644 index 00000000..3699084d --- /dev/null +++ b/repair_stock_dest/views/stock_picking_type_views.xml @@ -0,0 +1,18 @@ + + + + stock.picking.type.repair.type.inherit + stock.picking.type + + + + + + + +