diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG
index 639015b8f7..ce605b6bba 100644
--- a/trytond/CHANGELOG
+++ b/trytond/CHANGELOG
@@ -1,3 +1,4 @@
+* Set Message-Id to email sent
* Add pair/unpair in tools
Version 7.6.0 - 2025-04-28
diff --git a/trytond/trytond/ir/email_.py b/trytond/trytond/ir/email_.py
index 04a242f8d3..71ee919409 100644
--- a/trytond/trytond/ir/email_.py
+++ b/trytond/trytond/ir/email_.py
@@ -69,6 +69,7 @@ class Email(ResourceAccessMixin, ModelSQL, ModelView):
'ir.email.address', 'email', "Addresses", readonly=True)
subject = fields.Char("Subject", readonly=True)
body = fields.Text("Body", readonly=True)
+ message_id = fields.Char("Message-ID", readonly=True)
@classmethod
def __setup__(cls):
@@ -228,6 +229,7 @@ class Email(ResourceAccessMixin, ModelSQL, ModelView):
recipients_hidden=msg['Bcc'],
addresses=[{'address': a} for a in to_addrs],
subject=msg['Subject'],
+ message_id=msg['Message-ID'],
**values)
diff --git a/trytond/trytond/ir/view/email_form.xml b/trytond/trytond/ir/view/email_form.xml
index b60307d7fa..2620b6b277 100644
--- a/trytond/trytond/ir/view/email_form.xml
+++ b/trytond/trytond/ir/view/email_form.xml
@@ -6,6 +6,10 @@ this repository contains the full copyright notices and license terms. -->
+
+
+
+
diff --git a/trytond/trytond/ir/view/email_list.xml b/trytond/trytond/ir/view/email_list.xml
index d3398818ef..8f4a5fd326 100644
--- a/trytond/trytond/ir/view/email_list.xml
+++ b/trytond/trytond/ir/view/email_list.xml
@@ -3,6 +3,7 @@
this repository contains the full copyright notices and license terms. -->
+
diff --git a/trytond/trytond/sendmail.py b/trytond/trytond/sendmail.py
index e64ee39d58..b0f416db54 100644
--- a/trytond/trytond/sendmail.py
+++ b/trytond/trytond/sendmail.py
@@ -5,11 +5,12 @@ import smtplib
import ssl
import time
from email.message import EmailMessage, Message
-from email.utils import formatdate
+from email.utils import formatdate, make_msgid
from urllib.parse import parse_qs, unquote_plus
from trytond.config import config, parse_uri
from trytond.transaction import Transaction
+from trytond.url import host
__all__ = [
'sendmail_transactional', 'sendmail',
@@ -29,6 +30,8 @@ def sendmail_transactional(
def send_message_transactional(
msg, from_addr=None, to_addrs=None, transaction=None, datamanager=None,
strict=False):
+ if not msg['Message-ID']:
+ msg['Message-ID'] = make_msgid(domain=host())
if transaction is None:
transaction = Transaction()
assert isinstance(transaction, Transaction), transaction
@@ -44,6 +47,8 @@ def sendmail(from_addr, to_addrs, msg, server=None, strict=False):
def send_message(
msg, from_addr=None, to_addrs=None, server=None, strict=False):
+ if not msg['Message-ID']:
+ msg['Message-ID'] = make_msgid(domain=host())
if server is None:
server = get_smtp_server(strict=strict)
if not server:
diff --git a/trytond/trytond/tests/test_ir.py b/trytond/trytond/tests/test_ir.py
index acfe5d62fa..b3a0b83b89 100644
--- a/trytond/trytond/tests/test_ir.py
+++ b/trytond/trytond/tests/test_ir.py
@@ -520,9 +520,13 @@ class IrTestCase(ModuleTestCase):
)
report.save()
+ def set_message_id(msg, *args, **kwargs):
+ msg['Message-ID'] = 'test'
+
with patch(
'trytond.ir.email_.send_message_transactional'
) as send_message:
+ send_message.side_effect = set_message_id
email = Email.send(
to='"John Doe" , Jane ',
cc='User ',
@@ -551,6 +555,7 @@ class IrTestCase(ModuleTestCase):
self.assertEqual(
[a.address for a in email.addresses],
addresses)
+ self.assertEqual(email.message_id, 'test')
self.assertEqual(email.subject, "Email subject")
self.assertEqual(email.body, 'Hello
')
self.assertEqual(len(attachments), 2)
diff --git a/trytond/trytond/tests/test_sendmail.py b/trytond/trytond/tests/test_sendmail.py
index c15ace1587..8a060e425d 100644
--- a/trytond/trytond/tests/test_sendmail.py
+++ b/trytond/trytond/tests/test_sendmail.py
@@ -1,7 +1,7 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import smtplib
-from email.message import Message
+from email.message import EmailMessage, Message
from unittest.mock import ANY, MagicMock, Mock, call, patch
from trytond.sendmail import (
@@ -22,7 +22,7 @@ class SendmailTestCase(TestCase):
@with_transaction()
def test_sendmail_transactional(self):
'Test sendmail_transactional'
- message = MagicMock()
+ message = EmailMessage()
datamanager = Mock()
sendmail_transactional(
'tryton@example.com', 'foo@example.com', message,
@@ -30,16 +30,42 @@ class SendmailTestCase(TestCase):
datamanager.put.assert_called_once_with(
'tryton@example.com', 'foo@example.com', message)
+ self.assertTrue(message['Message-ID'])
+
+ @with_transaction()
+ def test_sendmail_transactional_keeps_message_id(self):
+ "Test sendmail_transactional keeps Message-ID"
+ message = EmailMessage()
+ message['Message-ID'] = 'test'
+
+ datamanager = Mock()
+ sendmail_transactional(
+ 'tryton@example.com', 'foo@example.com', message,
+ datamanager=datamanager)
+
+ self.assertEqual(message['Message-ID'], 'test')
def test_sendmail(self):
'Test sendmail'
- message = MagicMock()
+ message = EmailMessage()
server = Mock()
sendmail(
'tryton@example.com', 'foo@example.com', message, server=server)
server.send_message.assert_called_with(
message, 'tryton@example.com', 'foo@example.com')
server.quit.assert_not_called()
+ self.assertTrue(message['Message-ID'])
+
+ def test_sendmail_keeps_message_id(self):
+ "Test sendmail keeps Message-ID"
+ message = EmailMessage()
+ message['Message-ID'] = 'test'
+
+ server = Mock()
+ sendmail(
+ 'tryton@example.com', 'foo@example.com', message, server=server)
+
+ self.assertEqual(message['Message-ID'], 'test')
def test_get_smtp_server(self):
'Test get_smtp_server'