From 16d909879299cd25aa4d945efbe6405d0cee4d2e Mon Sep 17 00:00:00 2001 From: Ronald Portier Date: Tue, 6 May 2025 11:34:34 +0200 Subject: [PATCH 1/3] [ADD] mail_visible_email: new module This module will show actual to, cc and bcc headers used on incoming and outgoing mails. --- mail_visible_email/README.rst | 123 +++++ mail_visible_email/__init__.py | 3 + mail_visible_email/__manifest__.py | 23 + mail_visible_email/models/__init__.py | 5 + mail_visible_email/models/mail_mail.py | 54 ++ mail_visible_email/models/mail_message.py | 24 + mail_visible_email/models/mail_thread.py | 21 + mail_visible_email/readme/CONFIGURE.rst | 1 + mail_visible_email/readme/CONTRIBUTORS.rst | 3 + mail_visible_email/readme/DESCRIPTION.rst | 28 ++ .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 462 ++++++++++++++++++ mail_visible_email/tests/__init__.py | 4 + mail_visible_email/tests/test_mail_receive.py | 61 +++ mail_visible_email/tests/test_mail_send.py | 69 +++ .../views/mail_message_views.xml | 15 + 16 files changed, 896 insertions(+) create mode 100644 mail_visible_email/README.rst create mode 100644 mail_visible_email/__init__.py create mode 100644 mail_visible_email/__manifest__.py create mode 100644 mail_visible_email/models/__init__.py create mode 100644 mail_visible_email/models/mail_mail.py create mode 100644 mail_visible_email/models/mail_message.py create mode 100644 mail_visible_email/models/mail_thread.py create mode 100644 mail_visible_email/readme/CONFIGURE.rst create mode 100644 mail_visible_email/readme/CONTRIBUTORS.rst create mode 100644 mail_visible_email/readme/DESCRIPTION.rst create mode 100644 mail_visible_email/static/description/icon.png create mode 100644 mail_visible_email/static/description/index.html create mode 100644 mail_visible_email/tests/__init__.py create mode 100644 mail_visible_email/tests/test_mail_receive.py create mode 100644 mail_visible_email/tests/test_mail_send.py create mode 100644 mail_visible_email/views/mail_message_views.xml diff --git a/mail_visible_email/README.rst b/mail_visible_email/README.rst new file mode 100644 index 000000000..2921be17e --- /dev/null +++ b/mail_visible_email/README.rst @@ -0,0 +1,123 @@ +====================================== +Make emails for to, cc and bcc visible +====================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:53373ba10ebe61d982a5d796725296eee83924dc9d4d6aa7e54e0ec9839f59a5 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/16.0/mail_visible_email + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-16-0/social-16-0-mail_visible_email + :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/social&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +In Odoo mails it is often unclear who where the other recipients of mails +received, or what the actual mail addresses where of mails sent. + +This module adds the following fields to mail_message: +- email_to +- email_cc +- email_bcc + +For both incoming and outgoing mails, the actual to and cc headers +from the mails will be stored here. For outgoing mails also the bcc +header. + +In case we receive a mail because we received it on an address that was +in the bcc of the email sent, the address will actually be shown on the +email_to field. This is because there is no bcc header in an incoming mail, +we will have the address in the Delivered-To header. + +Note that we will only store the unadorned email (without partner name), +as this will be the relevant part, and the partner names are visible on +other fields. + +For technical reasons this module depends on mail_composer_cc_bcc: + +* We need the email_bcc field on mail.mail; +* The module mail_composer_bcc fundamentally changes the workings of the _send() + method on mail.mail. In order not to have to support both methods depending + on whether mail_composer_bcc is installed or not, it is easier to just make sure + it is installed. + +.. 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: + +Configuration +============= + +This module has no separate configuration options. + +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 +~~~~~~~ + +* Therp BV + +Contributors +~~~~~~~~~~~~ + +* `Therp BV `_: + + * Ronald Portier + +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-NL66278| image:: https://github.com/NL66278.png?size=40px + :target: https://github.com/NL66278 + :alt: NL66278 + +Current `maintainer `__: + +|maintainer-NL66278| + +This module is part of the `OCA/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_visible_email/__init__.py b/mail_visible_email/__init__.py new file mode 100644 index 000000000..31660d6a9 --- /dev/null +++ b/mail_visible_email/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/mail_visible_email/__manifest__.py b/mail_visible_email/__manifest__.py new file mode 100644 index 000000000..808a6d3bd --- /dev/null +++ b/mail_visible_email/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2025 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Make emails for to, cc and bcc visible", + "summary": "Save and show the actual email addresses used in mail.message.", + "version": "16.0.1.0.0", + "development_status": "Alpha", + "category": "Social", + "website": "https://github.com/OCA/social", + "author": "Therp BV, Odoo Community Association (OCA)", + "maintainers": ["NL66278"], + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "mail", + "test_mail", + "mail_composer_cc_bcc", + ], + "data": [ + "views/mail_message_views.xml", + ], +} diff --git a/mail_visible_email/models/__init__.py b/mail_visible_email/models/__init__.py new file mode 100644 index 000000000..c54733443 --- /dev/null +++ b/mail_visible_email/models/__init__.py @@ -0,0 +1,5 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import mail_thread +from . import mail_message +from . import mail_mail diff --git a/mail_visible_email/models/mail_mail.py b/mail_visible_email/models/mail_mail.py new file mode 100644 index 000000000..73d713f4a --- /dev/null +++ b/mail_visible_email/models/mail_mail.py @@ -0,0 +1,54 @@ +# Copyright 2025 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import models, tools + + +def list_emails(partners): + return [ + p.email and tools.mail._normalize_email(p.email) for p in partners if p.email + ] + + +class MailMail(models.Model): + _inherit = "mail.mail" + + def _send(self, auto_commit=False, raise_exception=False, smtp_session=None): + """Store email_to, email_cc, email_bcc also in mail.message.""" + for this in self.filtered(lambda r: r.state == "outgoing"): + message_vals = {} + email_values = this._get_email_values() + for fieldname in ["email_to", "email_cc", "email_bcc"]: + emails = email_values.get(fieldname, []) + if not emails and not this[fieldname]: + continue + message_vals[fieldname] = this._append_email(fieldname, emails) + if message_vals: + this.mail_message_id.write(message_vals) + return super()._send( + auto_commit=auto_commit, + raise_exception=raise_exception, + smtp_session=smtp_session, + ) + + def _get_email_values(self): + self.ensure_one() + partners_to = ( + self.recipient_ids - self.recipient_cc_ids - self.recipient_bcc_ids + ) + return { + "email_to": list_emails(partners_to), + "email_cc": list_emails(self.recipient_cc_ids), + "email_bcc": list_emails(self.recipient_bcc_ids), + } + + def _append_email(self, fieldname, emails): + """Do not override existing emails.""" + self.ensure_one() + message = self.mail_message_id + preset_emails = self[fieldname] + if preset_emails: + emails += tools.email_normalize_all(preset_emails) + existing = message[fieldname] + if existing: + emails += existing.split(",") + return ",".join(list(set(emails))) diff --git a/mail_visible_email/models/mail_message.py b/mail_visible_email/models/mail_message.py new file mode 100644 index 000000000..5fc183716 --- /dev/null +++ b/mail_visible_email/models/mail_message.py @@ -0,0 +1,24 @@ +# Copyright 2025 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class MailMessage(models.Model): + _inherit = "mail.message" + + email_to = fields.Char( + string="To", + readonly=True, + help="original email addresses in 'to' header", + ) + email_cc = fields.Char( + string="Cc", + readonly=True, + help="original email addresses in 'cc' header", + ) + email_bcc = fields.Char( + string="Bcc", + readonly=True, + help="original email addresses in 'bcc' header", + ) diff --git a/mail_visible_email/models/mail_thread.py b/mail_visible_email/models/mail_thread.py new file mode 100644 index 000000000..aa1238124 --- /dev/null +++ b/mail_visible_email/models/mail_thread.py @@ -0,0 +1,21 @@ +# Copyright 2025 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, models + + +class MailThread(models.AbstractModel): + _inherit = "mail.thread" + + @api.model + def _message_route_process(self, message, message_dict, routes): + """Intercept message_dict to write 'to' and 'cc' to mail.message. + + Smtp does not deliver messages with a bcc header. If a message is + received from a bcc address, this address will be in the raw + Delivered-To header, which message_parse adds to the 'to' key + of message_dict. + """ + message_dict["email_to"] = message_dict.get("to", False) + message_dict["email_cc"] = message_dict.get("cc", False) + return super()._message_route_process(message, message_dict, routes) diff --git a/mail_visible_email/readme/CONFIGURE.rst b/mail_visible_email/readme/CONFIGURE.rst new file mode 100644 index 000000000..7d2c7b671 --- /dev/null +++ b/mail_visible_email/readme/CONFIGURE.rst @@ -0,0 +1 @@ +This module has no separate configuration options. diff --git a/mail_visible_email/readme/CONTRIBUTORS.rst b/mail_visible_email/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..1659ed220 --- /dev/null +++ b/mail_visible_email/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Therp BV `_: + + * Ronald Portier diff --git a/mail_visible_email/readme/DESCRIPTION.rst b/mail_visible_email/readme/DESCRIPTION.rst new file mode 100644 index 000000000..e2ec870b3 --- /dev/null +++ b/mail_visible_email/readme/DESCRIPTION.rst @@ -0,0 +1,28 @@ +In Odoo mails it is often unclear who where the other recipients of mails +received, or what the actual mail addresses where of mails sent. + +This module adds the following fields to mail_message: +- email_to +- email_cc +- email_bcc + +For both incoming and outgoing mails, the actual to and cc headers +from the mails will be stored here. For outgoing mails also the bcc +header. + +In case we receive a mail because we received it on an address that was +in the bcc of the email sent, the address will actually be shown on the +email_to field. This is because there is no bcc header in an incoming mail, +we will have the address in the Delivered-To header. + +Note that we will only store the unadorned email (without partner name), +as this will be the relevant part, and the partner names are visible on +other fields. + +For technical reasons this module depends on mail_composer_cc_bcc: + +* We need the email_bcc field on mail.mail; +* The module mail_composer_bcc fundamentally changes the workings of the _send() + method on mail.mail. In order not to have to support both methods depending + on whether mail_composer_bcc is installed or not, it is easier to just make sure + it is installed. diff --git a/mail_visible_email/static/description/icon.png b/mail_visible_email/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/mail_visible_email/static/description/index.html b/mail_visible_email/static/description/index.html new file mode 100644 index 000000000..d523a5cdb --- /dev/null +++ b/mail_visible_email/static/description/index.html @@ -0,0 +1,462 @@ + + + + + +Make emails for to, cc and bcc visible + + + +
+

