From 7a15fb5684fb2882a22a0ec80618b364de6235f3 Mon Sep 17 00:00:00 2001 From: tarteo Date: Wed, 1 Jul 2026 10:03:34 +0200 Subject: [PATCH] [IMP] mail_tracking: Add email score search functionality fw port from 18.0: 06818b7b33b889cb945a2dad5d06f6612390cf09 --- mail_tracking/README.rst | 4 ++ mail_tracking/models/res_partner.py | 32 +++++++++++- mail_tracking/readme/CONTRIBUTORS.md | 2 + mail_tracking/static/description/index.html | 4 ++ mail_tracking/tests/test_mail_tracking.py | 55 +++++++++++++++++++++ 5 files changed, 96 insertions(+), 1 deletion(-) diff --git a/mail_tracking/README.rst b/mail_tracking/README.rst index 3cf0b0466..902c1b930 100644 --- a/mail_tracking/README.rst +++ b/mail_tracking/README.rst @@ -179,6 +179,10 @@ Contributors - Quoc Pham Ngoc +- `Onestein `__: + + - Dennis Sluijk + Other credits ------------- diff --git a/mail_tracking/models/res_partner.py b/mail_tracking/models/res_partner.py index cb0bcd1fe..8d146f81c 100644 --- a/mail_tracking/models/res_partner.py +++ b/mail_tracking/models/res_partner.py @@ -23,7 +23,37 @@ class ResPartner(models.Model): tracking_emails_count = fields.Integer( compute="_compute_email_score_and_count", readonly=True ) - email_score = fields.Float(compute="_compute_email_score_and_count", readonly=True) + email_score = fields.Float( + compute="_compute_email_score_and_count", + search="_search_email_score", + ) + + def _search_email_score(self, operator, value): + supported_operators = ["<", ">", "<=", ">=", "=", "!="] + if operator not in supported_operators: + raise NotImplementedError() + weights = self.env["mail.tracking.email"]._email_score_weights() + case_expr = " ".join( + f"WHEN '{state}' THEN {score}" for state, score in weights.items() + ) + state_expr = f"CASE state {case_expr} ELSE 0.0 END" + query = f""" + SELECT partner.id + FROM res_partner partner + LEFT JOIN ( + SELECT recipient_address, + GREATEST(0.0, LEAST(100.0, + 50.0 + SUM({state_expr}) + )) AS score + FROM mail_tracking_email + WHERE recipient_address IS NOT NULL + GROUP BY recipient_address + ) mte ON mte.recipient_address = LOWER(partner.email) + WHERE COALESCE(mte.score, 50.0) {operator} %s + """ + self.env.cr.execute(query, (value,)) + partner_ids = [row[0] for row in self.env.cr.fetchall()] + return [("id", "in", partner_ids)] @api.depends("email") def _compute_email_score_and_count(self): diff --git a/mail_tracking/readme/CONTRIBUTORS.md b/mail_tracking/readme/CONTRIBUTORS.md index 7e51bb1f1..dba4e30aa 100644 --- a/mail_tracking/readme/CONTRIBUTORS.md +++ b/mail_tracking/readme/CONTRIBUTORS.md @@ -13,3 +13,5 @@ - Tris Doan - [Komit](https://komit-consulting.com): - Quoc Pham Ngoc +- [Onestein](https://onestein.nl): + - Dennis Sluijk diff --git a/mail_tracking/static/description/index.html b/mail_tracking/static/description/index.html index 15c15833f..5e3842cb3 100644 --- a/mail_tracking/static/description/index.html +++ b/mail_tracking/static/description/index.html @@ -505,6 +505,10 @@

Contributors

  • Quoc Pham Ngoc
  • +
  • Onestein:
      +
    • Dennis Sluijk
    • +
    +
  • diff --git a/mail_tracking/tests/test_mail_tracking.py b/mail_tracking/tests/test_mail_tracking.py index cf8b92f37..74e8ab42f 100644 --- a/mail_tracking/tests/test_mail_tracking.py +++ b/mail_tracking/tests/test_mail_tracking.py @@ -53,6 +53,13 @@ def setUp(self, *args, **kwargs): self.recipient = self.env["res.partner"].create( {"name": "Test recipient", "email": "recipient@example.com"} ) + self.recipient_1 = self.env["res.partner"].create( + {"name": "Test recipient 1", "email": "recipient1@example.com"} + ) + self.recipient_2 = self.env["res.partner"].create( + {"name": "Test recipient 2", "email": "recipient2@example.com"} + ) + self.recipients = self.recipient + self.recipient_1 + self.recipient_2 self.last_request = None if self.patch_http_request: self.last_request = http.request @@ -811,6 +818,54 @@ def test_partner_email_change(self): self.assertEqual(orig_score, self.recipient.email_score) self.assertEqual(orig_count, self.recipient.tracking_emails_count) + def test_search_on_email_score(self): + recipients_domain = [("id", "in", self.recipients.ids)] + mail, tracking = self.mail_send(self.recipient.email) + tracking.event_create("open", {}) + mail, tracking = self.mail_send(self.recipient.email) + tracking.event_create("open", {}) + mail, tracking = self.mail_send(self.recipient_1.email) + tracking.event_create("reject", {}) + # Ensure the email_score is correctly computed for the 3 partners of the test + self.assertEqual(60.0, self.recipient.email_score) + self.assertEqual(25.0, self.recipient_1.email_score) + self.assertEqual(50.0, self.recipient_2.email_score) + # Search for email_score > 50 + result = self.env["res.partner"].search( + recipients_domain + [("email_score", ">", 50)] + ) + self.assertEqual(result, self.recipient) + # Search for email_score < 30 + result = self.env["res.partner"].search( + recipients_domain + [("email_score", "<", 30)] + ) + self.assertEqual(result, self.recipient_1) + # Search for email_score = 50 + result = self.env["res.partner"].search( + recipients_domain + [("email_score", "=", 50)] + ) + self.assertEqual(result, self.recipient_2) + # Search for email_score >= 50 + result = self.env["res.partner"].search( + recipients_domain + [("email_score", ">=", 50)] + ) + self.assertEqual(result, self.recipient + self.recipient_2) + # Search for email_score <= 50 + result = self.env["res.partner"].search( + recipients_domain + [("email_score", "<=", 50)] + ) + self.assertEqual(result, self.recipient_1 + self.recipient_2) + # Search for email_score != 50 + result = self.env["res.partner"].search( + recipients_domain + [("email_score", "!=", 50)] + ) + self.assertEqual(result, self.recipient + self.recipient_1) + # Search for email_score between 30 and 65 + result = self.env["res.partner"].search( + recipients_domain + [("email_score", ">", 30), ("email_score", "<", 65)] + ) + self.assertEqual(result, self.recipient + self.recipient_2) + def test_process_hard_bounce(self): mail, tracking = self.mail_send(self.recipient.email) tracking.event_create("hard_bounce", {})