diff --git a/repair_picking_after_done/README.rst b/repair_picking_after_done/README.rst new file mode 100644 index 00000000..e9f18d75 --- /dev/null +++ b/repair_picking_after_done/README.rst @@ -0,0 +1,105 @@ +========================= +Repair picking after done +========================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:f11fa65acd2414cbbab2c2c100e3d976b2147ad613787ffc494bc85e0242b0bd + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Frepair-lightgray.png?logo=github + :target: https://github.com/OCA/repair/tree/17.0/repair_picking_after_done + :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_picking_after_done + :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 adds the functionality to create transfer of repaired move +once repair order is done. + +.. 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 +============= + +No configuration needed for this module. + +Usage +===== + +After repair order is done, You will be able to see button "Transfer" on +repair order's form view. You will be able to create internal transfer +between repair location to any destination location. + +Known issues / Roadmap +====================== + + + +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 +------- + +* ForgeFlow + +Contributors +------------ + +- `ForgeFlow `__: + + - Dhaval Talpada + +- `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. + +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_picking_after_done/__init__.py b/repair_picking_after_done/__init__.py new file mode 100644 index 00000000..08eb1400 --- /dev/null +++ b/repair_picking_after_done/__init__.py @@ -0,0 +1,3 @@ +from . import models +from . import wizards +from .hooks import post_load_hook diff --git a/repair_picking_after_done/__manifest__.py b/repair_picking_after_done/__manifest__.py new file mode 100644 index 00000000..a0edf33d --- /dev/null +++ b/repair_picking_after_done/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2021 ForgeFlow S.L. +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Repair picking after done", + "version": "17.0.1.0.0", + "author": "ForgeFlow, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/repair", + "summary": "Transfer repaired move to another location directly from repair order", + "category": "Repair", + "depends": ["repair_type", "repair_stock"], + "data": [ + "security/ir.model.access.csv", + "views/repair.xml", + "wizards/repair_move_transfer_views.xml", + ], + "installable": True, + "development_status": "Alpha", + "license": "AGPL-3", + "application": False, + "post_load": "post_load_hook", +} diff --git a/repair_picking_after_done/hooks.py b/repair_picking_after_done/hooks.py new file mode 100644 index 00000000..220aa3c2 --- /dev/null +++ b/repair_picking_after_done/hooks.py @@ -0,0 +1,25 @@ +# Copyright 2024 Patryk Pyczko (APSL-Nagarro) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.addons.repair.models.stock_move import StockMove + + +def post_load_hook(): + """ + This hook modifies the stock move splitting logic to: + - Allow splitting stock moves related to repair + orders that are marked as "done", which is prevented + by the core Odoo logic. + - This change enables the creation of backorders for + these split stock moves when the associated repair is completed. + """ + + def _split_for_repair_custom(self, qty, restrict_partner_id=False): + if self.repair_id and self.repair_id.state != "done": + return [] + + return super(StockMove, self)._split(qty, restrict_partner_id) + + if not hasattr(StockMove, "_split_original"): + StockMove._split_original = StockMove._split + StockMove._split = _split_for_repair_custom diff --git a/repair_picking_after_done/i18n/it.po b/repair_picking_after_done/i18n/it.po new file mode 100644 index 00000000..75043073 --- /dev/null +++ b/repair_picking_after_done/i18n/it.po @@ -0,0 +1,117 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * repair_picking_after_done +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-12-20 15:36+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.view_repair_move_transfer_wizard +msgid "Cancel" +msgstr "Annulla" + +#. module: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.repair_type_form_inherit +msgid "Create Transfer" +msgstr "Crea trasferimento" + +#. module: repair_picking_after_done +#: model:ir.model,name:repair_picking_after_done.model_repair_move_transfer +msgid "Create an internal transfer from repaired moves" +msgstr "Crea un trasferimento interno da movimenti di riparazione" + +#. module: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.view_repair_move_transfer_wizard +msgid "Create transfer" +msgstr "Crea trasferimento" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__location_dest_id +msgid "Destination location" +msgstr "Ubicazione di destinazione" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__id +msgid "ID" +msgstr "ID" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__quantity +msgid "Quantity to transfer" +msgstr "Quantità da trasferire" + +#. module: repair_picking_after_done +#. odoo-python +#: code:addons/repair_picking_after_done/wizards/repair_move_transfer.py:0 +#, python-format +msgid "Quantity to transfer must be greater than 0." +msgstr "La quantità da trasferire deve essere maggiore di 0." + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_order__remaining_quantity +msgid "Remaining quantity to be transferred" +msgstr "Quantità rimanente da trasferire" + +#. module: repair_picking_after_done +#: model:ir.model,name:repair_picking_after_done.model_repair_order +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__repair_order_id +msgid "Repair Order" +msgstr "Ordine di riparazione" + +#. module: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.view_repair_move_transfer_wizard +msgid "Transfer Repaired Moves" +msgstr "Movimenti trasferimenti di riparazione" + +#. module: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.view_repair_move_transfer_wizard +msgid "or" +msgstr "o" + +#~ msgid "Transfers" +#~ msgstr "Trasferimenti" + +#, python-format +#~ msgid "Transfers" +#~ msgstr "Trasferimenti" diff --git a/repair_picking_after_done/i18n/repair_picking_after_done.pot b/repair_picking_after_done/i18n/repair_picking_after_done.pot new file mode 100644 index 00000000..7dda7c8a --- /dev/null +++ b/repair_picking_after_done/i18n/repair_picking_after_done.pot @@ -0,0 +1,107 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * repair_picking_after_done +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.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: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.view_repair_move_transfer_wizard +msgid "Cancel" +msgstr "" + +#. module: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.repair_type_form_inherit +msgid "Create Transfer" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model,name:repair_picking_after_done.model_repair_move_transfer +msgid "Create an internal transfer from repaired moves" +msgstr "" + +#. module: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.view_repair_move_transfer_wizard +msgid "Create transfer" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__create_uid +msgid "Created by" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__create_date +msgid "Created on" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__location_dest_id +msgid "Destination location" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__display_name +msgid "Display Name" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__id +msgid "ID" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer____last_update +msgid "Last Modified on" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__write_date +msgid "Last Updated on" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__quantity +msgid "Quantity to transfer" +msgstr "" + +#. module: repair_picking_after_done +#. odoo-python +#: code:addons/repair_picking_after_done/wizards/repair_move_transfer.py:0 +#, python-format +msgid "Quantity to transfer must be greater than 0." +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_order__remaining_quantity +msgid "Remaining quantity to be transferred" +msgstr "" + +#. module: repair_picking_after_done +#: model:ir.model,name:repair_picking_after_done.model_repair_order +#: model:ir.model.fields,field_description:repair_picking_after_done.field_repair_move_transfer__repair_order_id +msgid "Repair Order" +msgstr "" + +#. module: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.view_repair_move_transfer_wizard +msgid "Transfer Repaired Moves" +msgstr "" + +#. module: repair_picking_after_done +#: model_terms:ir.ui.view,arch_db:repair_picking_after_done.view_repair_move_transfer_wizard +msgid "or" +msgstr "" diff --git a/repair_picking_after_done/models/__init__.py b/repair_picking_after_done/models/__init__.py new file mode 100644 index 00000000..3985e558 --- /dev/null +++ b/repair_picking_after_done/models/__init__.py @@ -0,0 +1 @@ +from . import repair diff --git a/repair_picking_after_done/models/repair.py b/repair_picking_after_done/models/repair.py new file mode 100644 index 00000000..3a4838c8 --- /dev/null +++ b/repair_picking_after_done/models/repair.py @@ -0,0 +1,40 @@ +# Copyright (C) 2022 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo import fields, models + + +class Repair(models.Model): + _inherit = "repair.order" + + remaining_quantity = fields.Float( + "Remaining quantity to be transferred", compute="_compute_remaining_quantity" + ) + + def _compute_remaining_quantity(self): + for rec in self: + remaining_quantity = rec.product_qty + if rec.picking_ids: + stock_moves = rec.picking_ids.mapped("move_ids").filtered( + lambda x: x.state != "cancel" + ) + remaining_quantity = rec.product_qty - sum( + stock_moves.mapped("product_uom_qty") + ) + rec.remaining_quantity = remaining_quantity + + def action_transfer_done_moves(self): + self.ensure_one() + return { + "name": "Transfer repair moves", + "type": "ir.actions.act_window", + "view_type": "form", + "view_mode": "form", + "res_model": "repair.move.transfer", + "context": { + "default_repair_order_id": self.id, + "default_quantity": self.remaining_quantity, + "default_remaining_quantity": self.remaining_quantity, + }, + "target": "new", + } diff --git a/repair_picking_after_done/pyproject.toml b/repair_picking_after_done/pyproject.toml new file mode 100644 index 00000000..4231d0cc --- /dev/null +++ b/repair_picking_after_done/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/repair_picking_after_done/readme/CONFIGURE.md b/repair_picking_after_done/readme/CONFIGURE.md new file mode 100644 index 00000000..029bb402 --- /dev/null +++ b/repair_picking_after_done/readme/CONFIGURE.md @@ -0,0 +1 @@ +No configuration needed for this module. diff --git a/repair_picking_after_done/readme/CONTRIBUTORS.md b/repair_picking_after_done/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..f15d30c5 --- /dev/null +++ b/repair_picking_after_done/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- [ForgeFlow](https://forgeflow.com): + + > - Dhaval Talpada \<\> +- [APSL-Nagarro](https://www.apsl.tech): + - Patryk Pyczko \<\> \ No newline at end of file diff --git a/repair_picking_after_done/readme/DESCRIPTION.md b/repair_picking_after_done/readme/DESCRIPTION.md new file mode 100644 index 00000000..c10f2f76 --- /dev/null +++ b/repair_picking_after_done/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module adds the functionality to create transfer of repaired move +once repair order is done. diff --git a/repair_picking_after_done/readme/ROADMAP.md b/repair_picking_after_done/readme/ROADMAP.md new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/repair_picking_after_done/readme/ROADMAP.md @@ -0,0 +1 @@ + diff --git a/repair_picking_after_done/readme/USAGE.md b/repair_picking_after_done/readme/USAGE.md new file mode 100644 index 00000000..47f06395 --- /dev/null +++ b/repair_picking_after_done/readme/USAGE.md @@ -0,0 +1,3 @@ +After repair order is done, You will be able to see button "Transfer" on +repair order's form view. You will be able to create internal transfer +between repair location to any destination location. diff --git a/repair_picking_after_done/security/ir.model.access.csv b/repair_picking_after_done/security/ir.model.access.csv new file mode 100644 index 00000000..08fa9b71 --- /dev/null +++ b/repair_picking_after_done/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 +access_repair_move_transfer_all,repair.move.transfer.all,model_repair_move_transfer,,1,1,1,1 diff --git a/repair_picking_after_done/static/description/icon.png b/repair_picking_after_done/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/repair_picking_after_done/static/description/icon.png differ diff --git a/repair_picking_after_done/static/description/index.html b/repair_picking_after_done/static/description/index.html new file mode 100644 index 00000000..ef3efbac --- /dev/null +++ b/repair_picking_after_done/static/description/index.html @@ -0,0 +1,457 @@ + + + + + +Repair picking after done + + + +
+

