From 755ae2b232b1e2def371e662fd5e1a11d9fef022 Mon Sep 17 00:00:00 2001 From: ilo Date: Tue, 29 Oct 2024 11:11:18 -0300 Subject: [PATCH] [17.0][ADD] fieldservice_equipment_stock_return: add return order type --- .../README.rst | 115 +++++ .../__init__.py | 0 .../__manifest__.py | 30 ++ .../data/fsm_order_type.xml | 9 + .../models/__init__.py | 5 + .../models/fsm_equipment.py | 33 ++ .../models/fsm_order.py | 94 ++++ .../models/fsm_order_type.py | 17 + .../pyproject.toml | 3 + .../readme/CONFIGURE.md | 8 + .../readme/CONTRIBUTORS.md | 1 + .../readme/CREDITS.md | 3 + .../readme/DESCRIPTION.md | 1 + .../static/description/icon.png | 0 .../static/description/index.html | 442 ++++++++++++++++++ .../tests/__init__.py | 0 .../tests/test_fsm_equipment.py | 87 ++++ .../tests/test_stock_move.py | 0 .../views/fsm_equipment.xml | 28 ++ .../views/fsm_order_type_view.xml | 23 + .../views/fsm_order_view.xml | 25 + 21 files changed, 924 insertions(+) create mode 100644 fieldservice_equipment_stock_return/README.rst create mode 100644 fieldservice_equipment_stock_return/__init__.py create mode 100644 fieldservice_equipment_stock_return/__manifest__.py create mode 100644 fieldservice_equipment_stock_return/data/fsm_order_type.xml create mode 100644 fieldservice_equipment_stock_return/models/__init__.py create mode 100644 fieldservice_equipment_stock_return/models/fsm_equipment.py create mode 100644 fieldservice_equipment_stock_return/models/fsm_order.py create mode 100644 fieldservice_equipment_stock_return/models/fsm_order_type.py create mode 100644 fieldservice_equipment_stock_return/pyproject.toml create mode 100644 fieldservice_equipment_stock_return/readme/CONFIGURE.md create mode 100644 fieldservice_equipment_stock_return/readme/CONTRIBUTORS.md create mode 100644 fieldservice_equipment_stock_return/readme/CREDITS.md create mode 100644 fieldservice_equipment_stock_return/readme/DESCRIPTION.md create mode 100644 fieldservice_equipment_stock_return/static/description/icon.png create mode 100644 fieldservice_equipment_stock_return/static/description/index.html create mode 100644 fieldservice_equipment_stock_return/tests/__init__.py create mode 100644 fieldservice_equipment_stock_return/tests/test_fsm_equipment.py create mode 100644 fieldservice_equipment_stock_return/tests/test_stock_move.py create mode 100644 fieldservice_equipment_stock_return/views/fsm_equipment.xml create mode 100644 fieldservice_equipment_stock_return/views/fsm_order_type_view.xml create mode 100644 fieldservice_equipment_stock_return/views/fsm_order_view.xml diff --git a/fieldservice_equipment_stock_return/README.rst b/fieldservice_equipment_stock_return/README.rst new file mode 100644 index 0000000000..d310a7715c --- /dev/null +++ b/fieldservice_equipment_stock_return/README.rst @@ -0,0 +1,115 @@ +====================================== +Field Service - Stock Equipment Return +====================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:ccef108ff488fd2820ce9403b90512a866de4e4edaa606f7f03418b70520b741 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Ffield--service-lightgray.png?logo=github + :target: https://github.com/OCA/field-service/tree/17.0/fieldservice_equipment_stock_return + :alt: OCA/field-service +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/field-service-17-0/field-service-17-0-fieldservice_equipment_stock_return + :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/field-service&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows you to return FSM equipments creating a new picking. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Check ``Field Service - Stock Equipment`` module configuration. + +To configure this module, you need to: + +- Go to Field Service > Configuration > Order Types +- Check if you have at least one ``return`` order type +- Set the ``Picking Type`` linked to the ``return`` order type + +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 +------- + +* Camptocamp +* Italo LOPES + +Contributors +------------ + +- Italo LOPES + +Other credits +------------- + +The development of this module has been financially supported by: + +- Camptocamp SA + +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-brian10048| image:: https://github.com/brian10048.png?size=40px + :target: https://github.com/brian10048 + :alt: brian10048 +.. |maintainer-wolfhall| image:: https://github.com/wolfhall.png?size=40px + :target: https://github.com/wolfhall + :alt: wolfhall +.. |maintainer-max3903| image:: https://github.com/max3903.png?size=40px + :target: https://github.com/max3903 + :alt: max3903 +.. |maintainer-smangukiya| image:: https://github.com/smangukiya.png?size=40px + :target: https://github.com/smangukiya + :alt: smangukiya +.. |maintainer-imlopes| image:: https://github.com/imlopes.png?size=40px + :target: https://github.com/imlopes + :alt: imlopes + +Current `maintainers `__: + +|maintainer-brian10048| |maintainer-wolfhall| |maintainer-max3903| |maintainer-smangukiya| |maintainer-imlopes| + +This module is part of the `OCA/field-service `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/fieldservice_equipment_stock_return/__init__.py b/fieldservice_equipment_stock_return/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/fieldservice_equipment_stock_return/__manifest__.py b/fieldservice_equipment_stock_return/__manifest__.py new file mode 100644 index 0000000000..8e814826b3 --- /dev/null +++ b/fieldservice_equipment_stock_return/__manifest__.py @@ -0,0 +1,30 @@ +# Copyright 2024 Camptocamp SA (https://www.camptocamp.com). +# @author: Italo Lopes +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Field Service - Stock Equipment Return", + "summary": "Integrate return orders for field service equipments", + "version": "17.0.1.0.0", + "category": "Field Service", + "author": "Camptocamp, " "Italo LOPES, " "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/field-service", + "depends": [ + "fieldservice_equipment_stock", + ], + "data": [ + "data/fsm_order_type.xml", + "views/fsm_equipment.xml", + "views/fsm_order_view.xml", + "views/fsm_order_type_view.xml", + ], + "license": "AGPL-3", + "development_status": "Beta", + "maintainers": [ + "brian10048", + "wolfhall", + "max3903", + "smangukiya", + "imlopes", + ], +} diff --git a/fieldservice_equipment_stock_return/data/fsm_order_type.xml b/fieldservice_equipment_stock_return/data/fsm_order_type.xml new file mode 100644 index 0000000000..39d0acb902 --- /dev/null +++ b/fieldservice_equipment_stock_return/data/fsm_order_type.xml @@ -0,0 +1,9 @@ + + + + + Return + return + + + diff --git a/fieldservice_equipment_stock_return/models/__init__.py b/fieldservice_equipment_stock_return/models/__init__.py new file mode 100644 index 0000000000..7e821bbc7f --- /dev/null +++ b/fieldservice_equipment_stock_return/models/__init__.py @@ -0,0 +1,5 @@ +from . import ( + fsm_equipment, + fsm_order, + fsm_order_type, +) diff --git a/fieldservice_equipment_stock_return/models/fsm_equipment.py b/fieldservice_equipment_stock_return/models/fsm_equipment.py new file mode 100644 index 0000000000..02909aebf8 --- /dev/null +++ b/fieldservice_equipment_stock_return/models/fsm_equipment.py @@ -0,0 +1,33 @@ +# Copyright 2024 Camptocamp SA (https://www.camptocamp.com). +# @author: Italo Lopes +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, models +from odoo.exceptions import UserError + + +class FSMEquipment(models.Model): + _inherit = "fsm.equipment" + + def create_equipment_order_return(self): + self.ensure_one() + order_type = self.env.ref( + "fieldservice_equipment_stock_return.fsm_order_type_return", + raise_if_not_found=False, + ) + if not order_type: + order_type = self.env["fsm.order.type"].search( + [("internal_type", "=", "return")], limit=1 + ) + if not order_type: + raise UserError(_("No return order type found.")) + return { + "name": "Return Equipment", + "type": "ir.actions.act_window", + "res_model": "fsm.order", + "view_mode": "form", + "context": { + "default_equipment_id": self.id, + "default_type": order_type and order_type.id or False, + }, + } diff --git a/fieldservice_equipment_stock_return/models/fsm_order.py b/fieldservice_equipment_stock_return/models/fsm_order.py new file mode 100644 index 0000000000..24c9538f30 --- /dev/null +++ b/fieldservice_equipment_stock_return/models/fsm_order.py @@ -0,0 +1,94 @@ +# Copyright 2024 Camptocamp SA (https://www.camptocamp.com). +# @author: Italo Lopes +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, models +from odoo.exceptions import ValidationError + + +class FSMOrder(models.Model): + _inherit = "fsm.order" + + def _prepare_return_procurement_group_values(self): + self.ensure_one() + return { + "name": self.display_name, + "fsm_order_id": self.id, + "move_type": "direct", + } + + def _get_equipment_current_location(self): + self.ensure_one() + if not self.equipment_id: + raise ValidationError(_("Cannot create return order without an equipment.")) + if self.equipment_id.current_stock_location_id: + return self.equipment_id.current_stock_location_id + elif self.equipment_id.current_location_id: + return ( + self.equipment_id.current_location_id + and self.equipment_id.current_location_id.inventory_location_id + ) + else: + raise ValidationError( + _("Impossible to find the equipment current location.") + ) + + def _prepare_return_stock_picking_values(self): + self.ensure_one() + source_location_id = self._get_equipment_current_location() + return { + "picking_type_id": self.type.picking_type_id.id, + "origin": self.display_name, + "location_dest_id": self.type.picking_type_id.default_location_dest_id.id, + "location_id": source_location_id and source_location_id.id, + "fsm_order_id": self.id, + "group_id": self.procurement_group_id.id, + } + + def _prepare_return_stock_move_values(self): + self.ensure_one() + source_location_id = self._get_equipment_current_location() + return { + "name": self.display_name, + "product_id": self.equipment_id.product_id.id, + "product_uom_qty": 1, + "product_uom": self.equipment_id.product_id.uom_id.id, + "location_id": source_location_id.id, + "location_dest_id": self.type.picking_type_id.default_location_dest_id.id, + "group_id": self.procurement_group_id.id, + "fsm_order_id": self.id, + "lot_ids": [(4, self.equipment_id.lot_id.id)] + if self.equipment_id.lot_id + else False, + } + + @api.model + def create(self, vals): + # if FSM order with type return is created then + # create a picking order + order = super().create(vals) + if order.type.internal_type == "return": + if order.equipment_id and order.type.picking_type_id: + group = self.env["procurement.group"].search( + [("fsm_order_id", "=", order.id)] + ) + if not group: + values = order._prepare_return_procurement_group_values() + group = self.env["procurement.group"].create(values) + order.procurement_group_id = group and group.id + return_picking_values = order._prepare_return_stock_picking_values() + new_picking = self.env["stock.picking"].create(return_picking_values) + return_move_values = order._prepare_return_stock_move_values() + return_move_values["picking_id"] = new_picking.id + self.env["stock.move"].create(return_move_values) + new_picking.action_confirm() + + elif not order.type.picking_type_id: + raise ValidationError( + _( + "Cannot create a return order because " + "order type does not have a current " + "Picking Type." + ) + ) + return order diff --git a/fieldservice_equipment_stock_return/models/fsm_order_type.py b/fieldservice_equipment_stock_return/models/fsm_order_type.py new file mode 100644 index 0000000000..f295be005f --- /dev/null +++ b/fieldservice_equipment_stock_return/models/fsm_order_type.py @@ -0,0 +1,17 @@ +# Copyright 2024 Camptocamp SA (https://www.camptocamp.com). +# @author: Italo Lopes +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class FsmOrderType(models.Model): + _inherit = "fsm.order.type" + + internal_type = fields.Selection( + selection_add=[("return", "Return")], + ) + picking_type_id = fields.Many2one( + "stock.picking.type", + domain=[("code", "=", "incoming")], + ) diff --git a/fieldservice_equipment_stock_return/pyproject.toml b/fieldservice_equipment_stock_return/pyproject.toml new file mode 100644 index 0000000000..4231d0cccb --- /dev/null +++ b/fieldservice_equipment_stock_return/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/fieldservice_equipment_stock_return/readme/CONFIGURE.md b/fieldservice_equipment_stock_return/readme/CONFIGURE.md new file mode 100644 index 0000000000..3c334e1ad9 --- /dev/null +++ b/fieldservice_equipment_stock_return/readme/CONFIGURE.md @@ -0,0 +1,8 @@ +Check `Field Service - Stock Equipment` module configuration. + + +To configure this module, you need to: + +- Go to Field Service \> Configuration \> Order Types +- Check if you have at least one `return` order type +- Set the `Picking Type` linked to the `return` order type diff --git a/fieldservice_equipment_stock_return/readme/CONTRIBUTORS.md b/fieldservice_equipment_stock_return/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..0b564563c9 --- /dev/null +++ b/fieldservice_equipment_stock_return/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- Italo LOPES \<\> diff --git a/fieldservice_equipment_stock_return/readme/CREDITS.md b/fieldservice_equipment_stock_return/readme/CREDITS.md new file mode 100644 index 0000000000..f2acf51d88 --- /dev/null +++ b/fieldservice_equipment_stock_return/readme/CREDITS.md @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +- Camptocamp SA \<\> diff --git a/fieldservice_equipment_stock_return/readme/DESCRIPTION.md b/fieldservice_equipment_stock_return/readme/DESCRIPTION.md new file mode 100644 index 0000000000..8b406f7213 --- /dev/null +++ b/fieldservice_equipment_stock_return/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module allows you to return FSM equipments creating a new picking. diff --git a/fieldservice_equipment_stock_return/static/description/icon.png b/fieldservice_equipment_stock_return/static/description/icon.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/fieldservice_equipment_stock_return/static/description/index.html b/fieldservice_equipment_stock_return/static/description/index.html new file mode 100644 index 0000000000..618f4a9d3d --- /dev/null +++ b/fieldservice_equipment_stock_return/static/description/index.html @@ -0,0 +1,442 @@ + + + + + +Field Service - Stock Equipment Return + + + +
+

