diff --git a/repair_notification/README.rst b/repair_notification/README.rst new file mode 100644 index 00000000..a2bc04f2 --- /dev/null +++ b/repair_notification/README.rst @@ -0,0 +1,108 @@ +=================== +Repair Notification +=================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:3c3f7bfffa4d25e9a8fba4dadaef7e8ffd758a1c6b3d4afdb7edd0e1ecf6a49d + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/16.0/repair_notification + :alt: OCA/repair +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/repair-16-0/repair-16-0-repair_notification + :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=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends the standard Repair application to improve customer +communication during the repair process. + +It adds new configuration options in the Repair application settings +allowing users to: + +- Enable or disable automatic email notifications when a repair starts + +- Enable or disable automatic email notifications when a repair ends + +- Select a specific email template for each notification type (start and + end) + +When enabled, an email is automatically sent to the customer: + +- When the repair order moves to the in progress state + +- When the repair order is completed + +To avoid duplicate notifications, the module ensures that the “repair +start” email is sent only once per repair order. If a repair is started, +then cancelled, and later restarted, the customer will not receive the +start notification again. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +- Go to Repair > Configuration > Settings +- Activate Repair Start Confirmation Email > Select the Email Template > + save +- Activate Repair End Confirmation Email > Select the Email Template > + save + +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 +------- + +* ACSONE SA/NV + +Contributors +------------ + +- Nihel GABSI nihel.gabsi@acsone.eu +- Souheil BEJAOUI souheil.bejaoui@acsone.eu + +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_notification/__init__.py b/repair_notification/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/repair_notification/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/repair_notification/__manifest__.py b/repair_notification/__manifest__.py new file mode 100644 index 00000000..507762a9 --- /dev/null +++ b/repair_notification/__manifest__.py @@ -0,0 +1,16 @@ +# Copyright 2026 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Repair Notification", + "summary": """Send mail notifications to the customer to inform about repair start/end""", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "author": "ACSONE SA/NV,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/repair", + "depends": ["base_repair_config", "repair"], + "data": [ + "views/res_config_settings.xml", + "data/mail_data.xml", + ], +} diff --git a/repair_notification/data/mail_data.xml b/repair_notification/data/mail_data.xml new file mode 100644 index 00000000..5530e48e --- /dev/null +++ b/repair_notification/data/mail_data.xml @@ -0,0 +1,72 @@ + + + + Repair Start Notification + + {{object.user_id.email_formatted }} + {{object.partner_id.id}} + Repair order (Ref {{object.name or 'n/a' }}) started + {{object.partner_id.lang}} + + +
+

+ Dear + + + + +
+
+ We would like to inform you that the reparation of your product + + + + is just started. +
+
+ team. +
+
+

+
+
+
+ + + Repair End Notification + + {{object.user_id.email_formatted }} + {{object.partner_id.id}} + Repair order (Ref {{object.name or 'n/a' }}) ended + {{object.partner_id.lang}} + + +
+

+ Dear + + + + +
+
+ We would like to inform you that the reparation of your product + + + + is just ended. +
+
+ team. +
+
+

