From e9e0d6583a2c7aec7a6a578a8c40c3e6f4124814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Alan=20Ramos=20Rodr=C3=ADguez?= Date: Wed, 20 Jan 2021 16:25:41 -0600 Subject: [PATCH] [ADD] repair_procurement: new module to create procurements from repair orders if there is no stock available in source location --- repair_procurement/README.rst | 89 ++++ repair_procurement/__init__.py | 4 + repair_procurement/__manifest__.py | 15 + repair_procurement/i18n/es.po | 97 ++++ repair_procurement/models/__init__.py | 8 + .../models/procurement_group.py | 10 + repair_procurement/models/repair_order.py | 120 +++++ repair_procurement/models/stock_move.py | 10 + repair_procurement/models/stock_picking.py | 12 + repair_procurement/models/stock_rule.py | 13 + repair_procurement/readme/CONTRIBUTORS.rst | 3 + repair_procurement/readme/DESCRIPTION.rst | 2 + repair_procurement/readme/USAGE.rst | 6 + .../static/description/index.html | 433 ++++++++++++++++++ repair_procurement/tests/__init__.py | 4 + repair_procurement/tests/test_repair_order.py | 52 +++ .../views/repair_order_view.xml | 22 + .../odoo/addons/repair_procurement | 1 + setup/repair_procurement/setup.py | 6 + 19 files changed, 907 insertions(+) create mode 100644 repair_procurement/README.rst create mode 100644 repair_procurement/__init__.py create mode 100644 repair_procurement/__manifest__.py create mode 100644 repair_procurement/i18n/es.po create mode 100644 repair_procurement/models/__init__.py create mode 100644 repair_procurement/models/procurement_group.py create mode 100644 repair_procurement/models/repair_order.py create mode 100644 repair_procurement/models/stock_move.py create mode 100644 repair_procurement/models/stock_picking.py create mode 100644 repair_procurement/models/stock_rule.py create mode 100644 repair_procurement/readme/CONTRIBUTORS.rst create mode 100644 repair_procurement/readme/DESCRIPTION.rst create mode 100644 repair_procurement/readme/USAGE.rst create mode 100644 repair_procurement/static/description/index.html create mode 100644 repair_procurement/tests/__init__.py create mode 100644 repair_procurement/tests/test_repair_order.py create mode 100644 repair_procurement/views/repair_order_view.xml create mode 120000 setup/repair_procurement/odoo/addons/repair_procurement create mode 100644 setup/repair_procurement/setup.py diff --git a/repair_procurement/README.rst b/repair_procurement/README.rst new file mode 100644 index 00000000..aed74e0a --- /dev/null +++ b/repair_procurement/README.rst @@ -0,0 +1,89 @@ +================== +Repair Procurement +================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:a555c3d246cb5f50375fdce26f280d36e1f722ecb37c627bba1ca84f71f82fc6 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Frepair-lightgray.png?logo=github + :target: https://github.com/OCA/repair/tree/15.0/repair_procurement + :alt: OCA/repair +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/repair-15-0/repair-15-0-repair_procurement + :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=15.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module generates a procurement of the parts required from source location +when a repair order is confirmed. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +Create a repair order with parts required and define source location. + +Confirm the repair order. + +If the product is not available in source location a procurement will be created. +Depending on the routes configuration it could trigger a purchase order, manufacture order or a transfer. + +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 +~~~~~~~ + +* Jarsa + +Contributors +~~~~~~~~~~~~ + +* `Jarsa `_ + + * Alan Ramos + +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_procurement/__init__.py b/repair_procurement/__init__.py new file mode 100644 index 00000000..8d859bb0 --- /dev/null +++ b/repair_procurement/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +from . import models diff --git a/repair_procurement/__manifest__.py b/repair_procurement/__manifest__.py new file mode 100644 index 00000000..c017a46f --- /dev/null +++ b/repair_procurement/__manifest__.py @@ -0,0 +1,15 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +{ + "name": "Repair Procurement", + "version": "15.0.1.0.0", + "category": "Repair", + "summary": "Create a procurement when a repair order is confirmed.", + "author": "Jarsa, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/repair", + "license": "LGPL-3", + "depends": ["repair"], + "data": ["views/repair_order_view.xml"], + "installable": True, +} diff --git a/repair_procurement/i18n/es.po b/repair_procurement/i18n/es.po new file mode 100644 index 00000000..635b4a1f --- /dev/null +++ b/repair_procurement/i18n/es.po @@ -0,0 +1,97 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * repair_procurement +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-01-20 22:14+0000\n" +"PO-Revision-Date: 2021-01-20 16:14-0600\n" +"Last-Translator: Jesús Alan Ramos Rodríguez \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" +"Language: es\n" +"X-Generator: Poedit 2.4.2\n" + +#. module: repair_procurement +#: model:ir.model.fields,field_description:repair_procurement.field_repair_order__picking_count +msgid "Delivery Orders" +msgstr "Albaranes de salida" + +#. module: repair_procurement +#: model:ir.model.fields,field_description:repair_procurement.field_procurement_group__display_name +#: model:ir.model.fields,field_description:repair_procurement.field_repair_line__display_name +#: model:ir.model.fields,field_description:repair_procurement.field_repair_order__display_name +#: model:ir.model.fields,field_description:repair_procurement.field_stock_move__display_name +#: model:ir.model.fields,field_description:repair_procurement.field_stock_picking__display_name +#: model:ir.model.fields,field_description:repair_procurement.field_stock_rule__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: repair_procurement +#: model:ir.model.fields,field_description:repair_procurement.field_procurement_group__id +#: model:ir.model.fields,field_description:repair_procurement.field_repair_line__id +#: model:ir.model.fields,field_description:repair_procurement.field_repair_order__id +#: model:ir.model.fields,field_description:repair_procurement.field_stock_move__id +#: model:ir.model.fields,field_description:repair_procurement.field_stock_picking__id +#: model:ir.model.fields,field_description:repair_procurement.field_stock_rule__id +msgid "ID" +msgstr "" + +#. module: repair_procurement +#: model:ir.model.fields,field_description:repair_procurement.field_procurement_group____last_update +#: model:ir.model.fields,field_description:repair_procurement.field_repair_line____last_update +#: model:ir.model.fields,field_description:repair_procurement.field_repair_order____last_update +#: model:ir.model.fields,field_description:repair_procurement.field_stock_move____last_update +#: model:ir.model.fields,field_description:repair_procurement.field_stock_picking____last_update +#: model:ir.model.fields,field_description:repair_procurement.field_stock_rule____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: repair_procurement +#: model:ir.model.fields,field_description:repair_procurement.field_repair_order__picking_ids +#: model_terms:ir.ui.view,arch_db:repair_procurement.repair_order_form +msgid "Pickings" +msgstr "Albaranes" + +#. module: repair_procurement +#: model:ir.model,name:repair_procurement.model_procurement_group +#: model:ir.model.fields,field_description:repair_procurement.field_repair_order__procurement_group_id +msgid "Procurement Group" +msgstr "Grupo de abastecimiento" + +#. module: repair_procurement +#: model:ir.model.fields,field_description:repair_procurement.field_stock_move__repair_line_id +msgid "Repair Line" +msgstr "Línea de reparación" + +#. module: repair_procurement +#: model:ir.model,name:repair_procurement.model_repair_line +msgid "Repair Line (parts)" +msgstr "Línea de Reparación (partes)" + +#. module: repair_procurement +#: model:ir.model,name:repair_procurement.model_repair_order +#: model:ir.model.fields,field_description:repair_procurement.field_procurement_group__repair_id +#: model:ir.model.fields,field_description:repair_procurement.field_stock_picking__repair_id +msgid "Repair Order" +msgstr "Orden de reparación" + +#. module: repair_procurement +#: model:ir.model,name:repair_procurement.model_stock_move +msgid "Stock Move" +msgstr "Movimiento de existencias" + +#. module: repair_procurement +#: model:ir.model,name:repair_procurement.model_stock_rule +msgid "Stock Rule" +msgstr "Regla de Inventario" + +#. module: repair_procurement +#: model:ir.model,name:repair_procurement.model_stock_picking +msgid "Transfer" +msgstr "Albarán" diff --git a/repair_procurement/models/__init__.py b/repair_procurement/models/__init__.py new file mode 100644 index 00000000..65594cdc --- /dev/null +++ b/repair_procurement/models/__init__.py @@ -0,0 +1,8 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +from . import repair_order +from . import procurement_group +from . import stock_move +from . import stock_picking +from . import stock_rule diff --git a/repair_procurement/models/procurement_group.py b/repair_procurement/models/procurement_group.py new file mode 100644 index 00000000..45ca6dbb --- /dev/null +++ b/repair_procurement/models/procurement_group.py @@ -0,0 +1,10 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +from odoo import fields, models + + +class ProcurementGroup(models.Model): + _inherit = "procurement.group" + + repair_id = fields.Many2one("repair.order", "Repair Order") diff --git a/repair_procurement/models/repair_order.py b/repair_procurement/models/repair_order.py new file mode 100644 index 00000000..289354eb --- /dev/null +++ b/repair_procurement/models/repair_order.py @@ -0,0 +1,120 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +from odoo import api, fields, models +from odoo.tools import float_compare + + +class RepairOrder(models.Model): + _inherit = "repair.order" + + procurement_group_id = fields.Many2one("procurement.group", copy=False) + picking_ids = fields.One2many("stock.picking", "repair_id", string="Pickings") + picking_count = fields.Integer( + string="Delivery Orders", compute="_compute_picking_count" + ) + + @api.depends("picking_ids") + def _compute_picking_count(self): + for order in self: + order.picking_count = len(order.picking_ids) + + def action_view_pickings(self): + action = self.env["ir.actions.actions"]._for_xml_id( + "stock.action_picking_tree_all" + ) + + pickings = self.mapped("picking_ids") + if len(pickings) > 1: + action["domain"] = [("id", "in", pickings.ids)] + elif pickings: + form_view = [(self.env.ref("stock.view_picking_form").id, "form")] + if "views" in action: + action["views"] = form_view + [ + (state, view) for state, view in action["views"] if view != "form" + ] + else: + action["views"] = form_view + action["res_id"] = pickings.id + action["context"] = dict( + self._context, + default_picking_type_id=pickings[0].picking_type_id.id, + default_origin=self.name, + default_group_id=pickings[0].group_id.id, + ) + return action + + def action_validate(self): + precision = self.env["decimal.precision"].precision_get( + "Product Unit of Measure" + ) + procurements = [] + for line in self.mapped("operations"): + if line.product_id.type in ("consu", "service"): + continue + available_qty = self.env["stock.quant"]._get_available_quantity( + line.product_id, line.location_id, strict=True + ) + repair_qty = line.product_uom._compute_quantity( + line.product_uom_qty, line.product_id.uom_id + ) + if ( + float_compare(available_qty, repair_qty, precision_digits=precision) + == 1 + ): + continue + group = line._get_procurement_group() + values = line._prepare_procurement_values(group_id=group) + product_qty, procurement_uom = line.product_uom._adjust_uom_quantities( + line.product_uom_qty, line.product_id.uom_id + ) + procurements.append( + self.env["procurement.group"].Procurement( + line.product_id, + product_qty, + procurement_uom, + line.location_id, + line.name, + line.repair_id.name, + line.repair_id.company_id, + values, + ) + ) + if procurements: + self.env["procurement.group"].run(procurements) + return super().action_validate() + + def action_repair_cancel(self): + self.picking_ids.filtered( + lambda p: p.state not in ["cancel", "done"] + ).action_cancel() + return super().action_repair_cancel() + + +class RepairOrderLine(models.Model): + _inherit = "repair.line" + + def _get_procurement_group(self): + self.ensure_one() + if not self.repair_id.procurement_group_id: + group = self.env["procurement.group"].create( + self._prepare_procurement_group_vals() + ) + self.repair_id.procurement_group_id = group + return self.repair_id.procurement_group_id + + def _prepare_procurement_group_vals(self): + self.ensure_one() + return { + "name": self.repair_id.name, + "move_type": "direct", + "repair_id": self.repair_id.id, + } + + def _prepare_procurement_values(self, group_id=False): + self.ensure_one() + return { + "group_id": group_id, + "repair_line_id": self.id, + "warehouse_id": self.location_id.warehouse_id, + } diff --git a/repair_procurement/models/stock_move.py b/repair_procurement/models/stock_move.py new file mode 100644 index 00000000..c2a9ac86 --- /dev/null +++ b/repair_procurement/models/stock_move.py @@ -0,0 +1,10 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +from odoo import fields, models + + +class StockMove(models.Model): + _inherit = "stock.move" + + repair_line_id = fields.Many2one("repair.line", index=True) diff --git a/repair_procurement/models/stock_picking.py b/repair_procurement/models/stock_picking.py new file mode 100644 index 00000000..8f6d454e --- /dev/null +++ b/repair_procurement/models/stock_picking.py @@ -0,0 +1,12 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +from odoo import fields, models + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + repair_id = fields.Many2one( + related="group_id.repair_id", string="Repair Order", store=True + ) diff --git a/repair_procurement/models/stock_rule.py b/repair_procurement/models/stock_rule.py new file mode 100644 index 00000000..e5f1e802 --- /dev/null +++ b/repair_procurement/models/stock_rule.py @@ -0,0 +1,13 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +from odoo import models + + +class StockRule(models.Model): + _inherit = "stock.rule" + + def _get_custom_move_fields(self): + fields = super()._get_custom_move_fields() + fields += ["repair_line_id"] + return fields diff --git a/repair_procurement/readme/CONTRIBUTORS.rst b/repair_procurement/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..1e715abd --- /dev/null +++ b/repair_procurement/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Jarsa `_ + + * Alan Ramos diff --git a/repair_procurement/readme/DESCRIPTION.rst b/repair_procurement/readme/DESCRIPTION.rst new file mode 100644 index 00000000..fbab8720 --- /dev/null +++ b/repair_procurement/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module generates a procurement of the parts required from source location +when a repair order is confirmed. diff --git a/repair_procurement/readme/USAGE.rst b/repair_procurement/readme/USAGE.rst new file mode 100644 index 00000000..f6bc89bb --- /dev/null +++ b/repair_procurement/readme/USAGE.rst @@ -0,0 +1,6 @@ +Create a repair order with parts required and define source location. + +Confirm the repair order. + +If the product is not available in source location a procurement will be created. +Depending on the routes configuration it could trigger a purchase order, manufacture order or a transfer. diff --git a/repair_procurement/static/description/index.html b/repair_procurement/static/description/index.html new file mode 100644 index 00000000..83fd0176 --- /dev/null +++ b/repair_procurement/static/description/index.html @@ -0,0 +1,433 @@ + + + + + + +Repair Procurement + + + +
+