Field Service - Stock Equipment Return

+ + +

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

+

This module allows you to return FSM equipments creating a new picking.

+

Table of contents

+ +
+

Configuration

+

Check Field Service - Stock Equipment module configuration.

+

To configure this module, you need to:

+
    +
  • Go to Field Service > Configuration > Order Types
  • +
  • Check if you have at least one return order type
  • +
  • Set the Picking Type linked to the return order type
  • +
+
+
+

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

+
    +
  • Camptocamp
  • +
  • Italo LOPES
  • +
+
+ +
+

Other credits

+

The development of this module has been financially supported by:

+ +
+
+

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 maintainers:

+

brian10048 wolfhall max3903 smangukiya imlopes

+

This module is part of the OCA/field-service project on GitHub.

+

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

+
+
+
+ + diff --git a/fieldservice_equipment_stock_return/tests/__init__.py b/fieldservice_equipment_stock_return/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/fieldservice_equipment_stock_return/tests/test_fsm_equipment.py b/fieldservice_equipment_stock_return/tests/test_fsm_equipment.py new file mode 100644 index 0000000000..d88d1ea157 --- /dev/null +++ b/fieldservice_equipment_stock_return/tests/test_fsm_equipment.py @@ -0,0 +1,87 @@ +# Copyright (C) 2020 - TODAY, Marcel Savegnago (Escodoo) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.tests.common import Form, TransactionCase + + +class TestFSMEquipment(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.Equipment = cls.env["fsm.equipment"] + cls.stock_location = cls.env.ref("stock.stock_location_customers") + cls.current_location = cls.env.ref("fieldservice.location_1") + cls.test_location = cls.env.ref("fieldservice.test_location") + + currency = cls.env["res.currency"].create( + { + "name": "Currency 1", + "symbol": "$", + } + ) + partner = cls.env["res.partner"].create( + { + "name": "Partner 1", + } + ) + cls.company1 = cls.env["res.company"].create( + { + "name": "Company 1", + "currency_id": currency.id, + "partner_id": partner.id, + } + ) + cls.product1 = cls.env["product.product"].create( + { + "name": "Product A", + "type": "product", + "tracking": "serial", + } + ) + cls.lot1 = cls.env["stock.lot"].create( + { + "name": "serial1", + "product_id": cls.product1.id, + "company_id": cls.company1.id, + } + ) + cls.env["stock.quant"].create( + { + "product_id": cls.product1.id, + "location_id": cls.stock_location.id, + "quantity": 1.0, + "lot_id": cls.lot1.id, + } + ) + + cls.equipment = cls.Equipment.create( + { + "name": "Equipment 1", + "product_id": cls.product1.id, + "lot_id": cls.lot1.id, + "current_stock_location_id": cls.stock_location.id, + } + ) + + def test_onchange_product(self): + equipment = self.equipment + equipment._onchange_product() + self.assertFalse(equipment.current_stock_location_id) + + def test_compute_current_stock_loc_id(self): + equipment = self.equipment + equipment._compute_current_stock_loc_id() + self.assertTrue(equipment.current_stock_location_id == self.stock_location) + + def test_fsm_equipment(self): + # Create an equipment + view_id = "fieldservice.fsm_equipment_form_view" + with Form(self.Equipment, view=view_id) as f: + f.name = "Test Equipment 1" + f.current_location_id = self.current_location + f.location_id = self.test_location + f.lot_id = self.lot1 + f.product_id = self.product1 + equipment = f.save() + + self.assertEqual(f.id, equipment.lot_id.fsm_equipment_id.id) diff --git a/fieldservice_equipment_stock_return/tests/test_stock_move.py b/fieldservice_equipment_stock_return/tests/test_stock_move.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/fieldservice_equipment_stock_return/views/fsm_equipment.xml b/fieldservice_equipment_stock_return/views/fsm_equipment.xml new file mode 100644 index 0000000000..634bac6cbe --- /dev/null +++ b/fieldservice_equipment_stock_return/views/fsm_equipment.xml @@ -0,0 +1,28 @@ + + + + + fsm.equipment.form.stock + fsm.equipment + + + + + + + diff --git a/fieldservice_equipment_stock_return/views/fsm_order_type_view.xml b/fieldservice_equipment_stock_return/views/fsm_order_type_view.xml new file mode 100644 index 0000000000..2abf2397a3 --- /dev/null +++ b/fieldservice_equipment_stock_return/views/fsm_order_type_view.xml @@ -0,0 +1,23 @@ + + + + + + fsm.order.type + + + + + + + + + diff --git a/fieldservice_equipment_stock_return/views/fsm_order_view.xml b/fieldservice_equipment_stock_return/views/fsm_order_view.xml new file mode 100644 index 0000000000..ef035bd193 --- /dev/null +++ b/fieldservice_equipment_stock_return/views/fsm_order_view.xml @@ -0,0 +1,25 @@ + + + + + + fsm.order + + + + + + internal_type in ['return'] + + + + +