+
+
+
+
diff --git a/repair_notification/models/__init__.py b/repair_notification/models/__init__.py new file mode 100644 index 00000000..6d13176e --- /dev/null +++ b/repair_notification/models/__init__.py @@ -0,0 +1,3 @@ +from . import res_config_settings +from . import res_company +from . import repair_order diff --git a/repair_notification/models/repair_order.py b/repair_notification/models/repair_order.py new file mode 100644 index 00000000..20473831 --- /dev/null +++ b/repair_notification/models/repair_order.py @@ -0,0 +1,40 @@ +# Copyright 2026 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class RepairOrder(models.Model): + + _inherit = "repair.order" + + repair_start_mail_sent = fields.Boolean(default=False, copy=False) + + def write(self, vals): + res = super().write(vals) + if ( + "state" in vals + and vals["state"] == "under_repair" + and not self.repair_start_mail_sent + ): + self._send_repair_start_confirmation_email() + self.repair_start_mail_sent = True + elif "state" in vals and vals["state"] == "done": + self._send_repair_end_confirmation_email() + return res + + def _send_repair_start_confirmation_email(self): + """Send customer notification when the repair is started""" + for rec in self.filtered("company_id.send_repair_start_confirmation"): + repair_template_id = rec.company_id.repair_start_template_id.id + rec.with_context( + force_send=True, + ).message_post_with_template(repair_template_id) + + def _send_repair_end_confirmation_email(self): + """Send customer notification when the repair is ended""" + for rec in self.filtered("company_id.send_repair_end_confirmation"): + repair_template_id = rec.company_id.repair_end_template_id.id + rec.with_context( + force_send=True, + ).message_post_with_template(repair_template_id) diff --git a/repair_notification/models/res_company.py b/repair_notification/models/res_company.py new file mode 100644 index 00000000..22884d83 --- /dev/null +++ b/repair_notification/models/res_company.py @@ -0,0 +1,46 @@ +# Copyright 2026 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResCompany(models.Model): + + _inherit = "res.company" + + def _default_repair_start_template(self): + try: + return self.env.ref( + "repair_notification.mail_template_repair_start_notification" + ).id + except ValueError: + return False + + def _default_repair_end_template(self): + try: + return self.env.ref( + "repair_notification.mail_template_repair_end_notification" + ).id + except ValueError: + return False + + send_repair_start_confirmation = fields.Boolean( + help="Notify the customer when repairing starts" + ) + send_repair_end_confirmation = fields.Boolean( + help="Notify the customer when repairing ends" + ) + repair_start_template_id = fields.Many2one( + comodel_name="mail.template", + string="Email Template confirmation for repair start", + domain="[('model', '=', 'repair.order')]", + default=_default_repair_start_template, + help="Email sent to the customer once the repair is started.", + ) + repair_end_template_id = fields.Many2one( + comodel_name="mail.template", + string="Email Template confirmation for repair end", + domain="[('model', '=', 'repair.order')]", + default=_default_repair_end_template, + help="Email sent to the customer once the repair is ended.", + ) diff --git a/repair_notification/models/res_config_settings.py b/repair_notification/models/res_config_settings.py new file mode 100644 index 00000000..683c566d --- /dev/null +++ b/repair_notification/models/res_config_settings.py @@ -0,0 +1,26 @@ +# Copyright 2026 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + + _inherit = "res.config.settings" + + send_repair_start_confirmation = fields.Boolean( + related="company_id.send_repair_start_confirmation", + readonly=False, + ) + repair_start_template_id = fields.Many2one( + related="company_id.repair_start_template_id", + readonly=False, + ) + send_repair_end_confirmation = fields.Boolean( + related="company_id.send_repair_end_confirmation", + readonly=False, + ) + repair_end_template_id = fields.Many2one( + related="company_id.repair_end_template_id", + readonly=False, + ) diff --git a/repair_notification/readme/CONFIGURE.md b/repair_notification/readme/CONFIGURE.md new file mode 100644 index 00000000..34bcc0ba --- /dev/null +++ b/repair_notification/readme/CONFIGURE.md @@ -0,0 +1,3 @@ +- Go to Repair > Configuration > Settings +- Activate Repair Start Confirmation Email > Select the Email Template > save +- Activate Repair End Confirmation Email > Select the Email Template > save diff --git a/repair_notification/readme/CONTRIBUTORS.md b/repair_notification/readme/CONTRIBUTORS.md new file mode 100644 index 00000000..eaae7470 --- /dev/null +++ b/repair_notification/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- Nihel GABSI +- Souheil BEJAOUI diff --git a/repair_notification/readme/DESCRIPTION.md b/repair_notification/readme/DESCRIPTION.md new file mode 100644 index 00000000..9f003157 --- /dev/null +++ b/repair_notification/readme/DESCRIPTION.md @@ -0,0 +1,18 @@ +This module extends the standard Repair application to improve customer communication during the repair process. + +It adds new configuration options in the Repair application settings allowing users to: + +- Enable or disable automatic email notifications when a repair starts + +- Enable or disable automatic email notifications when a repair ends + +- Select a specific email template for each notification type (start and end) + +When enabled, an email is automatically sent to the customer: + +- When the repair order moves to the in progress state + +- When the repair order is completed + +To avoid duplicate notifications, the module ensures that the “repair start” email is sent only once per repair order. +If a repair is started, then cancelled, and later restarted, the customer will not receive the start notification again. diff --git a/repair_notification/static/description/icon.png b/repair_notification/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/repair_notification/static/description/icon.png differ diff --git a/repair_notification/static/description/index.html b/repair_notification/static/description/index.html new file mode 100644 index 00000000..a08fbeaf --- /dev/null +++ b/repair_notification/static/description/index.html @@ -0,0 +1,453 @@ + + + + + +Repair Notification + + + +
+

Repair Notification

+ + +

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

+

This module extends the standard Repair application to improve customer +communication during the repair process.

+

It adds new configuration options in the Repair application settings +allowing users to:

+
    +
  • Enable or disable automatic email notifications when a repair starts
  • +
  • Enable or disable automatic email notifications when a repair ends
  • +
  • Select a specific email template for each notification type (start and +end)
  • +
