diff --git a/mail_alias_with_domain/README.rst b/mail_alias_with_domain/README.rst new file mode 100644 index 0000000000..5e31e26abc --- /dev/null +++ b/mail_alias_with_domain/README.rst @@ -0,0 +1,131 @@ +====================== +Mail Alias With Domain +====================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:3c7166c28331e6e457d77bd71f2af2420004c8b92ad4b8d745f80ad1e6884603 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/14.0/mail_alias_with_domain + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-14-0/social-14-0-mail_alias_with_domain + :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=14.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds possibility to process aliases together with domain. + +For example, suppose we have 3 companies in odoo. +Each company wants to have an alias where customers can send the bills. +invoice@company1.com +invoice@company2.com +invoice@company3.com + +In odoo, aliases are unique, and this module extends this functionality in +such a way that you can have many of the same aliases but with different domains. + +Note that when an incoming mail can be linked to an alias with a domain, +this will be the only alias used. However when an incoming mail can be +linked to multiple aliasses that have a domain, it is possible to have +multiple used. + +FOR DEVELOPERS + +In the default alias system, only the local part of an email address (the part +before the @) is used to link an incoming email to an alias. This happens in the +message_route method of the mail.thread model. + +Aliasses in standard Odoo store the alias_name field without domain. + +To still be able to use a domain name, we need a trick. What we will do is: + +* Replace the alias_name in the user interface with an alias_entry field, where a + complete email address can be entered. + +* If an alias is entered as a complete email address, this will be stored in the + alias_name as __at__. For instance alex__at__example.com. + alias_name is therefore changed from a writable field to a stored computed field. + +* The computation of alias_domain will be enhanced to take full email addresses into + account. + +* If an incoming mail can be linked to a full email address alias, we will write a + context key pointing to this alias. The search method of mail.alias will be overriden + to check for this key, and then not search at all, but just return the alias + requested. + + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +To use this module, you need to: + +Got to the mail aliasses and check which aliasses you want to link to a specific +domain. + +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 +~~~~~~~ + +* Solvti +* Therp BV + +Contributors +~~~~~~~~~~~~ + +* `Solvti sp. z o.o. `_: + + * Jakub Wiselka + +* `Therp `_: + + * Ronald Portier (ronald@therp.nl) + +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/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/mail_alias_with_domain/__init__.py b/mail_alias_with_domain/__init__.py new file mode 100644 index 0000000000..53d63dae48 --- /dev/null +++ b/mail_alias_with_domain/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import models +from .post_init_hook import init_alias_entry diff --git a/mail_alias_with_domain/__manifest__.py b/mail_alias_with_domain/__manifest__.py new file mode 100644 index 0000000000..28d0321546 --- /dev/null +++ b/mail_alias_with_domain/__manifest__.py @@ -0,0 +1,17 @@ +# Copyright 2023 Solvti sp. z o.o. (https://solvti.pl) +# Copyright 2025-2026 Therp BV (https://therp.nl) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Mail Alias With Domain", + "summary": "Allow simple mail alias to be combined with a mail domain", + "author": "Solvti, Therp BV, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/social", + "version": "14.0.1.0.0", + "license": "AGPL-3", + "application": False, + "installable": True, + "post_init_hook": "init_alias_entry", + "depends": ["mail"], + "data": ["views/mail_alias_views.xml"], +} diff --git a/mail_alias_with_domain/models/__init__.py b/mail_alias_with_domain/models/__init__.py new file mode 100644 index 0000000000..06aaa650a8 --- /dev/null +++ b/mail_alias_with_domain/models/__init__.py @@ -0,0 +1,4 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import mail_alias +from . import mail_thread diff --git a/mail_alias_with_domain/models/mail_alias.py b/mail_alias_with_domain/models/mail_alias.py new file mode 100644 index 0000000000..d1ca65237c --- /dev/null +++ b/mail_alias_with_domain/models/mail_alias.py @@ -0,0 +1,105 @@ +# Copyright 2023 Solvti sp. z o.o. (https://solvti.pl). +# Copyright 2025-2026 Therp BV (https://therp.nl). +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from odoo import api, fields, models + + +class Alias(models.Model): + _inherit = "mail.alias" + + @api.depends("alias_name") + def _compute_alias_domain(self): + alias_with_domain = self.filtered( + lambda r: r.alias_name and "__at__" in r.alias_name + ) + for alias in alias_with_domain: + alias.alias_domain = alias.alias_name.split("__at__")[1] + alias_without_domain = self - alias_with_domain + if alias_without_domain: + super(Alias, alias_without_domain)._compute_alias_domain() + return None + + alias_entry = fields.Char( + help="This will be used to enter an email, complete with domain", + ) + + _sql_constraints = [ + ("unique_alias_entry", "UNIQUE(alias_entry)", "Alias entry must be unique!") + ] + + @api.model + def search(self, domain, **kwargs): + """If mail alias in context, return this as result.""" + matching_alias = self.env.context.get("matching_alias", False) + if matching_alias: + return matching_alias + return super().search(domain, **kwargs) + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + self._patch_alias_vals(vals) + records = super().create(vals_list) + records._synchronize_alias_entry_with_name() + return records + + def write(self, vals): + self._patch_alias_vals(vals) + result = super().write(vals) + self._synchronize_alias_entry_with_name() + return result + + def _synchronize_alias_entry_with_name(self): + """In case alias created/written without alias_entry, complete entry field.""" + for this in self: + if not this.alias_name: + alias_entry = False + elif "__at__" in this.alias_name: + alias_entry = this.alias_name.replace("__at__", "@") + else: + alias_entry = this.alias_name + if this.alias_entry != alias_entry: + super(Alias, this).write({"alias_entry": alias_entry}) + return None + + @api.model + def _patch_alias_vals(self, vals): + """If vals contains alias_entry, add corresponding alias_name.""" + alias_entry = vals.get("alias_entry", False) + if alias_entry: + default_domain = self._get_default_domain() + if "@" not in alias_entry: + alias_name = alias_entry + elif default_domain and default_domain in alias_entry: + alias_name = alias_entry.split("@")[0] + else: + alias_name = alias_entry.replace("@", "__at__") + vals["alias_name"] = alias_name + + @api.model + def _get_default_domain(self): + """get default domain.""" + ICP = self.env["ir.config_parameter"].sudo() + return ICP.get_param("mail.catchall.domain") + + @api.model + def get_clean_email(self, email): + """Users tend to pollute emails with extra info. get just the email.""" + # In Odoo 17.0 there is a new method parse_contact_from_email in + # odoo/tools/mail.py that we could use for this purpose. + if email: + # 1. Replace special characters with spaces. + cleaned = ( + email.replace('"', " ") + .replace("<", " ") + .replace(">", " ") + .replace(",", " ") + ) + # 2. Split on whitespace + parts = cleaned.split() + # 3. Find the part with an '@' if any and assume it is the real email. + for part in parts: + if "@" in part: + return part.lower() + return False # Else module partner_email_check would raise ValidationError. diff --git a/mail_alias_with_domain/models/mail_thread.py b/mail_alias_with_domain/models/mail_thread.py new file mode 100644 index 0000000000..8e2f2e68b0 --- /dev/null +++ b/mail_alias_with_domain/models/mail_thread.py @@ -0,0 +1,46 @@ +# Copyright 2023 Solvti sp. z o.o. (https://solvti.pl) +# Copyright 2025-2026 Therp BV (https://therp.nl) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). +from odoo import api, models, tools + + +class MailThread(models.AbstractModel): + _inherit = "mail.thread" + + @api.model + def message_route( + self, message, message_dict, model=None, thread_id=None, custom_values=None + ): + """Check for a recipient that can be linked to a full domain alias.""" + if not self.env.context.get("matching_alias", False): + matching_alias = self._find_alias_with_domain(message_dict) + if matching_alias: + # Call super with extra context. + return ( + super() + .with_context(matching_alias=matching_alias) + .message_route( + message, + message_dict, + model=model, + thread_id=thread_id, + custom_values=custom_values, + ) + ) + return super().message_route( + message, + message_dict, + model=model, + thread_id=thread_id, + custom_values=custom_values, + ) + + def _find_alias_with_domain(self, message_dict): + """Find all aliasses that match.""" + Alias = self.env["mail.alias"] + alias_names = [ + email.replace("@", "__at__") + # tools.email_split only returns clean email addresses (no names). + for email in tools.email_split(message_dict["recipients"]) + ] + return Alias.search([("alias_name", "in", alias_names)]) diff --git a/mail_alias_with_domain/post_init_hook.py b/mail_alias_with_domain/post_init_hook.py new file mode 100644 index 0000000000..dbd35d0b6a --- /dev/null +++ b/mail_alias_with_domain/post_init_hook.py @@ -0,0 +1,10 @@ +# Copyright 2025 Therp BV (https://therp.nl) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +def init_alias_entry(cr, registry): + cr.execute( + "UPDATE mail_alias" + " SET alias_entry = alias_name" + " WHERE alias_entry IS NULL AND NOT alias_name IS NULL" + ) diff --git a/mail_alias_with_domain/readme/CONTRIBUTORS.rst b/mail_alias_with_domain/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..167a32718c --- /dev/null +++ b/mail_alias_with_domain/readme/CONTRIBUTORS.rst @@ -0,0 +1,7 @@ +* `Solvti sp. z o.o. `_: + + * Jakub Wiselka + +* `Therp `_: + + * Ronald Portier (ronald@therp.nl) diff --git a/mail_alias_with_domain/readme/DESCRIPTION.rst b/mail_alias_with_domain/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..cc5a4e2ede --- /dev/null +++ b/mail_alias_with_domain/readme/DESCRIPTION.rst @@ -0,0 +1,41 @@ +This module adds possibility to process aliases together with domain. + +For example, suppose we have 3 companies in odoo. +Each company wants to have an alias where customers can send the bills. +invoice@company1.com +invoice@company2.com +invoice@company3.com + +In odoo, aliases are unique, and this module extends this functionality in +such a way that you can have many of the same aliases but with different domains. + +Note that when an incoming mail can be linked to an alias with a domain, +this will be the only alias used. However when an incoming mail can be +linked to multiple aliasses that have a domain, it is possible to have +multiple used. + +FOR DEVELOPERS + +In the default alias system, only the local part of an email address (the part +before the @) is used to link an incoming email to an alias. This happens in the +message_route method of the mail.thread model. + +Aliasses in standard Odoo store the alias_name field without domain. + +To still be able to use a domain name, we need a trick. What we will do is: + +* Replace the alias_name in the user interface with an alias_entry field, where a + complete email address can be entered. + +* If an alias is entered as a complete email address, this will be stored in the + alias_name as __at__. For instance alex__at__example.com. + alias_name is therefore changed from a writable field to a stored computed field. + +* The computation of alias_domain will be enhanced to take full email addresses into + account. + +* If an incoming mail can be linked to a full email address alias, we will write a + context key pointing to this alias. The search method of mail.alias will be overriden + to check for this key, and then not search at all, but just return the alias + requested. + diff --git a/mail_alias_with_domain/readme/USAGE.rst b/mail_alias_with_domain/readme/USAGE.rst new file mode 100644 index 0000000000..93d8c7603e --- /dev/null +++ b/mail_alias_with_domain/readme/USAGE.rst @@ -0,0 +1,4 @@ +To use this module, you need to: + +Got to the mail aliasses and check which aliasses you want to link to a specific +domain. diff --git a/mail_alias_with_domain/static/description/icon.png b/mail_alias_with_domain/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/mail_alias_with_domain/static/description/icon.png differ diff --git a/mail_alias_with_domain/static/description/index.html b/mail_alias_with_domain/static/description/index.html new file mode 100644 index 0000000000..373bec966c --- /dev/null +++ b/mail_alias_with_domain/static/description/index.html @@ -0,0 +1,468 @@ + + + + + +Mail Alias With Domain + + + +
+

