diff --git a/repair_sale_order/README.rst b/repair_sale_order/README.rst new file mode 100644 index 000000000..a46b494b7 --- /dev/null +++ b/repair_sale_order/README.rst @@ -0,0 +1,104 @@ +==================== +Repair To Sale Order +==================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmanufacture-lightgray.png?logo=github + :target: https://github.com/OCA/manufacture/tree/14.0/repair_sale_order + :alt: OCA/manufacture +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/manufacture-14-0/manufacture-14-0-repair_sale_order + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/129/14.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module add possibility to create a sale order for spare parts on repair order + + +.. 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: + +Usage +===== + +On Repairs / Configuration / Repair Type you can set active Create Sale Order +this change behavior of repair form and can create Sales Order from Repair Order + +Your repair order will change to Done status when Sales Orders related confirmed, +if Sales Order Canceled, Repair Order will set as Confirmed + +Known issues / Roadmap +====================== + +Possible improvements for future versions: + +* The main use case is for make spare parts sell from repair orders, + we need a way to support cases when we can send freely spare parts to customer + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* ForgeFlow + +Contributors +~~~~~~~~~~~~ + +* Christopher Ormaza + +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-ChrisOForgeFlow| image:: https://github.com/ChrisOForgeFlow.png?size=40px + :target: https://github.com/ChrisOForgeFlow + :alt: ChrisOForgeFlow + +Current `maintainer `__: + +|maintainer-ChrisOForgeFlow| + +This module is part of the `OCA/manufacture `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/repair_sale_order/__init__.py b/repair_sale_order/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/repair_sale_order/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/repair_sale_order/__manifest__.py b/repair_sale_order/__manifest__.py new file mode 100644 index 000000000..7faa38d39 --- /dev/null +++ b/repair_sale_order/__manifest__.py @@ -0,0 +1,22 @@ +# Copyright 2022 ForgeFlow S.L. (https://forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Repair To Sale Order", + "version": "14.0.1.0.0", + "author": "ForgeFlow, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/manufacture", + "summary": "Repair To Sale Order", + "category": "Repair", + "depends": ["repair", "repair_type", "sale", "sales_team"], + "data": [ + "views/repair_order_view.xml", + "views/sale_order_view.xml", + "views/repair_type_view.xml", + ], + "installable": True, + "development_status": "Alpha", + "license": "AGPL-3", + "maintainers": ["ChrisOForgeFlow"], + "application": False, +} diff --git a/repair_sale_order/models/__init__.py b/repair_sale_order/models/__init__.py new file mode 100644 index 000000000..07d17c57d --- /dev/null +++ b/repair_sale_order/models/__init__.py @@ -0,0 +1,3 @@ +from . import repair_order +from . import sale_order +from . import repair_type diff --git a/repair_sale_order/models/repair_order.py b/repair_sale_order/models/repair_order.py new file mode 100644 index 000000000..be013af21 --- /dev/null +++ b/repair_sale_order/models/repair_order.py @@ -0,0 +1,102 @@ +# Copyright 2022 ForgeFlow S.L. (https://forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import _, fields, models +from odoo.exceptions import UserError + + +class RepairOrder(models.Model): + + _inherit = "repair.order" + + create_sale_order = fields.Boolean(related="repair_type_id.create_sale_order") + sale_order_ids = fields.Many2many( + comodel_name="sale.order", + string="Sale orders", + compute="_compute_sale_order", + ) + sale_order_count = fields.Integer( + string="Sale order count", + compute="_compute_sale_order", + ) + + def _compute_sale_order(self): + for rec in self: + rec.sale_order_ids = rec.mapped("operations.sale_line_id.order_id").ids + rec.sale_order_count = len(rec.sale_order_ids) + + def action_show_sales_order(self, new_orders=False): + action = self.env["ir.actions.actions"]._for_xml_id("sale.action_quotations") + form_view = [(self.env.ref("sale.view_order_form").id, "form")] + orders = new_orders + if not new_orders: + orders = self.mapped("sale_order_ids") + if len(orders) == 1: + 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"] = orders.ids[0] + else: + action["domain"] = [("id", "in", orders.ids)] + return action + + def _get_sale_order_data(self): + self.ensure_one() + res = { + "partner_id": self.partner_id.id, + "partner_shipping_id": self.address_id.id, + "origin": self.display_name, + "note": self.quotation_notes, + } + return res + + def action_create_sale_order(self): + order_model = self.env["sale.order"].sudo() + order_line_model = self.env["sale.order.line"].sudo() + orders = order_model.browse() + for rec in self.filtered( + lambda x: not x.sale_order_ids and x.create_sale_order + ): + sale_order_data = rec._get_sale_order_data() + sale_order = order_model.create(sale_order_data) + orders |= sale_order + sale_order.onchange_partner_id() + for line in rec.operations: + sale_order_line = order_line_model.create( + line._get_sale_line_data(sale_order) + ) + line.sale_line_id = sale_order_line.id + return self.action_show_sales_order(orders) + + def action_validate(self): + if self.filtered(lambda x: x.create_sale_order and not x.operations): + raise UserError( + _( + "You should input at least one part line to" + " continue on create Sales Order from Repair Order" + ) + ) + return super().action_validate() + + +class RepairLine(models.Model): + + _inherit = "repair.line" + + def _get_sale_line_data(self, sale_order): + self.ensure_one() + res = { + "product_id": self.product_id.id, + "name": self.name, + "product_uom_qty": self.product_uom_qty, + "price_unit": self.price_unit, + "tax_id": self.tax_id and [(6, 0, self.tax_id.ids)] or [], + "order_id": sale_order.id, + } + return res + + sale_line_id = fields.Many2one( + comodel_name="sale.order.line", string="Sale line", copy=False + ) diff --git a/repair_sale_order/models/repair_type.py b/repair_sale_order/models/repair_type.py new file mode 100644 index 000000000..b7fb5c3c4 --- /dev/null +++ b/repair_sale_order/models/repair_type.py @@ -0,0 +1,13 @@ +# Copyright 2022 ForgeFlow S.L. (https://forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import fields, models + + +class RepairType(models.Model): + + _inherit = "repair.type" + + create_sale_order = fields.Boolean( + string="Create sale order?", + default=False, + ) diff --git a/repair_sale_order/models/sale_order.py b/repair_sale_order/models/sale_order.py new file mode 100644 index 000000000..d681df179 --- /dev/null +++ b/repair_sale_order/models/sale_order.py @@ -0,0 +1,85 @@ +# Copyright 2022 ForgeFlow S.L. (https://forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import fields, models +from odoo.tools.safe_eval import safe_eval + + +class SaleOrder(models.Model): + + _inherit = "sale.order" + + repair_order_ids = fields.Many2many( + comodel_name="repair.order", + string="Repair orders", + compute="_compute_repair_order", + ) + repair_order_count = fields.Integer( + string="Repair orders count", + compute="_compute_repair_order", + ) + + def _compute_repair_order(self): + for rec in self: + rec.repair_order_ids = rec.mapped( + "order_line.repair_line_ids.repair_id" + ).ids + rec.repair_order_count = len(rec.repair_order_ids) + + def action_show_repair_order(self): + action = self.env["ir.actions.actions"]._for_xml_id( + "repair.action_repair_order_tree" + ) + form_view = [(self.env.ref("repair.view_repair_order_form").id, "form")] + orders = self.mapped("repair_order_ids") + if len(orders) == 1: + 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"] = orders.ids[0] + else: + action["domain"] += [("id", "in", orders.ids)] + domain = safe_eval(action["domain"]) or [] + domain += [("id", "in", orders.ids)] + action["domain"] = action["domain"] + return action + + def _change_repair_order_status_done(self): + for repair_order in self.mapped("repair_order_ids"): + repair_order.write( + { + "state": "done", + } + ) + + def _change_repair_order_status_confirmed(self): + for repair_order in self.mapped("repair_order_ids"): + repair_order.write( + { + "state": "confirmed", + } + ) + + def action_confirm(self): + res = super().action_confirm() + self._change_repair_order_status_done() + return res + + def action_cancel(self): + res = super().action_cancel() + self._change_repair_order_status_confirmed() + return res + + +class SaleOrderLine(models.Model): + + _inherit = "sale.order.line" + + repair_line_ids = fields.One2many( + comodel_name="repair.line", + inverse_name="sale_line_id", + string="Repair lines", + required=False, + ) diff --git a/repair_sale_order/readme/CONTRIBUTORS.rst b/repair_sale_order/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..b647a292e --- /dev/null +++ b/repair_sale_order/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Christopher Ormaza diff --git a/repair_sale_order/readme/DESCRIPTION.rst b/repair_sale_order/readme/DESCRIPTION.rst new file mode 100644 index 000000000..58afe9828 --- /dev/null +++ b/repair_sale_order/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +This module add possibility to create a sale order for spare parts on repair order diff --git a/repair_sale_order/readme/ROADMAP.rst b/repair_sale_order/readme/ROADMAP.rst new file mode 100644 index 000000000..1df9cb177 --- /dev/null +++ b/repair_sale_order/readme/ROADMAP.rst @@ -0,0 +1,4 @@ +Possible improvements for future versions: + +* The main use case is for make spare parts sell from repair orders, + we need a way to support cases when we can send freely spare parts to customer diff --git a/repair_sale_order/readme/USAGE.rst b/repair_sale_order/readme/USAGE.rst new file mode 100644 index 000000000..895213413 --- /dev/null +++ b/repair_sale_order/readme/USAGE.rst @@ -0,0 +1,5 @@ +On Repairs / Configuration / Repair Type you can set active Create Sale Order +this change behavior of repair form and can create Sales Order from Repair Order + +Your repair order will change to Done status when Sales Orders related confirmed, +if Sales Order Canceled, Repair Order will set as Confirmed diff --git a/repair_sale_order/static/description/index.html b/repair_sale_order/static/description/index.html new file mode 100644 index 000000000..6ee2497da --- /dev/null +++ b/repair_sale_order/static/description/index.html @@ -0,0 +1,444 @@ + + + + + + +Repair To Sale Order + + + +
+