Make emails for to, cc and bcc visible

+ + +

Alpha License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

+

In Odoo mails it is often unclear who where the other recipients of mails +received, or what the actual mail addresses where of mails sent.

+

This module adds the following fields to mail_message: +- email_to +- email_cc +- email_bcc

+

For both incoming and outgoing mails, the actual to and cc headers +from the mails will be stored here. For outgoing mails also the bcc +header.

+

In case we receive a mail because we received it on an address that was +in the bcc of the email sent, the address will actually be shown on the +email_to field. This is because there is no bcc header in an incoming mail, +we will have the address in the Delivered-To header.

+

Note that we will only store the unadorned email (without partner name), +as this will be the relevant part, and the partner names are visible on +other fields.

+

For technical reasons this module depends on mail_composer_cc_bcc:

+
    +
  • We need the email_bcc field on mail.mail;
  • +
  • The module mail_composer_bcc fundamentally changes the workings of the _send() +method on mail.mail. In order not to have to support both methods depending +on whether mail_composer_bcc is installed or not, it is easier to just make sure +it is installed.
  • +
+
+

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

+ +
+

Configuration

+

This module has no separate configuration options.

+
+
+

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

+
    +
  • Therp BV
  • +
+
+
+

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:

+

NL66278

+

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

+

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