Mail Alias With Domain

+ + +

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

+

This module adds possibility to process aliases together with domain.

+

For example, suppose we have 3 companies in odoo. +Each company wants to have an alias where customers can send the bills. +invoice@company1.com +invoice@company2.com +invoice@company3.com

+

In odoo, aliases are unique, and this module extends this functionality in +such a way that you can have many of the same aliases but with different domains.

+

Note that when an incoming mail can be linked to an alias with a domain, +this will be the only alias used. However when an incoming mail can be +linked to multiple aliasses that have a domain, it is possible to have +multiple used.

+

FOR DEVELOPERS

+

In the default alias system, only the local part of an email address (the part +before the @) is used to link an incoming email to an alias. This happens in the +message_route method of the mail.thread model.

+

Aliasses in standard Odoo store the alias_name field without domain.

+

To still be able to use a domain name, we need a trick. What we will do is:

+
    +
  • Replace the alias_name in the user interface with an alias_entry field, where a +complete email address can be entered.
  • +
  • If an alias is entered as a complete email address, this will be stored in the +alias_name as <localpart>__at__<domain>. For instance alex__at__example.com. +alias_name is therefore changed from a writable field to a stored computed field.
  • +
  • The computation of alias_domain will be enhanced to take full email addresses into +account.
  • +
  • If an incoming mail can be linked to a full email address alias, we will write a +context key pointing to this alias. The search method of mail.alias will be overriden +to check for this key, and then not search at all, but just return the alias +requested.
  • +
