diff --git a/helpdesk_mgmt/README.rst b/helpdesk_mgmt/README.rst index f76df435e6..404c941230 100644 --- a/helpdesk_mgmt/README.rst +++ b/helpdesk_mgmt/README.rst @@ -115,6 +115,16 @@ Tags |image5| +Auto-reply Ignore List +---------------------- + +To prevent automatic replies from being sent to certain senders (e.g. +monitoring systems, no-reply addresses, or mailing lists): + +1. Go to *Helpdesk > Settings*. +2. Select one or more contacts in the **Auto-reply ignored partners** field. + Only contacts that have an email address are shown. + Permissions ----------- diff --git a/helpdesk_mgmt/models/helpdesk_ticket.py b/helpdesk_mgmt/models/helpdesk_ticket.py index 60974f22e3..9b9976d10a 100644 --- a/helpdesk_mgmt/models/helpdesk_ticket.py +++ b/helpdesk_mgmt/models/helpdesk_ticket.py @@ -1,5 +1,6 @@ from odoo import api, fields, models, tools from odoo.exceptions import AccessError +from odoo.tools import email_normalize class HelpdeskTicket(models.Model): @@ -307,6 +308,16 @@ def _track_template(self, tracking): res = super()._track_template(tracking) ticket = self[0] if "stage_id" in tracking and ticket.stage_id.mail_template_id: + # Skip auto-reply if the sender's email matches an ignored partner + ignored_emails = { + email_normalize(p.email) + for p in ticket.company_id.helpdesk_mgmt_autoreply_ignored_partners + if p.email + } + ignored_emails.discard(False) + ticket_email = email_normalize(ticket.partner_email or "") + if ticket_email and ticket_email in ignored_emails: + return res res["stage_id"] = ( ticket.stage_id.mail_template_id, { diff --git a/helpdesk_mgmt/models/res_company.py b/helpdesk_mgmt/models/res_company.py index 8e8a75ebea..5ad0a036e1 100644 --- a/helpdesk_mgmt/models/res_company.py +++ b/helpdesk_mgmt/models/res_company.py @@ -33,3 +33,13 @@ class Company(models.Model): string="Auto assign tickets", default=True, ) + helpdesk_mgmt_autoreply_ignored_partners = fields.Many2many( + comodel_name="res.partner", + relation="helpdesk_mgmt_company_autoreply_ignored_partner_rel", + column1="company_id", + column2="partner_id", + string="Auto-reply ignored partners", + domain=[("email", "!=", False)], + help="Partners whose email address will not receive an automatic reply " + "when a helpdesk ticket is created from their email.", + ) diff --git a/helpdesk_mgmt/models/res_config_settings.py b/helpdesk_mgmt/models/res_config_settings.py index 17bc10e0ab..7596eb7eef 100644 --- a/helpdesk_mgmt/models/res_config_settings.py +++ b/helpdesk_mgmt/models/res_config_settings.py @@ -32,3 +32,7 @@ class ResConfigSettings(models.TransientModel): related="company_id.helpdesk_mgmt_ticket_auto_assign", readonly=False, ) + helpdesk_mgmt_autoreply_ignored_partners = fields.Many2many( + related="company_id.helpdesk_mgmt_autoreply_ignored_partners", + readonly=False, + ) diff --git a/helpdesk_mgmt/readme/CONFIGURE.md b/helpdesk_mgmt/readme/CONFIGURE.md index 5a1b50415c..6bcb1542bf 100644 --- a/helpdesk_mgmt/readme/CONFIGURE.md +++ b/helpdesk_mgmt/readme/CONFIGURE.md @@ -67,6 +67,15 @@ in the list view. ![](../static/description/Tags.PNG) +## Auto-reply Ignore List + +To prevent automatic replies from being sent to certain senders (e.g. +monitoring systems, no-reply addresses, or mailing lists): + +1. Go to *Helpdesk \> Settings*. +2. Select one or more contacts in the **Auto-reply ignored partners** field. + Only contacts that have an email address are shown. + ## Permissions There are restrictions to read tickets according to the user's diff --git a/helpdesk_mgmt/tests/test_helpdesk_fetchmail.py b/helpdesk_mgmt/tests/test_helpdesk_fetchmail.py index 2f6789d018..f19e26944e 100644 --- a/helpdesk_mgmt/tests/test_helpdesk_fetchmail.py +++ b/helpdesk_mgmt/tests/test_helpdesk_fetchmail.py @@ -81,3 +81,118 @@ def test_message_process_missing_channel(self): self.assertEqual(ticket_id.name, "Need backup") # ensure that the channel is not set self.assertFalse(ticket_id.channel_id) + + def _create_autoreply_template(self): + """Return a minimal mail.template for helpdesk.ticket.""" + return self.env["mail.template"].create( + { + "name": "Test Auto Reply", + "model_id": self.env.ref("helpdesk_mgmt.model_helpdesk_ticket").id, + "subject": "We received your ticket", + "body_html": "