+
+
+
+ + diff --git a/mail_visible_email/tests/__init__.py b/mail_visible_email/tests/__init__.py new file mode 100644 index 000000000..76b133608 --- /dev/null +++ b/mail_visible_email/tests/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_mail_receive +from . import test_mail_send diff --git a/mail_visible_email/tests/test_mail_receive.py b/mail_visible_email/tests/test_mail_receive.py new file mode 100644 index 000000000..9ca9eb2ff --- /dev/null +++ b/mail_visible_email/tests/test_mail_receive.py @@ -0,0 +1,61 @@ +# Copyright 2025 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.tests.common import TransactionCase, tagged +from odoo.tools import mute_logger + +from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE + + +@tagged("-at_install", "post_install") +class TestMailReceive(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.Alias = cls.env["mail.alias"] + cls.Partner = cls.env["res.partner"] + cls.MailThread = cls.env["mail.thread"] + cls.Message = cls.env["mail.message"] + + ICP = cls.env["ir.config_parameter"].sudo() + ICP.set_param("mail.catchall.domain", "fsf.org") + + cls.partner_model = cls.env["ir.model"].search([("model", "=", "res.partner")]) + cls.mail_alias_test = cls.Alias.create( + { + "alias_name": "test_alias", + "alias_model_id": cls.partner_model.id, + "alias_defaults": "{'name': 'Test Alias', 'is_company': True}", + } + ) + + @mute_logger("odoo.addons.mail.models.mail_thread", "odoo.models") + def test_incoming_email(self): + # Imitate what self.server.fetch_mail() would do + thread_id = self.MailThread.message_process( + self.Partner._name, + MAIL_TEMPLATE.format( + return_path="spambot@example.com", + email_from="spambot@example.com", + to="test_alias@fsf.org", + cc="nobody@fsf.org, anybody@fsf.org", + subject="I'm a robot, hello", + extra="", + msg_id="", + ), + ) + # We should now have a partner with the name 'Test Alias'. + self.assertTrue(thread_id) + partner = self.Partner.browse(thread_id) + self.assertEqual(partner.name, "Test Alias") + # We should have a mail message referring to this partner. + message = self.Message.search( + [ + ("model", "=", partner._name), + ("res_id", "=", partner.id), + ] + ) + self.assertTrue(message) + self.assertEqual(message.email_to, "test_alias@fsf.org") + # Use In to be independent of ordering and separator. + self.assertIn("anybody@fsf.org", message.email_cc) + self.assertIn("nobody@fsf.org", message.email_cc) diff --git a/mail_visible_email/tests/test_mail_send.py b/mail_visible_email/tests/test_mail_send.py new file mode 100644 index 000000000..1f5698622 --- /dev/null +++ b/mail_visible_email/tests/test_mail_send.py @@ -0,0 +1,69 @@ +# Copyright 2025 Therp BV . +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo.tests import Form + +from odoo.addons.mail.tests.test_mail_composer import TestMailComposer + + +class TestMailSend(TestMailComposer): + @classmethod + def setUpClass(cls): + # Based on example in mail_composer_cc_bcc. + super().setUpClass() + env = cls.env + cls.partner = env.ref("base.res_partner_address_31") + cls.partner_cc = env.ref("base.partner_demo") + cls.partner_cc2 = env.ref("base.partner_demo_portal") + cls.partner_bcc = env.ref("base.res_partner_main1") + cls.mail_template = cls._create_template( + "res.partner", + template_values={ + "auto_delete": False, + "email_to": cls.partner.email, + "email_cc": cls.partner_cc.email, + "email_bcc": cls.partner_bcc.email, + }, + ) + + def open_mail_composer_form(self): + # Based on example in mail_composer_cc_bcc. + # Use form to populate data + ctx = { + "default_partner_ids": self.partner.ids, + "default_model": self.partner._name, + "default_res_id": self.partner.id, + # to ensure consistent test results even when mail_post_defer is installed + "mail_notify_force_send": True, + } + form = Form(self.env["mail.compose.message"].with_context(**ctx)) + form.body = "

Hello