+

Table of contents

+ +
+

Usage

+

To use this module, you need to:

+

Got to the mail aliasses and check which aliasses you want to link to a specific +domain.

+
+
+

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

+
    +
  • Solvti
  • +
  • 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.

+

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_alias_with_domain/tests/__init__.py b/mail_alias_with_domain/tests/__init__.py new file mode 100644 index 0000000000..61a091079d --- /dev/null +++ b/mail_alias_with_domain/tests/__init__.py @@ -0,0 +1,3 @@ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from . import test_mail_thread diff --git a/mail_alias_with_domain/tests/test_mail_thread.py b/mail_alias_with_domain/tests/test_mail_thread.py new file mode 100644 index 0000000000..91030410cd --- /dev/null +++ b/mail_alias_with_domain/tests/test_mail_thread.py @@ -0,0 +1,225 @@ +# flake8: noqa: E222,E231,E711 +# Copyright 2023 Solvti sp. z o.o. (https://solvti.pl) +# Copyright 2025 Therp BV (https://therp.nl) +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +from email.message import EmailMessage + +from odoo.tests.common import SavepointCase + + +class TestMailThread(SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + ICP = cls.env["ir.config_parameter"].sudo() + ICP.set_param("mail.catchall.domain", "fsf.org") + + cls.contact_model = cls.env["ir.model"].search([("model", "=", "res.partner")]) + cls.Alias = cls.env["mail.alias"] + cls.mail_alias_with_domain = cls.Alias.create( + { + "alias_entry": "test_alias_entry@example.com", + "alias_model_id": cls.contact_model.id, + "alias_defaults": "{'name': 'Test Alias With Domain'}", + } + ) + cls.mail_alias_no_domain = cls.Alias.create( + { + "alias_entry": "test_alias", + "alias_model_id": cls.contact_model.id, + "alias_defaults": "{'name': 'Test Alias No Domain'}", + } + ) + message = EmailMessage() + message.add_header("Subject", "New Alias Test") + message.add_header("From", "test.user@example.com") + message.add_header("To", "info@fsf.org") + message.set_default_type("text/plain") + message.set_content("Please Create New Contact!") + cls.message = message + + cls.message_dict = { + "message_type": "email", + "message_id": "", + "subject": "New Contact", + "email_from": '"test.user@company.com" ', + "from": '"test.user@company.com" ', + "cc": "", + "partner_ids": [], + "references": "", + "in_reply_to": "", + "date": "2021-09-23 09:03:13", + "body": "Hello, Please create new contact", + "attachments": [], + "bounced_email": False, + "bounced_partner": "", + "bounced_msg_id": False, + "bounced_message": "", + } + + def test_patch_alias_vals(self): + # Non default domain in alias_entry. + vals = {"alias_entry": "test_patch@example.com"} + self.Alias._patch_alias_vals(vals) + self.assertEqual(vals["alias_name"], "test_patch__at__example.com") + # Default domain in alias_entry. + vals = {"alias_entry": "test_patch@fsf.org"} + self.Alias._patch_alias_vals(vals) + self.assertEqual(vals["alias_name"], "test_patch") + # No domain in alias_entry. + vals = {"alias_entry": "test_patch"} + self.Alias._patch_alias_vals(vals) + self.assertEqual(vals["alias_name"], "test_patch") + + def test_create_alias_by_alias_entry(self): + self.assertEqual( + self.mail_alias_with_domain.alias_name, "test_alias_entry__at__example.com" + ) + self.assertEqual(self.mail_alias_with_domain.alias_domain, "example.com") + self.assertEqual(self.mail_alias_no_domain.alias_name, "test_alias") + self.assertEqual(self.mail_alias_no_domain.alias_domain, "fsf.org") + + def test_write_alias_by_alias_entry(self): + self.assertEqual( + self.mail_alias_with_domain.alias_name, "test_alias_entry__at__example.com" + ) + self.mail_alias_with_domain.write({"alias_entry": "test_new_name@example.org"}) + self.assertEqual( + self.mail_alias_with_domain.alias_name, "test_new_name__at__example.org" + ) + # New domain should have been computed as well. + self.assertEqual(self.mail_alias_with_domain.alias_domain, "example.org") + + def test_clear_alias_name(self): + # Clearing the alias_name should also clear the alias_entry. + self.assertEqual( + self.mail_alias_with_domain.alias_name, "test_alias_entry__at__example.com" + ) + self.mail_alias_with_domain.write({"alias_name": False}) + self.assertEqual(self.mail_alias_with_domain.alias_entry, False) + # Domain should have been reset to default. + self.assertEqual(self.mail_alias_with_domain.alias_domain, "fsf.org") + + def test_create_alias_by_alias_name(self): + alias_with_domain = self.Alias.create( + { + "alias_name": "test_alias_name__at__example.com", + "alias_model_id": self.contact_model.id, + "alias_defaults": "{'name': 'Test Alias Name'}", + } + ) + self.assertEqual(alias_with_domain.alias_entry, "test_alias_name@example.com") + self.assertEqual(alias_with_domain.alias_domain, "example.com") + alias_no_domain = self.Alias.create( + { + "alias_name": "test_alias_no_domain", + "alias_model_id": self.contact_model.id, + "alias_defaults": "{'name': 'Test Alias'}", + } + ) + self.assertEqual(alias_no_domain.alias_entry, "test_alias_no_domain") + self.assertEqual(alias_no_domain.alias_domain, "fsf.org") + + def test_find_alias_with_domain(self): + email_to = "test_alias_entry@example.com" + self.message_dict.update( + { + "recipients": f'"{email_to}" <{email_to}>', + "to": f""" + "{email_to}" <{email_to}>, "someone@test-fake.com" + """, + } + ) + Thread = self.env["mail.thread"] + matching_alias = Thread._find_alias_with_domain(self.message_dict) + self.assertEqual(matching_alias, self.mail_alias_with_domain) + + def test_message_route_include_domain_alias(self): + email_to = "test_alias_entry@example.com" + self.message.replace_header("To", email_to) + self.message_dict.update( + { + "recipients": f'"{email_to}" <{email_to}>', + "to": f""" + "{email_to}" <{email_to}>, "someone@test-fake.com" + """, + } + ) + routes = self.env["mail.thread"].message_route( + self.message, + self.message_dict, + model=self.contact_model.model, + thread_id=None, + custom_values=None, + ) + self.assertEqual(len(routes), 1) # Will only use route with domain. + self.assertEqual(routes[0][4], self.mail_alias_with_domain) + + def test_message_route_different_domain_alias(self): + email_to = "test_alias_entry@something_else.com" + self.message.replace_header("To", email_to) + self.message_dict.update( + { + "recipients": f'"{email_to}" <{email_to}>', + "to": ( + f'"{email_to}" <{email_to}>, ' + '"someone@test-fake.com" ' + ), + } + ) + routes = self.env["mail.thread"].message_route( + self.message, + self.message_dict, + model=self.contact_model.model, + thread_id=None, + custom_values=None, + ) + self.assertEqual(len(routes), 1) # Should be default route + self.assertEqual(routes[0][0], self.contact_model.model) + self.assertEqual(routes[0][4], None) + + def test_message_route_two_types_of_aliases_at_once(self): + email_to_1 = "test_alias_entry@example.com" + email_to_2 = "test_alias@example_mail.com" + self.message.replace_header("To", f"{email_to_1}, {email_to_2}") + self.message_dict.update( + { + "recipients": ( + f'"{email_to_1}" <{email_to_1}>, "{email_to_2}" <{email_to_2}>' + ), + "to": ( + f'"{email_to_1}" <{email_to_1}>, "{email_to_2}" <{email_to_2}>,' + '"abc@abc.com" ' + ), + } + ) + routes = self.env["mail.thread"].message_route( + self.message, + self.message_dict, + model=self.contact_model.model, + thread_id=None, + custom_values=None, + ) + self.assertEqual(len(routes), 1) # Will only use route with domain. + self.assertEqual(routes[0][4], self.mail_alias_with_domain) + + def test_message_route_no_domain_alias(self): + email_to = "test_alias@example_mail.com" + self.message.replace_header("To", f"{email_to}") + self.message_dict.update( + { + "recipients": f'"{email_to}" <{email_to}>', + "to": f'"{email_to}" <{email_to}>, "abc@abc.com" ', + } + ) + routes = self.env["mail.thread"].message_route( + self.message, + self.message_dict, + model=self.contact_model.model, + thread_id=None, + custom_values=None, + ) + self.assertEqual(len(routes), 1) # Will only use route without domain. + self.assertEqual(routes[0][4], self.mail_alias_no_domain) diff --git a/mail_alias_with_domain/views/mail_alias_views.xml b/mail_alias_with_domain/views/mail_alias_views.xml new file mode 100644 index 0000000000..9034460038 --- /dev/null +++ b/mail_alias_with_domain/views/mail_alias_views.xml @@ -0,0 +1,31 @@ + + + + + + mail.alias.form - mail_alias_with_domain + mail.alias + + + + 1 + + + + + + + + mail.alias.tree.inherit - mail_alias_with_domain + mail.alias + + + + 1 + + + + + + + diff --git a/setup/mail_alias_with_domain/odoo/addons/mail_alias_with_domain b/setup/mail_alias_with_domain/odoo/addons/mail_alias_with_domain new file mode 120000 index 0000000000..24eaa858ad --- /dev/null +++ b/setup/mail_alias_with_domain/odoo/addons/mail_alias_with_domain @@ -0,0 +1 @@ +../../../../mail_alias_with_domain \ No newline at end of file diff --git a/setup/mail_alias_with_domain/setup.py b/setup/mail_alias_with_domain/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/mail_alias_with_domain/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)