Repair picking after done

+ + +

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

+

This module adds the functionality to create transfer of repaired move +once repair order is done.

+
+

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

+

No configuration needed for this module.

+
+
+

Usage

+

After repair order is done, You will be able to see button “Transfer” on +repair order’s form view. You will be able to create internal transfer +between repair location to any destination location.

+
+ +
+

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

+
    +
  • ForgeFlow
  • +
+
+
+

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/repair project on GitHub.

+

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

+
+
+
+ + diff --git a/repair_picking_after_done/tests/__init__.py b/repair_picking_after_done/tests/__init__.py new file mode 100644 index 00000000..d7527875 --- /dev/null +++ b/repair_picking_after_done/tests/__init__.py @@ -0,0 +1,5 @@ +# Copyright (C) 2022 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from . import test_repair_transfers +from . import test_stock_move_split diff --git a/repair_picking_after_done/tests/test_repair_transfers.py b/repair_picking_after_done/tests/test_repair_transfers.py new file mode 100644 index 00000000..e6c46411 --- /dev/null +++ b/repair_picking_after_done/tests/test_repair_transfers.py @@ -0,0 +1,141 @@ +# Copyright (C) 2022 ForgeFlow S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html) + +from odoo.exceptions import UserError +from odoo.tests.common import TransactionCase + + +class TestRepairTransfer(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Create a product with lot/serial tracking + cls.product_with_lot = cls.env["product.product"].create( + { + "name": "Product with lot tracking", + "type": "product", + "tracking": "lot", + "list_price": 10.0, + "categ_id": cls.env.ref("product.product_category_all").id, + } + ) + cls.lot_id = cls.env["stock.lot"].create( + { + "name": "LOT0001", + "product_id": cls.product_with_lot.id, + "company_id": cls.env.company.id, + } + ) + + # Create unique repair orders + cls.repair_r1 = cls.env["repair.order"].create( + { + "product_id": cls.product_with_lot.id, + "location_id": cls.env.ref("stock.stock_location_stock").id, + "lot_id": cls.lot_id.id, + "product_qty": 5.0, + } + ) + cls.repair_r2 = cls.env["repair.order"].create( + { + "product_id": cls.product_with_lot.id, + "location_id": cls.env.ref("stock.stock_location_stock").id, + "lot_id": cls.lot_id.id, + "product_qty": 1.0, + } + ) + + # Create a destination location + cls.stock_location_destination = cls.env["stock.location"].create( + {"name": "Destination Locations", "usage": "internal"} + ) + + # Add stock for repair orders + cls.env["stock.quant"].create( + { + "product_id": cls.product_with_lot.id, + "lot_id": cls.lot_id.id, + "location_id": cls.repair_r1.location_id.id, + "quantity": 5.0, + } + ) + + def setUpRepairOrder(self, repair_order): + # Validate and set the state of the repair order + repair_order.action_validate() + self.assertEqual(repair_order.state, "confirmed") + repair_order.action_repair_start() + self.assertEqual(repair_order.state, "under_repair") + repair_order.action_repair_end() + self.assertEqual(repair_order.state, "done") + + def createTransfer(self, repair_order, quantity): + # Create and execute a transfer wizard + transfer_repair_wizard = self.env["repair.move.transfer"].create( + { + "repair_order_id": repair_order.id, + "quantity": quantity, + "location_dest_id": self.stock_location_destination.id, + "remaining_quantity": repair_order.remaining_quantity, + } + ) + transfer_repair_wizard.action_create_transfer() + + def test_repair_transfer_1(self): + self.setUpRepairOrder(self.repair_r1) + self.createTransfer(self.repair_r1, 1.0) + self.assertEqual(len(self.repair_r1.picking_ids), 1) + + def test_repair_transfer_2(self): + self.setUpRepairOrder(self.repair_r2) + self.createTransfer(self.repair_r2, 1.0) + self.assertEqual(len(self.repair_r2.picking_ids), 1) + + move_line = self.repair_r2.picking_ids.mapped("move_ids").mapped( + "move_line_ids" + )[0] + self.assertEqual(move_line.lot_id.name, "LOT0001") + + def test_multiple_transfers(self): + self.setUpRepairOrder(self.repair_r1) + + # Attempt to create a transfer for 0 items. + with self.assertRaises( + UserError, msg="Quantity to transfer must be greater than 0." + ): + self.createTransfer(self.repair_r1, 0.0) + + # Create the first transfer for 1 item + self.createTransfer(self.repair_r1, 1.0) + + # Update remaining quantity after first transfer + self.repair_r1._compute_remaining_quantity() + + # Create the second transfer for 2 items + self.createTransfer(self.repair_r1, 2.0) + + # Update remaining quantity after second transfer + self.repair_r1._compute_remaining_quantity() + + # Attempt to create a third transfer for 3 items, + # which exceeds the remaining quantity (which is now 2) + with self.assertRaises( + UserError, + msg="Quantity to transfer cannot exceed the remaining " + "quantity in the repair order.", + ): + self.createTransfer(self.repair_r1, 3.0) + + # Check the number of pickings created + self.assertEqual(len(self.repair_r1.picking_ids), 2) + + # Check the total quantity transferred + total_transferred = sum( + qty + for picking in self.repair_r1.picking_ids + for qty in picking.move_ids.mapped("product_uom_qty") + ) + self.assertEqual( + total_transferred, 3.0, "Total transferred quantity should equal to 3.0" + ) diff --git a/repair_picking_after_done/tests/test_stock_move_split.py b/repair_picking_after_done/tests/test_stock_move_split.py new file mode 100644 index 00000000..e3b60ffb --- /dev/null +++ b/repair_picking_after_done/tests/test_stock_move_split.py @@ -0,0 +1,58 @@ +from odoo.tests.common import TransactionCase + + +class TestStockMoveSplit(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Create a product + cls.product = cls.env["product.product"].create( + { + "name": "Test Product", + "type": "product", + } + ) + + # Create a stock location + cls.location = cls.env["stock.location"].create( + { + "name": "Test Location", + "usage": "internal", + } + ) + + # Create a repair order in 'draft' state + cls.repair = cls.env["repair.order"].create( + { + "name": "Repair Order 1", + "product_id": cls.product.id, + "state": "draft", + } + ) + + # Create a stock move linked to the repair order + cls.stock_move = cls.env["stock.move"].create( + { + "name": "Stock Move for Repair Order 1", + "product_id": cls.product.id, + "product_uom_qty": 10.0, + "product_uom": cls.product.uom_id.id, + "repair_id": cls.repair.id, + "state": "confirmed", + "location_id": cls.location.id, + "location_dest_id": cls.location.id, + } + ) + + def test_split_move_with_incomplete_repair(self): + """Ensure a stock move linked to an incomplete repair cannot be split""" + result = self.stock_move._split(5.0) + self.assertEqual( + result, [], "Move should not split as the repair is not 'done'." + ) + + def test_split_move_with_completed_repair(self): + """Ensure a stock move linked to a completed repair can be split""" + self.repair.write({"state": "done"}) + result = self.stock_move._split(5.0) + self.assertTrue(result, "Move should split as the repair is 'done'.") diff --git a/repair_picking_after_done/views/repair.xml b/repair_picking_after_done/views/repair.xml new file mode 100644 index 00000000..040d54a0 --- /dev/null +++ b/repair_picking_after_done/views/repair.xml @@ -0,0 +1,19 @@ + + + + repair.type.inherit + repair.order + + +
+ +
+
+
+
diff --git a/repair_picking_after_done/wizards/__init__.py b/repair_picking_after_done/wizards/__init__.py new file mode 100644 index 00000000..7cf3e0d5 --- /dev/null +++ b/repair_picking_after_done/wizards/__init__.py @@ -0,0 +1 @@ +from . import repair_move_transfer diff --git a/repair_picking_after_done/wizards/repair_move_transfer.py b/repair_picking_after_done/wizards/repair_move_transfer.py new file mode 100644 index 00000000..d7bdd755 --- /dev/null +++ b/repair_picking_after_done/wizards/repair_move_transfer.py @@ -0,0 +1,91 @@ +# Copyright 2022 ForgeFlow S.L. (https://www.forgeflow.com) +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html). + +from odoo import _, fields, models +from odoo.exceptions import UserError +from odoo.tools.float_utils import float_compare + + +class MrpInventoryProcure(models.TransientModel): + _name = "repair.move.transfer" + _description = "Create an internal transfer from repaired moves" + + repair_order_id = fields.Many2one( + "repair.order", string="Repair Order", required=True + ) + location_dest_id = fields.Many2one( + "stock.location", string="Destination location", required=True + ) + quantity = fields.Float("Quantity to transfer", required=True) + remaining_quantity = fields.Float("Remaining Quantity to Transfer") + + def _get_picking_type(self): + self.ensure_one() + warehouse = self.repair_order_id.location_id.warehouse_id + return warehouse.int_type_id + + def _prepare_picking_vals(self): + return { + "partner_id": False, + "user_id": False, + "picking_type_id": self._get_picking_type().id, + "move_type": "direct", + "location_id": self.repair_order_id.location_id.id, + "location_dest_id": self.location_dest_id.id, + } + + def _prepare_stock_move_vals(self, picking): + self.ensure_one() + return { + "name": self.repair_order_id.product_id.name, + "product_id": self.repair_order_id.product_id.id, + "location_id": self.repair_order_id.location_id.id, + "location_dest_id": self.location_dest_id.id, + "picking_id": picking.id, + "state": "draft", + "company_id": picking.company_id.id, + "picking_type_id": self._get_picking_type().id, + "product_uom_qty": self.quantity, + "product_uom": self.repair_order_id.move_id.product_uom.id, + "quantity": 0, + "repair_id": self.repair_order_id.id, + } + + def action_create_transfer(self): + self.ensure_one() + + if ( + float_compare( + self.quantity, + 0.0, + precision_rounding=self.repair_order_id.product_id.uom_id.rounding, + ) + <= 0 + ): + raise UserError(_("Quantity to transfer must be greater than 0.")) + + if ( + float_compare( + self.quantity, + self.repair_order_id.remaining_quantity, + precision_rounding=self.repair_order_id.product_id.uom_id.rounding, + ) + > 0 + ): + raise UserError( + _( + "Quantity to transfer cannot exceed the remaining " + "quantity in the repair order." + ) + ) + + picking = self.env["stock.picking"].create(self._prepare_picking_vals()) + stock_move = self.env["stock.move"].create( + self._prepare_stock_move_vals(picking) + ) + picking.action_assign() + if self.repair_order_id.lot_id: + stock_move.move_line_ids[0].write( + {"lot_id": self.repair_order_id.lot_id.id} + ) + self.repair_order_id._compute_picking_ids() diff --git a/repair_picking_after_done/wizards/repair_move_transfer_views.xml b/repair_picking_after_done/wizards/repair_move_transfer_views.xml new file mode 100644 index 00000000..e41ab880 --- /dev/null +++ b/repair_picking_after_done/wizards/repair_move_transfer_views.xml @@ -0,0 +1,26 @@ + + + + Transfer Repaired Moves + repair.move.transfer + +
+ + + + + + +
+
+
+