" + return form + + def test_email_to_cc(self): + form = self.open_mail_composer_form() + composer = form.save() + # Use object to update Many2many fields (form can't do like this) + composer.partner_cc_ids = self.partner_cc + composer.partner_cc_ids |= self.partner_cc2 + composer.partner_bcc_ids = self.partner_bcc + with self.mock_mail_gateway(): + composer._action_send_mail() + # Verify recipients of mail.message + message = self.partner.message_ids[0] + self.assertEqual(message.email_to, self.partner.email) + self.assertIn(self.partner_cc.email, message.email_cc) + self.assertIn(self.partner_cc2.email, message.email_cc) + self.assertEqual(message.email_bcc, self.partner_bcc.email) + + def test_email_to(self): + """Test when using email_to directly.""" + Mail = self.env["mail.mail"] + mail_id = self.mail_template.send_mail(self.partner.id, force_send=True) + self.assertTrue(mail_id) + mail = Mail.browse(mail_id) + message = mail.mail_message_id + self.assertTrue(message) + self.assertEqual(message.email_to, self.partner.email) + self.assertEqual(message.email_cc, self.partner_cc.email) + # Odoo ignores the email_bcc field in the template! + # self.assertEqual(message.email_bcc, self.partner_bcc.email) diff --git a/mail_visible_email/views/mail_message_views.xml b/mail_visible_email/views/mail_message_views.xml new file mode 100644 index 000000000..735d76763 --- /dev/null +++ b/mail_visible_email/views/mail_message_views.xml @@ -0,0 +1,15 @@ + + + + mail.message.form - mail_visible_email + mail.message + + + + + + + + + + From bfb146d2722d1f6d48dc5696a84eb9b777d2a1fc Mon Sep 17 00:00:00 2001 From: Nikos Tsirintanis Date: Tue, 23 Jun 2026 13:53:15 +0200 Subject: [PATCH 2/3] [IMP] mail_visible_email: pre-commit auto fixes --- mail_visible_email/README.rst | 49 +++++++++---------- mail_visible_email/pyproject.toml | 3 ++ .../readme/{CONFIGURE.rst => CONFIGURE.md} | 0 mail_visible_email/readme/CONTRIBUTORS.md | 3 ++ mail_visible_email/readme/CONTRIBUTORS.rst | 3 -- mail_visible_email/readme/DESCRIPTION.md | 25 ++++++++++ mail_visible_email/readme/DESCRIPTION.rst | 28 ----------- .../static/description/index.html | 44 ++++++++--------- 8 files changed, 76 insertions(+), 79 deletions(-) create mode 100644 mail_visible_email/pyproject.toml rename mail_visible_email/readme/{CONFIGURE.rst => CONFIGURE.md} (100%) create mode 100644 mail_visible_email/readme/CONTRIBUTORS.md delete mode 100644 mail_visible_email/readme/CONTRIBUTORS.rst create mode 100644 mail_visible_email/readme/DESCRIPTION.md delete mode 100644 mail_visible_email/readme/DESCRIPTION.rst diff --git a/mail_visible_email/README.rst b/mail_visible_email/README.rst index 2921be17e..8882428ef 100644 --- a/mail_visible_email/README.rst +++ b/mail_visible_email/README.rst @@ -17,33 +17,30 @@ Make emails for to, cc and bcc visible :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github - :target: https://github.com/OCA/social/tree/16.0/mail_visible_email + :target: https://github.com/OCA/social/tree/18.0/mail_visible_email :alt: OCA/social .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/social-16-0/social-16-0-mail_visible_email + :target: https://translation.odoo-community.org/projects/social-18-0/social-18-0-mail_visible_email :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/social&target_branch=16.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/social&target_branch=18.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| -In Odoo mails it is often unclear who where the other recipients of mails -received, or what the actual mail addresses where of mails sent. +In Odoo mails it is often unclear who where the other recipients of +mails received, or what the actual mail addresses where of mails sent. -This module adds the following fields to mail_message: -- email_to -- email_cc -- email_bcc +This module adds the following fields to mail_message: - email_to - +email_cc - email_bcc -For both incoming and outgoing mails, the actual to and cc headers -from the mails will be stored here. For outgoing mails also the bcc -header. +For both incoming and outgoing mails, the actual to and cc headers from +the mails will be stored here. For outgoing mails also the bcc header. In case we receive a mail because we received it on an address that was in the bcc of the email sent, the address will actually be shown on the -email_to field. This is because there is no bcc header in an incoming mail, -we will have the address in the Delivered-To header. +email_to field. This is because there is no bcc header in an incoming +mail, we will have the address in the Delivered-To header. Note that we will only store the unadorned email (without partner name), as this will be the relevant part, and the partner names are visible on @@ -51,11 +48,11 @@ other fields. For technical reasons this module depends on mail_composer_cc_bcc: -* We need the email_bcc field on mail.mail; -* The module mail_composer_bcc fundamentally changes the workings of the _send() - method on mail.mail. In order not to have to support both methods depending - on whether mail_composer_bcc is installed or not, it is easier to just make sure - it is installed. +- We need the email_bcc field on mail.mail; +- The module mail_composer_bcc fundamentally changes the workings of + the \_send() method on mail.mail. In order not to have to support + both methods depending on whether mail_composer_bcc is installed or + not, it is easier to just make sure it is installed. .. IMPORTANT:: This is an alpha version, the data model and design can change at any time without warning. @@ -78,7 +75,7 @@ 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 `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -86,19 +83,19 @@ Credits ======= Authors -~~~~~~~ +------- * Therp BV Contributors -~~~~~~~~~~~~ +------------ -* `Therp BV `_: +- `Therp BV `__: - * Ronald Portier + - Ronald Portier Maintainers -~~~~~~~~~~~ +----------- This module is maintained by the OCA. @@ -118,6 +115,6 @@ Current `maintainer `__: |maintainer-NL66278| -This module is part of the `OCA/social `_ project on GitHub. +This module is part of the `OCA/social `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_visible_email/pyproject.toml b/mail_visible_email/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/mail_visible_email/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/mail_visible_email/readme/CONFIGURE.rst b/mail_visible_email/readme/CONFIGURE.md similarity index 100% rename from mail_visible_email/readme/CONFIGURE.rst rename to mail_visible_email/readme/CONFIGURE.md diff --git a/mail_visible_email/readme/CONTRIBUTORS.md b/mail_visible_email/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..676028b8e --- /dev/null +++ b/mail_visible_email/readme/CONTRIBUTORS.md @@ -0,0 +1,3 @@ +- [Therp BV](https://therp.nl): + + > - Ronald Portier \<\> diff --git a/mail_visible_email/readme/CONTRIBUTORS.rst b/mail_visible_email/readme/CONTRIBUTORS.rst deleted file mode 100644 index 1659ed220..000000000 --- a/mail_visible_email/readme/CONTRIBUTORS.rst +++ /dev/null @@ -1,3 +0,0 @@ -* `Therp BV `_: - - * Ronald Portier diff --git a/mail_visible_email/readme/DESCRIPTION.md b/mail_visible_email/readme/DESCRIPTION.md new file mode 100644 index 000000000..292f1f584 --- /dev/null +++ b/mail_visible_email/readme/DESCRIPTION.md @@ -0,0 +1,25 @@ +In Odoo mails it is often unclear who where the other recipients of +mails received, or what the actual mail addresses where of mails sent. + +This module adds the following fields to mail_message: - email_to - +email_cc - email_bcc + +For both incoming and outgoing mails, the actual to and cc headers from +the mails will be stored here. For outgoing mails also the bcc header. + +In case we receive a mail because we received it on an address that was +in the bcc of the email sent, the address will actually be shown on the +email_to field. This is because there is no bcc header in an incoming +mail, we will have the address in the Delivered-To header. + +Note that we will only store the unadorned email (without partner name), +as this will be the relevant part, and the partner names are visible on +other fields. + +For technical reasons this module depends on mail_composer_cc_bcc: + +- We need the email_bcc field on mail.mail; +- The module mail_composer_bcc fundamentally changes the workings of the + \_send() method on mail.mail. In order not to have to support both + methods depending on whether mail_composer_bcc is installed or not, it + is easier to just make sure it is installed. diff --git a/mail_visible_email/readme/DESCRIPTION.rst b/mail_visible_email/readme/DESCRIPTION.rst deleted file mode 100644 index e2ec870b3..000000000 --- a/mail_visible_email/readme/DESCRIPTION.rst +++ /dev/null @@ -1,28 +0,0 @@ -In Odoo mails it is often unclear who where the other recipients of mails -received, or what the actual mail addresses where of mails sent. - -This module adds the following fields to mail_message: -- email_to -- email_cc -- email_bcc - -For both incoming and outgoing mails, the actual to and cc headers -from the mails will be stored here. For outgoing mails also the bcc -header. - -In case we receive a mail because we received it on an address that was -in the bcc of the email sent, the address will actually be shown on the -email_to field. This is because there is no bcc header in an incoming mail, -we will have the address in the Delivered-To header. - -Note that we will only store the unadorned email (without partner name), -as this will be the relevant part, and the partner names are visible on -other fields. - -For technical reasons this module depends on mail_composer_cc_bcc: - -* We need the email_bcc field on mail.mail; -* The module mail_composer_bcc fundamentally changes the workings of the _send() - method on mail.mail. In order not to have to support both methods depending - on whether mail_composer_bcc is installed or not, it is easier to just make sure - it is installed. diff --git a/mail_visible_email/static/description/index.html b/mail_visible_email/static/description/index.html index d523a5cdb..42331f0ad 100644 --- a/mail_visible_email/static/description/index.html +++ b/mail_visible_email/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -274,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -300,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -368,30 +369,27 @@

Make emails for to, cc and bcc visible

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:53373ba10ebe61d982a5d796725296eee83924dc9d4d6aa7e54e0ec9839f59a5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Alpha License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

-

In Odoo mails it is often unclear who where the other recipients of mails -received, or what the actual mail addresses where of mails sent.

-

This module adds the following fields to mail_message: -- email_to -- email_cc -- email_bcc

-

For both incoming and outgoing mails, the actual to and cc headers -from the mails will be stored here. For outgoing mails also the bcc -header.

+

Alpha License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

+

In Odoo mails it is often unclear who where the other recipients of +mails received, or what the actual mail addresses where of mails sent.

+

This module adds the following fields to mail_message: - email_to - +email_cc - email_bcc

+

For both incoming and outgoing mails, the actual to and cc headers from +the mails will be stored here. For outgoing mails also the bcc header.

In case we receive a mail because we received it on an address that was in the bcc of the email sent, the address will actually be shown on the -email_to field. This is because there is no bcc header in an incoming mail, -we will have the address in the Delivered-To header.

+email_to field. This is because there is no bcc header in an incoming +mail, we will have the address in the Delivered-To header.

Note that we will only store the unadorned email (without partner name), as this will be the relevant part, and the partner names are visible on other fields.

For technical reasons this module depends on mail_composer_cc_bcc:

  • We need the email_bcc field on mail.mail;
  • -
  • The module mail_composer_bcc fundamentally changes the workings of the _send() -method on mail.mail. In order not to have to support both methods depending -on whether mail_composer_bcc is installed or not, it is easier to just make sure -it is installed.
  • +
  • The module mail_composer_bcc fundamentally changes the workings of +the _send() method on mail.mail. In order not to have to support +both methods depending on whether mail_composer_bcc is installed or +not, it is easier to just make sure it is installed.

Important

@@ -421,7 +419,7 @@

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.

+feedback.

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

@@ -447,13 +445,15 @@

Contributors

Maintainers

This module is maintained by the OCA.

-Odoo Community Association + +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:

NL66278

-

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

+

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

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

From 30b58967017e4590847b6288afcd01ed27a55e87 Mon Sep 17 00:00:00 2001 From: Nikos Tsirintanis Date: Tue, 23 Jun 2026 14:25:09 +0200 Subject: [PATCH 3/3] [MIG] mail_visible_email: Migration to 18.0 --- mail_visible_email/README.rst | 35 +++++----- mail_visible_email/__manifest__.py | 8 +-- mail_visible_email/models/mail_mail.py | 65 ++++++++++--------- mail_visible_email/models/mail_thread.py | 13 +++- mail_visible_email/readme/CONFIGURE.md | 1 + mail_visible_email/readme/CONTRIBUTORS.md | 1 + mail_visible_email/readme/DESCRIPTION.md | 28 ++++---- .../static/description/index.html | 28 ++++---- mail_visible_email/tests/test_mail_receive.py | 14 ++-- mail_visible_email/tests/test_mail_send.py | 40 +++++------- 10 files changed, 114 insertions(+), 119 deletions(-) diff --git a/mail_visible_email/README.rst b/mail_visible_email/README.rst index 8882428ef..cc89e358a 100644 --- a/mail_visible_email/README.rst +++ b/mail_visible_email/README.rst @@ -16,23 +16,26 @@ Make emails for to, cc and bcc visible .. |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%2Fsocial-lightgray.png?logo=github - :target: https://github.com/OCA/social/tree/18.0/mail_visible_email - :alt: OCA/social +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmail-lightgray.png?logo=github + :target: https://github.com/OCA/mail/tree/18.0/mail_visible_email + :alt: OCA/mail .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/social-18-0/social-18-0-mail_visible_email + :target: https://translation.odoo-community.org/projects/mail-18-0/mail-18-0-mail_visible_email :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/social&target_branch=18.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/mail&target_branch=18.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| -In Odoo mails it is often unclear who where the other recipients of -mails received, or what the actual mail addresses where of mails sent. +In Odoo mails it is often unclear who were the other recipients of mails +received, or what the actual mail addresses were of mails sent. -This module adds the following fields to mail_message: - email_to - -email_cc - email_bcc +This module adds the following fields to mail_message: + +- email_to +- email_cc +- email_bcc For both incoming and outgoing mails, the actual to and cc headers from the mails will be stored here. For outgoing mails also the bcc header. @@ -46,14 +49,6 @@ Note that we will only store the unadorned email (without partner name), as this will be the relevant part, and the partner names are visible on other fields. -For technical reasons this module depends on mail_composer_cc_bcc: - -- We need the email_bcc field on mail.mail; -- The module mail_composer_bcc fundamentally changes the workings of - the \_send() method on mail.mail. In order not to have to support - both methods depending on whether mail_composer_bcc is installed or - not, it is easier to just make sure it is installed. - .. 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. @@ -72,10 +67,10 @@ This module has no separate configuration options. Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. +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 `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -115,6 +110,6 @@ Current `maintainer `__: |maintainer-NL66278| -This module is part of the `OCA/social `_ project on GitHub. +This module is part of the `OCA/mail `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_visible_email/__manifest__.py b/mail_visible_email/__manifest__.py index 808a6d3bd..4e6ec44fb 100644 --- a/mail_visible_email/__manifest__.py +++ b/mail_visible_email/__manifest__.py @@ -3,10 +3,10 @@ { "name": "Make emails for to, cc and bcc visible", "summary": "Save and show the actual email addresses used in mail.message.", - "version": "16.0.1.0.0", + "version": "18.0.1.0.0", "development_status": "Alpha", - "category": "Social", - "website": "https://github.com/OCA/social", + "category": "Email", + "website": "https://github.com/OCA/mail", "author": "Therp BV, Odoo Community Association (OCA)", "maintainers": ["NL66278"], "license": "AGPL-3", @@ -14,8 +14,8 @@ "installable": True, "depends": [ "mail", - "test_mail", "mail_composer_cc_bcc", + "test_mail", ], "data": [ "views/mail_message_views.xml", diff --git a/mail_visible_email/models/mail_mail.py b/mail_visible_email/models/mail_mail.py index 73d713f4a..455a8ede2 100644 --- a/mail_visible_email/models/mail_mail.py +++ b/mail_visible_email/models/mail_mail.py @@ -1,54 +1,57 @@ -# Copyright 2025 Therp BV . +# Copyright 2025 Therp BV . # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import models, tools +from odoo import api, models, tools def list_emails(partners): return [ - p.email and tools.mail._normalize_email(p.email) for p in partners if p.email + tools.mail.email_normalize(p.email) + for p in partners + if p.email and tools.mail.email_normalize(p.email) ] class MailMail(models.Model): _inherit = "mail.mail" - def _send(self, auto_commit=False, raise_exception=False, smtp_session=None): - """Store email_to, email_cc, email_bcc also in mail.message.""" - for this in self.filtered(lambda r: r.state == "outgoing"): + @api.model_create_multi + def create(self, values_list): + mails = super().create(values_list) + for mail in mails: message_vals = {} - email_values = this._get_email_values() - for fieldname in ["email_to", "email_cc", "email_bcc"]: - emails = email_values.get(fieldname, []) - if not emails and not this[fieldname]: - continue - message_vals[fieldname] = this._append_email(fieldname, emails) + email_values = mail._get_email_values() + for fname in ("email_to", "email_cc", "email_bcc"): + emails = email_values.get(fname, []) + if emails: + message_vals[fname] = mail._append_email(fname, emails) if message_vals: - this.mail_message_id.write(message_vals) - return super()._send( - auto_commit=auto_commit, - raise_exception=raise_exception, - smtp_session=smtp_session, - ) + mail.mail_message_id.write(message_vals) + return mails def _get_email_values(self): + """Return normalized email lists for to, cc and bcc. + - Composer send: recipient_cc_ids/recipient_bcc_ids are set on mail.mail + at create time by the composer's _prepare_mail_values. + - Template send (send_mail): email_to, email_cc, email_bcc + are written directly to mail.mail at create time from the rendered + template values. + """ self.ensure_one() - partners_to = ( - self.recipient_ids - self.recipient_cc_ids - self.recipient_bcc_ids - ) + cc_bcc = self.recipient_cc_ids + self.recipient_bcc_ids + to_partners = self.recipient_ids - cc_bcc return { - "email_to": list_emails(partners_to), - "email_cc": list_emails(self.recipient_cc_ids), - "email_bcc": list_emails(self.recipient_bcc_ids), + "email_to": list_emails(to_partners) + or tools.mail.email_normalize_all(self.email_to or ""), + "email_cc": list_emails(self.recipient_cc_ids) + or tools.mail.email_normalize_all(self.email_cc or ""), + "email_bcc": list_emails(self.recipient_bcc_ids) + or tools.mail.email_normalize_all(self.email_bcc or ""), } def _append_email(self, fieldname, emails): - """Do not override existing emails.""" + """Merge new emails with any already stored on mail.message.""" self.ensure_one() - message = self.mail_message_id - preset_emails = self[fieldname] - if preset_emails: - emails += tools.email_normalize_all(preset_emails) - existing = message[fieldname] + existing = self.mail_message_id[fieldname] if existing: emails += existing.split(",") - return ",".join(list(set(emails))) + return ",".join(dict.fromkeys(filter(None, emails))) diff --git a/mail_visible_email/models/mail_thread.py b/mail_visible_email/models/mail_thread.py index aa1238124..43f8df124 100644 --- a/mail_visible_email/models/mail_thread.py +++ b/mail_visible_email/models/mail_thread.py @@ -1,8 +1,10 @@ -# Copyright 2025 Therp BV . +# Copyright 2025 Therp BV . # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). from odoo import api, models +_VISIBLE_EMAIL_FIELDS = {"email_to", "email_cc", "email_bcc"} + class MailThread(models.AbstractModel): _inherit = "mail.thread" @@ -19,3 +21,12 @@ def _message_route_process(self, message, message_dict, routes): message_dict["email_to"] = message_dict.get("to", False) message_dict["email_cc"] = message_dict.get("cc", False) return super()._message_route_process(message, message_dict, routes) + + def _get_message_create_ignore_field_names(self): + return super()._get_message_create_ignore_field_names() | _VISIBLE_EMAIL_FIELDS + + def _message_post_after_hook(self, message, msg_values): + vals = {k: msg_values[k] for k in _VISIBLE_EMAIL_FIELDS if msg_values.get(k)} + if vals: + message.write(vals) + return super()._message_post_after_hook(message, msg_values) diff --git a/mail_visible_email/readme/CONFIGURE.md b/mail_visible_email/readme/CONFIGURE.md index 7d2c7b671..c233bc2c8 100644 --- a/mail_visible_email/readme/CONFIGURE.md +++ b/mail_visible_email/readme/CONFIGURE.md @@ -1 +1,2 @@ This module has no separate configuration options. + \ No newline at end of file diff --git a/mail_visible_email/readme/CONTRIBUTORS.md b/mail_visible_email/readme/CONTRIBUTORS.md index 676028b8e..a184afa07 100644 --- a/mail_visible_email/readme/CONTRIBUTORS.md +++ b/mail_visible_email/readme/CONTRIBUTORS.md @@ -1,3 +1,4 @@ - [Therp BV](https://therp.nl): > - Ronald Portier \<\> + \ No newline at end of file diff --git a/mail_visible_email/readme/DESCRIPTION.md b/mail_visible_email/readme/DESCRIPTION.md index 292f1f584..284aa9e56 100644 --- a/mail_visible_email/readme/DESCRIPTION.md +++ b/mail_visible_email/readme/DESCRIPTION.md @@ -1,25 +1,21 @@ -In Odoo mails it is often unclear who where the other recipients of -mails received, or what the actual mail addresses where of mails sent. +In Odoo mails it is often unclear who were the other recipients of mails +received, or what the actual mail addresses were of mails sent. -This module adds the following fields to mail_message: - email_to - -email_cc - email_bcc +This module adds the following fields to mail_message: -For both incoming and outgoing mails, the actual to and cc headers from -the mails will be stored here. For outgoing mails also the bcc header. +- email_to +- email_cc +- email_bcc + +For both incoming and outgoing mails, the actual to and cc headers +from the mails will be stored here. For outgoing mails also the bcc +header. In case we receive a mail because we received it on an address that was in the bcc of the email sent, the address will actually be shown on the -email_to field. This is because there is no bcc header in an incoming -mail, we will have the address in the Delivered-To header. +email_to field. This is because there is no bcc header in an incoming mail, +we will have the address in the Delivered-To header. Note that we will only store the unadorned email (without partner name), as this will be the relevant part, and the partner names are visible on other fields. - -For technical reasons this module depends on mail_composer_cc_bcc: - -- We need the email_bcc field on mail.mail; -- The module mail_composer_bcc fundamentally changes the workings of the - \_send() method on mail.mail. In order not to have to support both - methods depending on whether mail_composer_bcc is installed or not, it - is easier to just make sure it is installed. diff --git a/mail_visible_email/static/description/index.html b/mail_visible_email/static/description/index.html index 42331f0ad..f28cd7f74 100644 --- a/mail_visible_email/static/description/index.html +++ b/mail_visible_email/static/description/index.html @@ -369,11 +369,15 @@