Thank you for contacting us!

", + } + ) + + def _create_ignored_partner(self, email, name=None): + """Create a res.partner with the given email and add it to the company's + autoreply ignore list.""" + partner = self.env["res.partner"].create( + {"name": name or email, "email": email} + ) + self.company.helpdesk_mgmt_autoreply_ignored_partners = [(4, partner.id)] + return partner + + def test_autoreply_sent_for_normal_email(self): + """_track_template includes the stage template for a non-ignored sender.""" + mail_template = self._create_autoreply_template() + self.new_stage.mail_template_id = mail_template + # configure an ignored partner whose email differs from the ticket sender + self._create_ignored_partner("noreply@monitoring.example.com") + ticket = self.env["helpdesk.ticket"].create( + { + "name": "Normal customer ticket", + "description": "I need help", + "team_id": self.team_a.id, + "stage_id": self.new_stage.id, + "partner_email": "customer@example.com", + } + ) + result = ticket._track_template({"stage_id": self.new_stage}) + self.assertIn( + "stage_id", + result, + "Auto-reply template should be present for a non-ignored sender.", + ) + + def test_autoreply_not_sent_for_ignored_partner(self): + """Stage template is skipped when the ticket sender is an ignored partner.""" + mail_template = self._create_autoreply_template() + self.new_stage.mail_template_id = mail_template + ignored_email = "noreply@monitoring.example.com" + self._create_ignored_partner(ignored_email) + ticket = self.env["helpdesk.ticket"].create( + { + "name": "Alert from monitoring system", + "description": "Automated alert", + "team_id": self.team_a.id, + "stage_id": self.new_stage.id, + "partner_email": ignored_email, + } + ) + result = ticket._track_template({"stage_id": self.new_stage}) + self.assertNotIn( + "stage_id", + result, + "Auto-reply template should be absent for an ignored sender.", + ) + + def test_autoreply_ignored_partner_email_is_normalized(self): + """Ignore list matching is case-insensitive (via email_normalize).""" + mail_template = self._create_autoreply_template() + self.new_stage.mail_template_id = mail_template + # store the partner's address in upper case + self._create_ignored_partner("NOREPLY@MONITORING.EXAMPLE.COM") + ticket = self.env["helpdesk.ticket"].create( + { + "name": "Alert from monitoring system", + "description": "Automated alert", + "team_id": self.team_a.id, + "stage_id": self.new_stage.id, + # ticket arrives with the address in a display-name format + "partner_email": "Monitoring Bot ", + } + ) + result = ticket._track_template({"stage_id": self.new_stage}) + self.assertNotIn( + "stage_id", + result, + "Ignore list matching should normalize both sides via email_normalize.", + ) + + def test_autoreply_multiple_ignored_partners(self): + """Multiple ignored partners all suppress the auto-reply.""" + mail_template = self._create_autoreply_template() + self.new_stage.mail_template_id = mail_template + emails = [ + "alerts@example.com", + "noreply@example.com", + "monitoring@example.com", + ] + for email in emails: + self._create_ignored_partner(email) + for email in emails: + ticket = self.env["helpdesk.ticket"].create( + { + "name": f"Alert from {email}", + "description": "Automated alert", + "team_id": self.team_a.id, + "stage_id": self.new_stage.id, + "partner_email": email, + } + ) + result = ticket._track_template({"stage_id": self.new_stage}) + self.assertNotIn( + "stage_id", + result, + f"Auto-reply should be suppressed for ignored address {email}.", + ) diff --git a/helpdesk_mgmt/views/res_config_settings_views.xml b/helpdesk_mgmt/views/res_config_settings_views.xml index 6f7df8754e..d9024fb1e2 100644 --- a/helpdesk_mgmt/views/res_config_settings_views.xml +++ b/helpdesk_mgmt/views/res_config_settings_views.xml @@ -115,6 +115,18 @@ + + +