+

When enabled, an email is automatically sent to the customer:

+
    +
  • When the repair order moves to the in progress state
  • +
  • When the repair order is completed
  • +
+

To avoid duplicate notifications, the module ensures that the “repair +start” email is sent only once per repair order. If a repair is started, +then cancelled, and later restarted, the customer will not receive the +start notification again.

+

Table of contents

+ +
+

Configuration

+
    +
  • Go to Repair > Configuration > Settings
  • +
  • Activate Repair Start Confirmation Email > Select the Email Template > +save
  • +
  • Activate Repair End Confirmation Email > Select the Email Template > +save
  • +
+
+
+

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

+
    +
  • ACSONE SA/NV
  • +
+
+
+

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_notification/tests/__init__.py b/repair_notification/tests/__init__.py new file mode 100644 index 00000000..26a5a76e --- /dev/null +++ b/repair_notification/tests/__init__.py @@ -0,0 +1 @@ +from . import test_notification diff --git a/repair_notification/tests/test_notification.py b/repair_notification/tests/test_notification.py new file mode 100644 index 00000000..7fe31583 --- /dev/null +++ b/repair_notification/tests/test_notification.py @@ -0,0 +1,87 @@ +# Copyright 2026 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestNotification(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Activate notifications + cls.env.company.send_repair_start_confirmation = True + cls.env.company.send_repair_end_confirmation = True + + # Partner + cls.res_partner = cls.env["res.partner"].create({"name": "Wood Corner"}) + cls.res_partner_address = cls.env["res.partner"].create( + {"name": "Willie Burke", "parent_id": cls.res_partner.id} + ) + + # Location + cls.stock_warehouse = cls.env["stock.warehouse"].search( + [("company_id", "=", cls.env.company.id)], limit=1 + ) + cls.stock_location = cls.env["stock.location"].create( + { + "name": "Shelf 2", + "location_id": cls.stock_warehouse.lot_stock_id.id, + } + ) + + # Product + cls.product_product = cls.env["product.product"].create( + {"name": "Desk Combination", "type": "product"} + ) + cls.env["stock.quant"]._update_available_quantity( + cls.product_product, cls.stock_location, 5 + ) + + # Repair Order + cls.repair = cls.env["repair.order"].create( + { + "address_id": cls.res_partner_address.id, + "guarantee_limit": "2027-01-01", + "invoice_method": "none", + "user_id": False, + "product_id": cls.product_product.id, + "product_qty": 1.0, + "product_uom": cls.env.ref("uom.product_uom_unit").id, + "partner_invoice_id": cls.res_partner_address.id, + "partner_id": cls.res_partner.id, + "location_id": cls.stock_location.id, + } + ) + + # Validate the repair order + cls.repair.action_validate() + + def test_start_repair_notification(self): + # Test case 1: Starting repair after order validation + messages = self.repair.message_ids + self.assertTrue(not self.repair.repair_start_mail_sent) + self.repair.action_repair_start() + new_messages = self.repair.message_ids - messages + self.assertTrue( + new_messages, + "No message was posted on the repair order after starting repair", + ) + self.assertTrue(self.repair.repair_start_mail_sent) + + # Test case 2: Starting repair after order cancellation -> Should not resend the email + self.repair.action_repair_cancel() + self.repair.action_repair_cancel_draft() + self.repair.action_validate() + self.repair.action_repair_start() + self.assertTrue(self.repair.repair_start_mail_sent) + + def test_end_repair_notification(self): + self.repair.action_repair_start() + messages = self.repair.message_ids + self.repair.action_repair_end() + new_messages = self.repair.message_ids - messages + self.assertTrue(new_messages) + self.assertTrue( + new_messages, + "No message was posted on the repair order after ending repair", + ) diff --git a/repair_notification/views/res_config_settings.xml b/repair_notification/views/res_config_settings.xml new file mode 100644 index 00000000..923166b5 --- /dev/null +++ b/repair_notification/views/res_config_settings.xml @@ -0,0 +1,97 @@ + + + + + + res.config.settings + + + +
+
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+ + + +
diff --git a/setup/repair_notification/odoo/addons/repair_notification b/setup/repair_notification/odoo/addons/repair_notification new file mode 120000 index 00000000..3e7a248b --- /dev/null +++ b/setup/repair_notification/odoo/addons/repair_notification @@ -0,0 +1 @@ +../../../../repair_notification \ No newline at end of file diff --git a/setup/repair_notification/setup.py b/setup/repair_notification/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/repair_notification/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)