Make emails for to, cc and bcc visible

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:53373ba10ebe61d982a5d796725296eee83924dc9d4d6aa7e54e0ec9839f59a5 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Alpha License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

-

In Odoo mails it is often unclear who where the other recipients of -mails received, or what the actual mail addresses where of mails sent.

-

This module adds the following fields to mail_message: - email_to - -email_cc - email_bcc

+

Alpha License: AGPL-3 OCA/mail Translate me on Weblate Try me on Runboat

+

In Odoo mails it is often unclear who were the other recipients of mails +received, or what the actual mail addresses were of mails sent.

+

This module adds the following fields to mail_message:

+
    +
  • email_to
  • +
  • email_cc
  • +
  • email_bcc
  • +

For both incoming and outgoing mails, the actual to and cc headers from the mails will be stored here. For outgoing mails also the bcc header.

In case we receive a mail because we received it on an address that was @@ -383,14 +387,6 @@

Make emails for to, cc and bcc visible

Note that we will only store the unadorned email (without partner name), as this will be the relevant part, and the partner names are visible on other fields.

-

For technical reasons this module depends on mail_composer_cc_bcc:

-
    -
  • We need the email_bcc field on mail.mail;
  • -
  • The module mail_composer_bcc fundamentally changes the workings of -the _send() method on mail.mail. In order not to have to support -both methods depending on whether mail_composer_bcc is installed or -not, it is easier to just make sure it is installed.
  • -