Repair To Sale Order

+ + +

Alpha License: AGPL-3 OCA/manufacture Translate me on Weblate Try me on Runbot

+

This module add possibility to create a sale order for spare parts on repair order

+
+

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

+ +
+

Usage

+

On Repairs / Configuration / Repair Type you can set active Create Sale Order +this change behavior of repair form and can create Sales Order from Repair Order

+

Your repair order will change to Done status when Sales Orders related confirmed, +if Sales Order Canceled, Repair Order will set as Confirmed

+
+
+

Known issues / Roadmap

+

Possible improvements for future versions:

+
    +
  • The main use case is for make spare parts sell from repair orders, +we need a way to support cases when we can send freely spare parts to customer
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • 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.

+

Current maintainer:

+

ChrisOForgeFlow

+

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

+

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

+
+
+
+ + diff --git a/repair_sale_order/tests/__init__.py b/repair_sale_order/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/repair_sale_order/tests/test_repair_sale_order.py b/repair_sale_order/tests/test_repair_sale_order.py new file mode 100644 index 000000000..5ce0e0524 --- /dev/null +++ b/repair_sale_order/tests/test_repair_sale_order.py @@ -0,0 +1,12 @@ +# Copyright 2022 ForgeFlow S.L. (https://forgeflow.com) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo.tests.common import SavepointCase + + +class TestRepairSaleOrder(SavepointCase): + def setUp(self): + super().setUp() + + def test_repair_sale_order(self): + return True diff --git a/repair_sale_order/views/repair_order_view.xml b/repair_sale_order/views/repair_order_view.xml new file mode 100644 index 000000000..3c65877c5 --- /dev/null +++ b/repair_sale_order/views/repair_order_view.xml @@ -0,0 +1,84 @@ + + + + + + repair.order.form.view + repair.order + + + + + + + + + + {'invisible': ['|', ('create_sale_order', '=', True), '&', ('state','!=','confirmed'), '!', '&', ('state','=','ready'), ('invoice_method','=','b4repair')]} + + + + + + + + + diff --git a/setup/repair_sale_order/odoo/addons/repair_sale_order b/setup/repair_sale_order/odoo/addons/repair_sale_order new file mode 120000 index 000000000..c0fd2e253 --- /dev/null +++ b/setup/repair_sale_order/odoo/addons/repair_sale_order @@ -0,0 +1 @@ +../../../../repair_sale_order \ No newline at end of file diff --git a/setup/repair_sale_order/setup.py b/setup/repair_sale_order/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/repair_sale_order/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)