Repair Procurement

+ + +

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

+

This module generates a procurement of the parts required from source location +when a repair order is confirmed.

+

Table of contents

+ +
+

Usage

+

Create a repair order with parts required and define source location.

+

Confirm the repair order.

+

If the product is not available in source location a procurement will be created. +Depending on the routes configuration it could trigger a purchase order, manufacture order or a transfer.

+
+
+

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

+
    +
  • Jarsa
  • +
+
+
+

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_procurement/tests/__init__.py b/repair_procurement/tests/__init__.py new file mode 100644 index 00000000..d9c44d65 --- /dev/null +++ b/repair_procurement/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +from . import test_repair_order diff --git a/repair_procurement/tests/test_repair_order.py b/repair_procurement/tests/test_repair_order.py new file mode 100644 index 00000000..7d35c051 --- /dev/null +++ b/repair_procurement/tests/test_repair_order.py @@ -0,0 +1,52 @@ +# Copyright 2021 Jarsa +# License LGPL-3 - See http://www.gnu.org/licenses/lgpl-3.0.html + +from odoo.tests.common import TransactionCase + + +class TestRepairOrder(TransactionCase): + def setUp(self): + super().setUp() + self.repair = self.env.ref("repair.repair_r1") + self.stock_loc = self.env.ref("stock.stock_location_stock") + self.spare_loc = self.env["stock.location"].create( + { + "name": "Spares", + "usage": "internal", + } + ) + self.internal_type = self.env.ref("stock.picking_type_internal") + self.spare_product = self.env.ref("product.product_product_11") + self.route = self.env["stock.location.route"].create( + { + "name": "Stock -> Spares", + "product_selectable": True, + "rule_ids": [ + ( + 0, + 0, + { + "name": "Stock -> Spares", + "action": "pull", + "picking_type_id": self.internal_type.id, + "location_src_id": self.stock_loc.id, + "location_id": self.spare_loc.id, + "procure_method": "make_to_stock", + "group_propagation_option": "propagate", + }, + ) + ], + } + ) + self.spare_product.write({"route_ids": [(4, self.route.id)]}) + + def test_repair_order(self): + self.repair.operations.write( + { + "location_id": self.spare_loc.id, + } + ) + self.repair.action_validate() + self.assertEqual(len(self.repair.picking_ids), 1) + self.repair.action_repair_cancel() + self.assertEqual(self.repair.picking_ids.state, "cancel") diff --git a/repair_procurement/views/repair_order_view.xml b/repair_procurement/views/repair_order_view.xml new file mode 100644 index 00000000..755e96d2 --- /dev/null +++ b/repair_procurement/views/repair_order_view.xml @@ -0,0 +1,22 @@ + + + + repair_order.form + repair.order + + + + + + + + + diff --git a/setup/repair_procurement/odoo/addons/repair_procurement b/setup/repair_procurement/odoo/addons/repair_procurement new file mode 120000 index 00000000..9e2d5131 --- /dev/null +++ b/setup/repair_procurement/odoo/addons/repair_procurement @@ -0,0 +1 @@ +../../../../repair_procurement \ No newline at end of file diff --git a/setup/repair_procurement/setup.py b/setup/repair_procurement/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/repair_procurement/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)