Important

This is an alpha version, the data model and design can change at any time without warning. @@ -416,10 +412,10 @@

Configuration

Bug Tracker

-

Bugs are tracked on GitHub Issues. +

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.

+feedback.

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

@@ -453,7 +449,7 @@

Maintainers

promote its widespread use.

Current maintainer:

NL66278

-

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

+

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

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

diff --git a/mail_visible_email/tests/test_mail_receive.py b/mail_visible_email/tests/test_mail_receive.py index 9ca9eb2ff..e24defe4f 100644 --- a/mail_visible_email/tests/test_mail_receive.py +++ b/mail_visible_email/tests/test_mail_receive.py @@ -15,14 +15,19 @@ def setUpClass(cls): cls.Partner = cls.env["res.partner"] cls.MailThread = cls.env["mail.thread"] cls.Message = cls.env["mail.message"] - - ICP = cls.env["ir.config_parameter"].sudo() - ICP.set_param("mail.catchall.domain", "fsf.org") + cls.alias_domain = cls.env["mail.alias.domain"].create( + { + "name": "fsf.org", + "catchall_alias": "catchall", + } + ) + cls.env.company.alias_domain_id = cls.alias_domain cls.partner_model = cls.env["ir.model"].search([("model", "=", "res.partner")]) cls.mail_alias_test = cls.Alias.create( { "alias_name": "test_alias", + "alias_domain_id": cls.alias_domain.id, "alias_model_id": cls.partner_model.id, "alias_defaults": "{'name': 'Test Alias', 'is_company': True}", } @@ -43,11 +48,9 @@ def test_incoming_email(self): msg_id="", ), ) - # We should now have a partner with the name 'Test Alias'. self.assertTrue(thread_id) partner = self.Partner.browse(thread_id) self.assertEqual(partner.name, "Test Alias") - # We should have a mail message referring to this partner. message = self.Message.search( [ ("model", "=", partner._name), @@ -56,6 +59,5 @@ def test_incoming_email(self): ) self.assertTrue(message) self.assertEqual(message.email_to, "test_alias@fsf.org") - # Use In to be independent of ordering and separator. self.assertIn("anybody@fsf.org", message.email_cc) self.assertIn("nobody@fsf.org", message.email_cc) diff --git a/mail_visible_email/tests/test_mail_send.py b/mail_visible_email/tests/test_mail_send.py index 1f5698622..182fd0189 100644 --- a/mail_visible_email/tests/test_mail_send.py +++ b/mail_visible_email/tests/test_mail_send.py @@ -1,20 +1,19 @@ # Copyright 2025 Therp BV . # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo.tests import Form +from odoo.tests import Form, tagged -from odoo.addons.mail.tests.test_mail_composer import TestMailComposer +from odoo.addons.mail.tests.common import MailCommon -class TestMailSend(TestMailComposer): +@tagged("-at_install", "post_install") +class TestMailSend(MailCommon): @classmethod def setUpClass(cls): - # Based on example in mail_composer_cc_bcc. super().setUpClass() - env = cls.env - cls.partner = env.ref("base.res_partner_address_31") - cls.partner_cc = env.ref("base.partner_demo") - cls.partner_cc2 = env.ref("base.partner_demo_portal") - cls.partner_bcc = env.ref("base.res_partner_main1") + cls.partner = cls.env.ref("base.res_partner_address_31") + cls.partner_cc = cls.env.ref("base.partner_demo") + cls.partner_cc2 = cls.env.ref("base.partner_demo_portal") + cls.partner_bcc = cls.env.ref("base.res_partner_main1") cls.mail_template = cls._create_template( "res.partner", template_values={ @@ -26,44 +25,35 @@ def setUpClass(cls): ) def open_mail_composer_form(self): - # Based on example in mail_composer_cc_bcc. - # Use form to populate data ctx = { "default_partner_ids": self.partner.ids, "default_model": self.partner._name, - "default_res_id": self.partner.id, - # to ensure consistent test results even when mail_post_defer is installed + "default_res_ids": self.partner.ids, "mail_notify_force_send": True, } form = Form(self.env["mail.compose.message"].with_context(**ctx)) form.body = "

Hello

" return form - def test_email_to_cc(self): + def test_email_to_cc_bcc_via_composer(self): + """Sending via composer populates email_to, cc, bcc on mail.message.""" form = self.open_mail_composer_form() composer = form.save() - # Use object to update Many2many fields (form can't do like this) - composer.partner_cc_ids = self.partner_cc - composer.partner_cc_ids |= self.partner_cc2 + composer.partner_cc_ids = self.partner_cc | self.partner_cc2 composer.partner_bcc_ids = self.partner_bcc with self.mock_mail_gateway(): composer._action_send_mail() - # Verify recipients of mail.message message = self.partner.message_ids[0] - self.assertEqual(message.email_to, self.partner.email) + self.assertEqual(message.email_to, self.partner.email) # add this self.assertIn(self.partner_cc.email, message.email_cc) self.assertIn(self.partner_cc2.email, message.email_cc) self.assertEqual(message.email_bcc, self.partner_bcc.email) - def test_email_to(self): - """Test when using email_to directly.""" + def test_email_to_cc_via_template(self): + """Sending via template populates email_to and email_cc on mail.message.""" Mail = self.env["mail.mail"] mail_id = self.mail_template.send_mail(self.partner.id, force_send=True) - self.assertTrue(mail_id) mail = Mail.browse(mail_id) message = mail.mail_message_id - self.assertTrue(message) self.assertEqual(message.email_to, self.partner.email) self.assertEqual(message.email_cc, self.partner_cc.email) - # Odoo ignores the email_bcc field in the template! - # self.assertEqual(message.email_bcc, self.partner_bcc.email)