[FW][MERGE] (test_/mass_)mail: improve composer and mailing tests
Purpose of this merge is to
* merge and clean mail.compose.message related tests currently located
in two files;
* improve those tests and add more detailed checks about generated
mail.mail and sent emails;
* add some docstrings and parameter explanation in mail helpers, tools
and asserts;
* test sent emails in mass mailing and marketing automation tests in
addition to traces and mail.mail;
This merge targets 14 to have a coherent test suite for mail_* features
through 14+ versions.
Task ID-2500615
Prepares Task ID-2484296 (improve markers use in Jinja)
Prepares Task ID-27033 (support QWeb in templates)
Prepares Task ID-2377974 (clean trace and status management in mass mailing)
closes odoo/odoo#69052
Forward-port-of: odoo/odoo#69004
Forward-port-of: odoo/odoo#68874
Related: odoo/enterprise#17593
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
@@ -120,7 +120,8 @@ class TestMailSchedule(TestEventCommon, MockEmail):
|
||||
self.assertEqual(len(self._new_mails), 2, 'event: should have 2 scheduled emails (1 / registration)')
|
||||
self.assertMailMailWEmails(
|
||||
[formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email))],
|
||||
'outgoing', '',
|
||||
'outgoing',
|
||||
content=None,
|
||||
fields_values={'subject': 'Your registration at %s' % test_event.name,
|
||||
'email_from': self.user_eventmanager.company_id.email_formatted,
|
||||
})
|
||||
@@ -157,7 +158,8 @@ class TestMailSchedule(TestEventCommon, MockEmail):
|
||||
self.assertEqual(len(self._new_mails), 2, 'event: should have 2 scheduled emails (1 / registration)')
|
||||
self.assertMailMailWEmails(
|
||||
[formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email))],
|
||||
'outgoing', '',
|
||||
'outgoing',
|
||||
content=None,
|
||||
fields_values={'subject': 'Your registration at %s' % test_event.name,
|
||||
'email_from': self.user_eventmanager.company_id.email_formatted,
|
||||
})
|
||||
@@ -191,7 +193,8 @@ class TestMailSchedule(TestEventCommon, MockEmail):
|
||||
self.assertEqual(len(self._new_mails), 2, 'event: should have scheduled 2 mails (1 / registration)')
|
||||
self.assertMailMailWEmails(
|
||||
[formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email))],
|
||||
'outgoing', '',
|
||||
'outgoing',
|
||||
content=None,
|
||||
fields_values={'subject': '%s: tomorrow' % test_event.name,
|
||||
'email_from': self.user_eventmanager.company_id.email_formatted,
|
||||
})
|
||||
@@ -265,7 +268,8 @@ class TestMailSchedule(TestEventCommon, MockEmail):
|
||||
self.assertEqual(len(self._new_mails), 3, 'event: should have scheduled 3 mails, one for each registration')
|
||||
self.assertMailMailWEmails(
|
||||
[formataddr((reg1.name, reg1.email)), formataddr((reg2.name, reg2.email)), formataddr((reg3.name, reg3.email))],
|
||||
'outgoing', '',
|
||||
'outgoing',
|
||||
content=None,
|
||||
fields_values={'subject': '%s: today' % test_event.name,
|
||||
'email_from': self.user_eventmanager.company_id.email_formatted,
|
||||
})
|
||||
|
||||
@@ -258,6 +258,19 @@ class MailRenderMixin(models.AbstractModel):
|
||||
|
||||
@api.model
|
||||
def _render_template_qweb(self, template_src, model, res_ids, add_context=None):
|
||||
""" Render a QWeb template.
|
||||
|
||||
:param str template_src: source QWeb template. It should be a string
|
||||
XmlID allowing to fetch an ir.ui.view;
|
||||
:param str model: see ``MailRenderMixin._render_field)``;
|
||||
:param list res_ids: see ``MailRenderMixin._render_field)``;
|
||||
|
||||
:param dict add_context: additional context to give to renderer. It
|
||||
allows to add values to base rendering context generated by
|
||||
``MailRenderMixin._render_qweb_eval_context()``;
|
||||
|
||||
:return dict: {res_id: string of rendered template based on record}
|
||||
"""
|
||||
view = self.env.ref(template_src, raise_if_not_found=False) or self.env['ir.ui.view']
|
||||
results = dict.fromkeys(res_ids, u"")
|
||||
if not view:
|
||||
@@ -370,15 +383,14 @@ class MailRenderMixin(models.AbstractModel):
|
||||
@api.model
|
||||
def _render_template(self, template_src, model, res_ids, engine='jinja', add_context=None, post_process=False):
|
||||
""" Render the given string on records designed by model / res_ids using
|
||||
the given rendering engine. Currently only jinja is supported.
|
||||
the given rendering engine. Currently only jinja or qweb are supported.
|
||||
|
||||
:param str template_src: template text to render (jinja) or xml id of view (qweb)
|
||||
this could be cleaned but hey, we are in a rush
|
||||
:param str model: model name of records on which we want to perform rendering
|
||||
:param list res_ids: list of ids of records (all belonging to same model)
|
||||
:param string engine: jinja
|
||||
:param post_process: perform rendered str / html post processing (see
|
||||
``_render_template_postprocess``)
|
||||
:param post_process: see ``MailRenderMixin._render_field``;
|
||||
|
||||
:return dict: {res_id: string of rendered template based on record}
|
||||
"""
|
||||
@@ -437,16 +449,25 @@ class MailRenderMixin(models.AbstractModel):
|
||||
for lang, lang_res_ids in lang_to_res_ids.items()
|
||||
)
|
||||
|
||||
def _render_field(self, field, res_ids, compute_lang=False, set_lang=False, post_process=False):
|
||||
""" Given some record ids, render a given field of template rendered on
|
||||
all records.
|
||||
def _render_field(self, field, res_ids,
|
||||
compute_lang=False, set_lang=False,
|
||||
post_process=False):
|
||||
""" Given some record ids, render a template located on field on all
|
||||
records. ``field`` should be a field of self (i.e. ``body_html`` on
|
||||
``mail.template``). res_ids are record IDs linked to ``model`` field
|
||||
on self.
|
||||
|
||||
:param list res_ids: list of ids of records (all belonging to same model
|
||||
defined by self.model)
|
||||
:param compute_lang: compute rendering language based on template.lang
|
||||
:param set_lang: force language
|
||||
:param post_process: perform rendered str / html post processing (see
|
||||
``_render_template_postprocess``)
|
||||
defined by ``self.model``)
|
||||
|
||||
:param boolean compute_lang: compute language to render on translated
|
||||
version of the template instead of default (probably english) one.
|
||||
Language will be computed based on ``self.lang``;
|
||||
:param string set_lang: force language for rendering. It should be a
|
||||
valid lang code matching an activate res.lang. Checked only if
|
||||
``compute_lang`` is False;
|
||||
:param boolean post_process: perform a post processing on rendered result
|
||||
(notably html links management). See``_render_template_postprocess``);
|
||||
|
||||
:return dict: {res_id: string of rendered template based on record}
|
||||
"""
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
from . import test_mail_channel
|
||||
from . import test_mail_channel_partner
|
||||
from . import test_mail_full_composer
|
||||
from . import test_mail_render
|
||||
from . import test_res_partner
|
||||
from . import test_update_notification
|
||||
|
||||
+162
-42
@@ -8,6 +8,7 @@ import time
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from functools import partial
|
||||
from lxml import html
|
||||
from unittest.mock import patch
|
||||
from smtplib import SMTPServerDisconnected
|
||||
|
||||
@@ -41,8 +42,8 @@ class MockEmail(common.BaseCase):
|
||||
build_email_origin = IrMailServer.build_email
|
||||
mail_create_origin = MailMail.create
|
||||
mail_unlink_origin = MailMail.unlink
|
||||
self._init_mail_mock()
|
||||
self.mail_unlink_sent = mail_unlink_sent
|
||||
self._init_mail_mock()
|
||||
|
||||
def _ir_mail_server_connect(model, *args, **kwargs):
|
||||
if sim_error and sim_error == 'connect_smtp_notfound':
|
||||
@@ -151,8 +152,6 @@ class MockEmail(common.BaseCase):
|
||||
return email.message_from_string(pycompat.to_text(text), policy=email.policy.SMTP)
|
||||
|
||||
def assertHtmlEqual(self, value, expected, message=None):
|
||||
from lxml import html
|
||||
|
||||
tree = html.fragment_fromstring(value, parser=html.HTMLParser(encoding='utf-8'), create_parent='body')
|
||||
|
||||
# mass mailing: add base tag we have to remove
|
||||
@@ -170,19 +169,38 @@ class MockEmail(common.BaseCase):
|
||||
self.assertEqual(tree, expected_node)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# GATEWAY ASSERTS
|
||||
# GATEWAY GETTERS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def _find_sent_mail_wemail(self, email_to):
|
||||
for email in self._mails:
|
||||
if set(email['email_to']) == set([email_to]):
|
||||
""" Find a sent email with a given list of recipients. Email should match
|
||||
exactly the recipients.
|
||||
|
||||
:return email: a dictionary mapping values given to ``build_email``;
|
||||
"""
|
||||
for sent_email in self._mails:
|
||||
if set(sent_email['email_to']) == set([email_to]):
|
||||
break
|
||||
else:
|
||||
raise AssertionError('sent mail not found for email_to %s' % (email_to))
|
||||
return email
|
||||
|
||||
return sent_email
|
||||
|
||||
def _find_mail_mail_wpartners(self, recipients, status, mail_message=None, author=None):
|
||||
""" Find a mail.mail record based on various parameters, notably a list
|
||||
of recipients (partners).
|
||||
|
||||
:param recipients: a ``res.partner`` recordset Check all of them are in mail
|
||||
recipients to find the right mail.mail record;
|
||||
:param status: state of mail.mail. If not void use it to filter mail.mail
|
||||
record;
|
||||
:param mail_message: optional check/filter on mail_message_id field aka
|
||||
a ``mail.message`` record;
|
||||
:param author: optional check/filter on author_id field aka a ``res.partner``
|
||||
record;
|
||||
|
||||
:return mail: a ``mail.mail`` record generated during the mock and matching
|
||||
given parameters and filters;
|
||||
"""
|
||||
for mail in self._new_mails:
|
||||
if author is not None and mail.author_id != author:
|
||||
continue
|
||||
@@ -193,10 +211,25 @@ class MockEmail(common.BaseCase):
|
||||
if all(p in mail.recipient_ids for p in recipients):
|
||||
break
|
||||
else:
|
||||
raise AssertionError('mail.mail not found for message %s / status %s / recipients %s' % (mail_message, status, recipients.ids))
|
||||
raise AssertionError('mail.mail not found for message %s / status %s / recipients %s / author %s' % (mail_message, status, recipients.ids, author))
|
||||
return mail
|
||||
|
||||
def _find_mail_mail_wemail(self, email_to, status, mail_message=None, author=None):
|
||||
""" Find a mail.mail record based on various parameters, notably a list
|
||||
of email to (string emails).
|
||||
|
||||
:param email_to: either matching mail.email_to value, either a mail sent
|
||||
to a single recipient whose email is email_to;
|
||||
:param status: state of mail.mail. If not void use it to filter mail.mail
|
||||
record;
|
||||
:param mail_message: optional check/filter on mail_message_id field aka
|
||||
a ``mail.message`` record;
|
||||
:param author: optional check/filter on author_id field aka a ``res.partner``
|
||||
record;
|
||||
|
||||
:return mail: a ``mail.mail`` record generated during the mock and matching
|
||||
given parameters and filters;
|
||||
"""
|
||||
for mail in self._new_mails:
|
||||
if author is not None and mail.author_id != author:
|
||||
continue
|
||||
@@ -204,13 +237,17 @@ class MockEmail(common.BaseCase):
|
||||
continue
|
||||
if status and mail.state != status:
|
||||
continue
|
||||
if (mail.email_to == email_to and not mail.recipient_ids) or (not mail.email_to and mail.recipient_ids.email) == email_to:
|
||||
if (mail.email_to == email_to and not mail.recipient_ids) or (not mail.email_to and mail.recipient_ids.email == email_to):
|
||||
break
|
||||
else:
|
||||
raise AssertionError('mail.mail not found for email_to %s / status %s in %s' % (email_to, status, repr([m.email_to for m in self._new_mails])))
|
||||
return mail
|
||||
|
||||
def _find_mail_mail_wrecord(self, record):
|
||||
""" Find a mail.mail record based on model / res_id of a record.
|
||||
|
||||
:return mail: a ``mail.mail`` record generated during the mock;
|
||||
"""
|
||||
for mail in self._new_mails:
|
||||
if mail.model == record._name and mail.res_id == record.id:
|
||||
break
|
||||
@@ -218,39 +255,81 @@ class MockEmail(common.BaseCase):
|
||||
raise AssertionError('mail.mail not found for record %s in %s' % (record, repr([m.email_to for m in self._new_mails])))
|
||||
return mail
|
||||
|
||||
def assertMailFailed(self, author, recipients, mail_message):
|
||||
mail = self._find_mail(author, recipients, mail_message)
|
||||
self.assertEqual(mail.state, 'exception')
|
||||
# ------------------------------------------------------------
|
||||
# GATEWAY ASSERTS
|
||||
# ------------------------------------------------------------
|
||||
|
||||
def assertMailMail(self, recipients, status, check_mail_mail=True, mail_message=None, author=None, email_values=None, fields_values=None):
|
||||
if check_mail_mail:
|
||||
mail = self._find_mail_mail_wpartners(recipients, status, mail_message=mail_message, author=author)
|
||||
self.assertTrue(bool(mail))
|
||||
for fname, fvalue in (fields_values or {}).items():
|
||||
self.assertEqual(mail[fname], fvalue)
|
||||
def assertMailMail(self, recipients, status,
|
||||
check_mail_mail=True, mail_message=None, author=None,
|
||||
content=None, fields_values=None, email_values=None):
|
||||
""" Assert mail.mail records are created and maybe sent as emails. Allow
|
||||
asserting their content. Records to check are the one generated when
|
||||
using mock (mail.mail and outgoing emails). This method takes partners
|
||||
as source of record fetch and assert.
|
||||
|
||||
:param recipients: a ``res.partner`` recordset. See ``_find_mail_mail_wpartners``;
|
||||
:param status: mail.mail state used to filter mails. If ``sent`` this method
|
||||
also check that emails have been sent trough gateway;
|
||||
:param mail_message: see ``_find_mail_mail_wpartners``;
|
||||
:param author: see ``_find_mail_mail_wpartners``;;
|
||||
:param content: if given, check it is contained within mail html body;
|
||||
:param fields_values: if given, should be a dictionary of field names /
|
||||
values allowing to check ``mail.mail`` additional values (subject,
|
||||
reply_to, ...);
|
||||
:param email_values: if given, should be a dictionary of keys / values
|
||||
allowing to check sent email additional values (if any).
|
||||
See ``assertSentEmail``;
|
||||
|
||||
:param check_mail_mail: deprecated, use ``assertSentEmail`` if False
|
||||
"""
|
||||
found_mail = self._find_mail_mail_wpartners(recipients, status, mail_message=mail_message, author=author)
|
||||
self.assertTrue(bool(found_mail))
|
||||
if content:
|
||||
self.assertIn(content, found_mail.body_html)
|
||||
for fname, fvalue in (fields_values or {}).items():
|
||||
self.assertEqual(
|
||||
found_mail[fname], fvalue,
|
||||
'Mail: expected %s for %s, got %s' % (fvalue, fname, found_mail[fname]))
|
||||
if status == 'sent':
|
||||
for recipient in recipients:
|
||||
self.assertSentEmail(author, [recipient], **email_values)
|
||||
self.assertSentEmail(email_values['email_from'] if email_values and email_values.get('email_from') else author, [recipient], **(email_values or {}))
|
||||
|
||||
def assertMailMailWEmails(self, emails, status, content, fields_values=None):
|
||||
""" Will check in self._new_mails to find a sent mail.mail. To use with
|
||||
mail gateway mock.
|
||||
def assertMailMailWEmails(self, emails, status,
|
||||
mail_message=None, author=None,
|
||||
content=None, fields_values=None, email_values=None):
|
||||
""" Assert mail.mail records are created and maybe sent as emails. Allow
|
||||
asserting their content. Records to check are the one generated when
|
||||
using mock (mail.mail and outgoing emails). This method takes emails
|
||||
as source of record fetch and assert.
|
||||
|
||||
:param emails: list of emails;
|
||||
:param status: status of mail.mail;
|
||||
:param content: content to check for each email;
|
||||
:param fields_values: specific value to check on the mail.mail record;
|
||||
:param emails: a list of emails. See ``_find_mail_mail_wemail``;
|
||||
:param status: mail.mail state used to filter mails. If ``sent`` this method
|
||||
also check that emails have been sent trough gateway;
|
||||
:param mail_message: see ``_find_mail_mail_wemail``;
|
||||
:param author: see ``_find_mail_mail_wemail``;;
|
||||
:param content: if given, check it is contained within mail html body;
|
||||
:param fields_values: if given, should be a dictionary of field names /
|
||||
values allowing to check ``mail.mail`` additional values (subject,
|
||||
reply_to, ...);
|
||||
:param email_values: if given, should be a dictionary of keys / values
|
||||
allowing to check sent email additional values (if any).
|
||||
See ``assertSentEmail``;
|
||||
|
||||
:param check_mail_mail: deprecated, use ``assertSentEmail`` if False
|
||||
"""
|
||||
for email_to in emails:
|
||||
sent_mail = self._find_mail_mail_wemail(email_to, status)
|
||||
found_mail = self._find_mail_mail_wemail(email_to, status, mail_message=mail_message, author=author)
|
||||
if content:
|
||||
self.assertIn(content, sent_mail.body_html)
|
||||
self.assertIn(content, found_mail.body_html)
|
||||
for fname, fvalue in (fields_values or {}).items():
|
||||
self.assertEqual(
|
||||
sent_mail[fname], fvalue,
|
||||
'Mail: expected %s for %s, got %s' % (fvalue, fname, sent_mail[fname]))
|
||||
found_mail[fname], fvalue,
|
||||
'Mail: expected %s for %s, got %s' % (fvalue, fname, found_mail[fname]))
|
||||
if status == 'sent':
|
||||
for email_to in emails:
|
||||
self.assertSentEmail(email_values['email_from'] if email_values and email_values.get('email_from') else author, [email_to], **(email_values or {}))
|
||||
|
||||
def assertNoMail(self, author, recipients, mail_message=None):
|
||||
def assertNoMail(self, recipients, mail_message=None, author=None):
|
||||
try:
|
||||
self._find_mail_mail_wpartners(recipients, False, mail_message=mail_message, author=author)
|
||||
except AssertionError:
|
||||
@@ -302,6 +381,15 @@ class MockEmail(common.BaseCase):
|
||||
for val in ['reply_to', 'subject', 'references', 'attachments']:
|
||||
if val in expected:
|
||||
self.assertEqual(expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val]))
|
||||
if 'attachments_info' in values:
|
||||
attachments = sent_mail['attachments']
|
||||
for attachment_info in values['attachments_info']:
|
||||
attachment = next(attach for attach in attachments if attach[0] == attachment_info['name'])
|
||||
if attachment_info.get('raw'):
|
||||
self.assertEqual(attachment[1], attachment_info['raw'])
|
||||
if attachment_info.get('type'):
|
||||
self.assertEqual(attachment[2], attachment_info['type'])
|
||||
self.assertEqual(len(values['attachments_info']), len(attachments))
|
||||
for val in ['body']:
|
||||
if val in expected:
|
||||
self.assertHtmlEqual(expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val]))
|
||||
@@ -591,18 +679,27 @@ class MailCase(MockEmail):
|
||||
for recipients in email_groups.values():
|
||||
partners = self.env['res.partner'].sudo().concat(*recipients)
|
||||
if all(p in mail_groups['failure'] for p in partners):
|
||||
self.assertMailMail(partners, 'exception', author=message.author_id, mail_message=message)
|
||||
if not self.mail_unlink_sent:
|
||||
self.assertMailMail(partners, 'exception',
|
||||
author=message.author_id,
|
||||
mail_message=message)
|
||||
else:
|
||||
for recipient in partners:
|
||||
self.assertSentEmail(message.author_id, [recipient])
|
||||
else:
|
||||
check_mail_mail = not self.mail_unlink_sent
|
||||
self.assertMailMail(
|
||||
partners, 'sent',
|
||||
author=message.author_id if message.author_id else message.email_from,
|
||||
mail_message=message,
|
||||
check_mail_mail=check_mail_mail,
|
||||
email_values={'body_content': mbody}
|
||||
)
|
||||
if not self.mail_unlink_sent:
|
||||
self.assertMailMail(
|
||||
partners, 'sent',
|
||||
author=message.author_id if message.author_id else message.email_from,
|
||||
mail_message=message,
|
||||
email_values={'body_content': mbody}
|
||||
)
|
||||
else:
|
||||
for recipient in partners:
|
||||
self.assertSentEmail(message.author_id if message.author_id else message.email_from, [recipient],
|
||||
email_values={'body_content': mbody})
|
||||
if not any(p for recipients in email_groups.values() for p in recipients):
|
||||
self.assertNoMail(message.author_id, partners, message)
|
||||
self.assertNoMail(partners, mail_message=message, author=message.author_id)
|
||||
|
||||
return done_msgs, done_notifs
|
||||
|
||||
@@ -655,6 +752,29 @@ class MailCase(MockEmail):
|
||||
|
||||
return bus_notifs
|
||||
|
||||
def assertNotified(self, message, recipients_info, is_complete=False):
|
||||
""" Lightweight check for notifications (mail.notification).
|
||||
|
||||
:param recipients_info: list notified recipients: [
|
||||
{'partner': res.partner record (may be empty),
|
||||
'type': notification_type to check,
|
||||
'is_read': is_read to check,
|
||||
}, {...}]
|
||||
"""
|
||||
notifications = self._new_notifs.filtered(lambda notif: notif in message.notification_ids)
|
||||
if is_complete:
|
||||
self.assertEqual(len(notifications), len(recipients_info))
|
||||
for rinfo in recipients_info:
|
||||
recipient_notif = next(
|
||||
(notif
|
||||
for notif in notifications
|
||||
if notif.res_partner_id == rinfo['partner']
|
||||
), False
|
||||
)
|
||||
self.assertTrue(recipient_notif)
|
||||
self.assertEqual(recipient_notif.is_read, rinfo['is_read'])
|
||||
self.assertEqual(recipient_notif.notification_type, rinfo['type'])
|
||||
|
||||
def assertTracking(self, message, data):
|
||||
tracking_values = message.sudo().tracking_value_ids
|
||||
for field_name, value_type, old_value, new_value in data:
|
||||
|
||||
@@ -508,8 +508,11 @@ class TestChannelModeration(MailCommon):
|
||||
(msg_c1_admin1 | msg_c1_emplo2).with_user(self.user_employee)._moderate('reject', title='RejectTitle', comment='RejectComment')
|
||||
self.assertEqual(len(self._new_mails), 2)
|
||||
|
||||
self.assertMailMailWEmails([self.partner_admin.email_formatted, self.partner_employee_2.email_formatted], 'outgoing', 'RejectComment',
|
||||
fields_values={'subject': 'RejectTitle', 'author_id': self.partner_employee})
|
||||
self.assertMailMailWEmails(
|
||||
[self.partner_admin.email_formatted, self.partner_employee_2.email_formatted], 'outgoing',
|
||||
content='RejectComment',
|
||||
fields_values={'subject': 'RejectTitle', 'author_id': self.partner_employee}
|
||||
)
|
||||
self.assertTrue(all('RejectComment' in body for body in self._new_mails.mapped('body_html')))
|
||||
self.assertTrue(all('Body' in body for body in self._new_mails.mapped('body_html')))
|
||||
|
||||
@@ -674,7 +677,7 @@ class TestChannelModeration(MailCommon):
|
||||
})
|
||||
|
||||
# admin being banned, should not received any email
|
||||
self.assertNoMail(self.partner_employee, self.partner_admin)
|
||||
self.assertNoMail(self.partner_employee, author=self.partner_admin)
|
||||
|
||||
# only moderators can send guidelines
|
||||
channel.write({
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.addons.mail.tests import common
|
||||
from odoo.tests import tagged, users
|
||||
|
||||
|
||||
@tagged('mail_render')
|
||||
class TestMailRender(common.MailCommon):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestMailRender, cls).setUpClass()
|
||||
|
||||
# activate multi language support
|
||||
cls.env['res.lang']._activate_lang('fr_FR')
|
||||
cls.user_admin.write({'lang': 'en_US'})
|
||||
|
||||
# test records
|
||||
cls.render_object = cls.env['res.partner'].create({
|
||||
'name': 'TestRecord',
|
||||
'lang': 'en_US',
|
||||
})
|
||||
cls.render_object_fr = cls.env['res.partner'].create({
|
||||
'name': 'Element de Test',
|
||||
'lang': 'fr_FR',
|
||||
})
|
||||
|
||||
# some jinja templates
|
||||
cls.base_jinja_bits = [
|
||||
'<p>Hello</p>',
|
||||
'<p>Hello ${object.name}</p>',
|
||||
"""<p>
|
||||
% set english = object.lang == 'en_US'
|
||||
% if english
|
||||
<span>English Speaker</span>
|
||||
% else
|
||||
<span>Other Speaker</span>
|
||||
% endif
|
||||
</p>"""
|
||||
]
|
||||
cls.base_jinja_bits_fr = [
|
||||
'<p>Bonjour</p>',
|
||||
'<p>Bonjour ${object.name}</p>',
|
||||
"""<p>
|
||||
% set english = object.lang == 'en_US'
|
||||
% if english
|
||||
<span>Narrateur Anglais</span>
|
||||
% else
|
||||
<span>Autre Narrateur</span>
|
||||
% endif
|
||||
</p>"""
|
||||
]
|
||||
|
||||
# some qweb templates and their xml ids
|
||||
cls.base_qweb_templates = cls.env['ir.ui.view'].create([
|
||||
{'name': 'TestRender', 'type': 'qweb',
|
||||
'arch': '<p>Hello</p>',
|
||||
},
|
||||
{'name': 'TestRender2', 'type': 'qweb',
|
||||
'arch': '<p>Hello <t t-esc="object.name"/></p>',
|
||||
},
|
||||
{'name': 'TestRender3', 'type': 'qweb',
|
||||
'arch': """<p>
|
||||
<span t-if="object.lang == 'en_US'">English Speaker</span>
|
||||
<span t-else="">Other Speaker</span>
|
||||
</p>""",
|
||||
},
|
||||
])
|
||||
cls.base_qweb_templates_data = cls.env['ir.model.data'].create([
|
||||
{'name': template.name, 'module': 'mail',
|
||||
'model': template._name, 'res_id': template.id,
|
||||
} for template in cls.base_qweb_templates
|
||||
])
|
||||
cls.base_qweb_templates_xmlids = [
|
||||
model_data.complete_name
|
||||
for model_data in cls.base_qweb_templates_data
|
||||
]
|
||||
|
||||
# render result
|
||||
cls.base_rendered = [
|
||||
'<p>Hello</p>',
|
||||
'<p>Hello %s</p>' % cls.render_object.name,
|
||||
"""<p>
|
||||
<span>English Speaker</span>
|
||||
</p>"""
|
||||
]
|
||||
cls.base_rendered_fr = [
|
||||
'<p>Bonjour</p>',
|
||||
'<p>Bonjour %s</p>' % cls.render_object_fr.name,
|
||||
"""<p>
|
||||
<span>Autre Narrateur</span>
|
||||
</p>"""
|
||||
]
|
||||
|
||||
# link to mail template
|
||||
cls.test_template_jinja = cls.env['mail.template'].create({
|
||||
'name': 'Test Template',
|
||||
'subject': cls.base_jinja_bits[0],
|
||||
'body_html': cls.base_jinja_bits[1],
|
||||
'model_id': cls.env['ir.model']._get('res.partner').id,
|
||||
'lang': '${object.lang}'
|
||||
})
|
||||
|
||||
# some translations
|
||||
cls.env['ir.translation'].create({
|
||||
'type': 'model',
|
||||
'name': 'mail.template,subject',
|
||||
'lang': 'fr_FR',
|
||||
'res_id': cls.test_template_jinja.id,
|
||||
'src': cls.test_template_jinja.subject,
|
||||
'value': cls.base_jinja_bits_fr[0],
|
||||
})
|
||||
cls.env['ir.translation'].create({
|
||||
'type': 'model',
|
||||
'name': 'mail.template,body_html',
|
||||
'lang': 'fr_FR',
|
||||
'res_id': cls.test_template_jinja.id,
|
||||
'src': cls.test_template_jinja.body_html,
|
||||
'value': cls.base_jinja_bits_fr[1],
|
||||
})
|
||||
cls.env['ir.model.data'].create({
|
||||
'name': 'test_template_xmlid',
|
||||
'module': 'mail',
|
||||
'model': cls.test_template_jinja._name,
|
||||
'res_id': cls.test_template_jinja.id,
|
||||
})
|
||||
|
||||
@users('employee')
|
||||
def test_render_jinja(self):
|
||||
source = """<p>
|
||||
% set line_statement_variable = 3
|
||||
<span>We have ${line_statement_variable} cookies in stock</span>
|
||||
<span>We have <% set block_variable = 4 %>${block_variable} cookies in stock</span>
|
||||
</p>"""
|
||||
partner = self.env['res.partner'].browse(self.render_object.ids)
|
||||
result = self.env['mail.render.mixin']._render_template(
|
||||
source,
|
||||
partner._name,
|
||||
partner.ids,
|
||||
engine='jinja',
|
||||
)[partner.id]
|
||||
self.assertEqual(result, """<p>
|
||||
<span>We have 3 cookies in stock</span>
|
||||
<span>We have 4 cookies in stock</span>
|
||||
</p>""")
|
||||
|
||||
@users('employee')
|
||||
def test_render_mail_template_jinja(self):
|
||||
template = self.env['mail.template'].browse(self.test_template_jinja.ids)
|
||||
partner = self.env['res.partner'].browse(self.render_object.ids)
|
||||
for fname, expected in zip(['subject', 'body_html'], self.base_rendered):
|
||||
rendered = template._render_field(
|
||||
fname,
|
||||
partner.ids,
|
||||
compute_lang=True
|
||||
)[partner.id]
|
||||
self.assertEqual(rendered, expected)
|
||||
|
||||
partner = self.env['res.partner'].browse(self.render_object_fr.ids)
|
||||
for fname, expected in zip(['subject', 'body_html'], self.base_rendered_fr):
|
||||
rendered = template._render_field(
|
||||
fname,
|
||||
partner.ids,
|
||||
compute_lang=True
|
||||
)[partner.id]
|
||||
self.assertEqual(rendered, expected)
|
||||
|
||||
@users('employee')
|
||||
def test_render_template_jinja(self):
|
||||
partner = self.env['res.partner'].browse(self.render_object.ids)
|
||||
for source, expected in zip(self.base_jinja_bits, self.base_rendered):
|
||||
rendered = self.env['mail.render.mixin']._render_template(
|
||||
source,
|
||||
partner._name,
|
||||
partner.ids,
|
||||
engine='jinja',
|
||||
)[partner.id]
|
||||
self.assertEqual(rendered, expected)
|
||||
|
||||
@users('employee')
|
||||
def test_render_template_qweb(self):
|
||||
partner = self.env['res.partner'].browse(self.render_object.ids)
|
||||
for source, expected in zip(self.base_qweb_templates_xmlids, self.base_rendered):
|
||||
rendered = self.env['mail.render.mixin']._render_template(
|
||||
source,
|
||||
partner._name,
|
||||
partner.ids,
|
||||
engine='qweb',
|
||||
)[partner.id].decode()
|
||||
self.assertEqual(rendered, expected)
|
||||
@@ -7,7 +7,8 @@ from odoo.addons.mail.tests.common import MailCase, MailCommon, mail_new_test_us
|
||||
|
||||
class MassMailCase(MailCase, MockLinkTracker):
|
||||
|
||||
def assertMailTraces(self, recipients_info, mailing, records, check_mail=True):
|
||||
def assertMailTraces(self, recipients_info, mailing, records,
|
||||
check_mail=True, author=None):
|
||||
""" Check content of traces.
|
||||
|
||||
:param recipients_info: list[{
|
||||
@@ -19,6 +20,15 @@ class MassMailCase(MailCase, MockLinkTracker):
|
||||
'failure_type': optional: UPDATE ME
|
||||
}, { ... }]
|
||||
"""
|
||||
# map trace state to email state
|
||||
state_mapping = {
|
||||
'sent': 'sent',
|
||||
'replied': 'sent', # replied imply something has been sent
|
||||
'ignored': 'cancel',
|
||||
'exception': 'exception',
|
||||
'canceled': 'canceled',
|
||||
}
|
||||
|
||||
traces = self.env['mailing.trace'].search([
|
||||
('mass_mailing_id', 'in', mailing.ids),
|
||||
('res_id', 'in', records.ids)
|
||||
@@ -47,22 +57,17 @@ class MassMailCase(MailCase, MockLinkTracker):
|
||||
)
|
||||
|
||||
if check_mail:
|
||||
if author is None:
|
||||
author = self.env.user.partner_id
|
||||
|
||||
fields_values = {'mailing_id': mailing}
|
||||
if 'failure_type' in recipient_info:
|
||||
fields_values['failure_type'] = recipient_info['failure_type']
|
||||
|
||||
if state == 'sent':
|
||||
self.assertMailMailWEmails([email], 'sent', content, fields_values=fields_values)
|
||||
elif state == 'replied': # replied imply something has been sent
|
||||
self.assertMailMailWEmails([email], 'sent', content, fields_values=fields_values)
|
||||
elif state == 'ignored':
|
||||
self.assertMailMailWEmails([email], 'cancel', content, fields_values=fields_values)
|
||||
elif state == 'exception':
|
||||
self.assertMailMailWEmails([email], 'exception', content, fields_values=fields_values)
|
||||
elif state == 'canceled':
|
||||
self.assertMailMailWEmails([email], 'canceled', content, fields_values=fields_values)
|
||||
if partner:
|
||||
self.assertMailMail(partner, state_mapping[state], author=author, content=content, fields_values=fields_values)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
self.assertMailMailWEmails([email], state_mapping[state], author=author, content=content, fields_values=fields_values)
|
||||
|
||||
|
||||
class MassMailCommon(MailCommon, MassMailCase):
|
||||
|
||||
@@ -240,8 +240,8 @@ class TestMassMailFeatures(MassMailCommon, CronMixinCase):
|
||||
mailing._process_mass_mailing_queue()
|
||||
|
||||
self.assertMailTraces(
|
||||
[{'email': 'test1@example.com'},
|
||||
{'email': 'test2@example.com', 'state': 'ignored'}],
|
||||
[{'partner': partner_a},
|
||||
{'partner': partner_b, 'state': 'ignored'}],
|
||||
mailing, partner_a | partner_b, check_mail=True
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
|
||||
from odoo.tests.common import TransactionCase, users
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
from odoo.tests import tagged
|
||||
from odoo.exceptions import AccessError
|
||||
from odoo.tests import tagged
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@tagged('post_install')
|
||||
@@ -26,7 +27,8 @@ class TestSmsTemplateAccessRights(TransactionCase):
|
||||
cls.sms_templates = cls.env['sms.template'].create(vals)
|
||||
|
||||
@users('user_employee')
|
||||
def test_access_rights_user_sms_template(self):
|
||||
@mute_logger('odoo.models.unlink')
|
||||
def test_access_rights_user(self):
|
||||
# Check if a member of group_user can only read on sms.template
|
||||
for sms_template in self.env['sms.template'].browse(self.sms_templates.ids):
|
||||
self.assertTrue(bool(sms_template.name))
|
||||
@@ -42,7 +44,8 @@ class TestSmsTemplateAccessRights(TransactionCase):
|
||||
sms_template.unlink()
|
||||
|
||||
@users('user_system')
|
||||
def test_access_rights_manager_sms_template(self):
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.base.models.ir_model')
|
||||
def test_access_rights_system(self):
|
||||
admin = self.env.ref('base.user_admin')
|
||||
for sms_template in self.env['sms.template'].browse(self.sms_templates.ids):
|
||||
self.assertTrue(bool(sms_template.name))
|
||||
|
||||
@@ -10,7 +10,6 @@ from . import test_mail_mail
|
||||
from . import test_mail_gateway
|
||||
from . import test_mail_template
|
||||
from . import test_mail_thread_internals
|
||||
from . import test_message_composer
|
||||
from . import test_message_management
|
||||
from . import test_message_post
|
||||
from . import test_message_track
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
import base64
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
from odoo.addons.test_mail.models.test_mail_models import MailTestTicket
|
||||
from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests.common import users, Form
|
||||
@@ -18,9 +21,14 @@ class TestMailComposer(TestMailCommon, TestRecipients):
|
||||
super(TestMailComposer, cls).setUpClass()
|
||||
cls._init_mail_gateway()
|
||||
|
||||
# ensure employee can create partners, necessary for templates
|
||||
cls.user_employee.write({
|
||||
'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)],
|
||||
})
|
||||
|
||||
cls.user_employee_2 = mail_new_test_user(
|
||||
cls.env, login='employee2', groups='base.group_user',
|
||||
notification_type='inbox', email='eglantine@example.com',
|
||||
notification_type='email', email='eglantine@example.com',
|
||||
name='Eglantine Employee', signature='--\nEglantine')
|
||||
cls.partner_employee_2 = cls.user_employee_2.partner_id
|
||||
|
||||
@@ -37,6 +45,7 @@ class TestMailComposer(TestMailCommon, TestRecipients):
|
||||
'report_type': 'qweb-pdf',
|
||||
'report_name': 'test_mail.mail_test_ticket_test_template',
|
||||
})
|
||||
cls.test_record_report = cls.test_report._render_qweb_pdf(cls.test_report.ids)
|
||||
|
||||
cls.test_from = '"John Doe" <john@example.com>'
|
||||
|
||||
@@ -54,9 +63,10 @@ class TestMailComposer(TestMailCommon, TestRecipients):
|
||||
'body_html': '<p>TemplateBody ${object.name}</p>',
|
||||
'partner_to': '${object.customer_id.id if object.customer_id else ""}',
|
||||
'email_to': '${(object.email_from if not object.customer_id else "") | safe}',
|
||||
'email_from': '${object.user_id.email_formatted | safe}',
|
||||
'email_from': '${(object.user_id.email_formatted or user.email_formatted) | safe}',
|
||||
'model_id': cls.env['ir.model']._get('mail.test.ticket').id,
|
||||
'mail_server_id': cls.mail_server.id,
|
||||
'auto_delete': True,
|
||||
})
|
||||
|
||||
def _generate_attachments_data(self, count):
|
||||
@@ -102,7 +112,59 @@ class TestComposerForm(TestMailComposer):
|
||||
# self.assertEqual(composer_form.record_name, self.test_record.name, 'MailComposer: comment mode should compute record name')
|
||||
self.assertFalse(composer_form.no_auto_thread)
|
||||
self.assertEqual(composer_form.composition_mode, 'comment')
|
||||
self.assertEqual(composer_form.model, self.test_records._name)
|
||||
self.assertEqual(composer_form.model, self.test_record._name)
|
||||
|
||||
@users('employee')
|
||||
def test_mail_composer_comment_attachments(self):
|
||||
"""Tests that all attachments are added to the composer, static attachments
|
||||
are not duplicated and while reports are re-generated, and that intermediary
|
||||
attachments are dropped."""
|
||||
attachment_data = self._generate_attachments_data(2)
|
||||
template_1 = self.template.copy({
|
||||
'attachment_ids': [(0, 0, a) for a in attachment_data],
|
||||
'report_name': 'TestReport for ${object.name}.html', # test cursor forces html
|
||||
'report_template': self.test_report.id,
|
||||
})
|
||||
template_1_attachments = template_1.attachment_ids
|
||||
self.assertEqual(len(template_1_attachments), 2)
|
||||
template_2 = self.template.copy({
|
||||
'attachment_ids': False,
|
||||
'report_template': self.test_report.id,
|
||||
})
|
||||
|
||||
# begins without attachments
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_record, add_web=True, default_attachment_ids=[])
|
||||
))
|
||||
self.assertEqual(len(composer_form.attachment_ids), 0)
|
||||
|
||||
# change template: 2 static (attachment_ids) and 1 dynamic (report)
|
||||
composer_form.template_id = template_1
|
||||
self.assertEqual(len(composer_form.attachment_ids), 3)
|
||||
report_attachments = [att for att in composer_form.attachment_ids if att not in template_1_attachments]
|
||||
self.assertEqual(len(report_attachments), 1)
|
||||
tpl_attachments = composer_form.attachment_ids[:] - report_attachments[0]
|
||||
self.assertEqual(tpl_attachments, template_1_attachments)
|
||||
|
||||
# change template: 0 static (attachment_ids) and 1 dynamic (report)
|
||||
composer_form.template_id = template_2
|
||||
self.assertEqual(len(composer_form.attachment_ids), 1)
|
||||
report_attachments = [att for att in composer_form.attachment_ids if att not in template_1_attachments]
|
||||
self.assertEqual(len(report_attachments), 1)
|
||||
tpl_attachments = composer_form.attachment_ids[:] - report_attachments[0]
|
||||
self.assertEqual(tpl_attachments, self.env['ir.attachment'])
|
||||
|
||||
# change back to template 1
|
||||
composer_form.template_id = template_1
|
||||
self.assertEqual(len(composer_form.attachment_ids), 3)
|
||||
report_attachments = [att for att in composer_form.attachment_ids if att not in template_1_attachments]
|
||||
self.assertEqual(len(report_attachments), 1)
|
||||
tpl_attachments = composer_form.attachment_ids[:] - report_attachments[0]
|
||||
self.assertEqual(tpl_attachments, template_1_attachments)
|
||||
|
||||
# reset template
|
||||
composer_form.template_id = self.env['mail.template']
|
||||
self.assertEqual(len(composer_form.attachment_ids), 0)
|
||||
|
||||
@users('employee')
|
||||
def test_mail_composer_mass(self):
|
||||
@@ -139,6 +201,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
attachment_data = self._generate_attachments_data(3)
|
||||
self.template.write({
|
||||
'attachment_ids': [(0, 0, a) for a in attachment_data],
|
||||
'report_name': 'TestReport for ${object.name}.html', # test cursor forces html
|
||||
'report_template': self.test_report.id,
|
||||
})
|
||||
attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])])
|
||||
@@ -161,6 +224,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
self.assertIn(attach, composer.attachment_ids)
|
||||
generated = composer.attachment_ids - attachs
|
||||
self.assertEqual(len(generated), 1, 'MailComposer: should have 1 additional attachment for report')
|
||||
self.assertEqual(generated.name, 'TestReport for %s.html' % self.test_record.name)
|
||||
self.assertEqual(generated.res_model, 'mail.compose.message')
|
||||
self.assertEqual(generated.res_id, 0)
|
||||
|
||||
@@ -207,9 +271,9 @@ class TestComposerInternals(TestMailComposer):
|
||||
self.assertEqual(composer.email_from, self.test_from)
|
||||
|
||||
@users('employee')
|
||||
def test_mail_composer_content_comment(self):
|
||||
""" Test content management (subject, body, server) in comment mode.
|
||||
Template update is also tested. """
|
||||
def test_mail_composer_content(self):
|
||||
""" Test content management (subject, body, server) in both comment and
|
||||
mass mailing mode. Template update is also tested. """
|
||||
for composition_mode in ['comment', 'mass_mail']:
|
||||
if composition_mode == 'comment':
|
||||
ctx = self._get_web_context(self.test_record, add_web=False)
|
||||
@@ -314,3 +378,423 @@ class TestComposerInternals(TestMailComposer):
|
||||
self.assertEqual(composer.subject, 'My amazing subject')
|
||||
self.assertEqual(composer.body, '<p>Test Body</p>')
|
||||
self.assertEqual(composer.mail_server_id.id, False)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_parent(self):
|
||||
""" Test specific management in comment mode when having parent_id set:
|
||||
record_name, subject, parent's partners. """
|
||||
parent = self.test_record.message_post(body='Test', partner_ids=(self.partner_1 + self.partner_2).ids)
|
||||
|
||||
composer = self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_record, add_web=False, default_parent_id=parent.id)
|
||||
).create({
|
||||
'body': '<p>Test Body</p>',
|
||||
})
|
||||
|
||||
# creation values taken from parent
|
||||
self.assertEqual(composer.subject, 'Re: %s' % self.test_record.name)
|
||||
self.assertEqual(composer.body, '<p>Test Body</p>')
|
||||
self.assertEqual(composer.partner_ids, self.partner_1 + self.partner_2)
|
||||
|
||||
def test_mail_composer_rights_portal(self):
|
||||
portal_user = self._create_portal_user()
|
||||
|
||||
with patch.object(MailTestTicket, 'check_access_rights', return_value=True):
|
||||
self.env['mail.compose.message'].with_user(portal_user).with_context(
|
||||
self._get_web_context(self.test_record)
|
||||
).create({
|
||||
'subject': 'Subject',
|
||||
'body': '<p>Body text</p>',
|
||||
'partner_ids': []
|
||||
}).send_mail()
|
||||
|
||||
self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text</p>')
|
||||
self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
|
||||
|
||||
self.env['mail.compose.message'].with_user(portal_user).with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_parent_id': self.test_record.message_ids.ids[0],
|
||||
}).create({
|
||||
'subject': 'Subject',
|
||||
'body': '<p>Body text 2</p>'
|
||||
}).send_mail()
|
||||
|
||||
self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text 2</p>')
|
||||
self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
|
||||
|
||||
@users('employee')
|
||||
def test_mail_composer_save_template(self):
|
||||
self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_record, add_web=False)
|
||||
).create({
|
||||
'subject': 'Template Subject',
|
||||
'body': '<p>Template Body</p>',
|
||||
}).save_as_template()
|
||||
|
||||
# Test: email_template subject, body_html, model
|
||||
template = self.env['mail.template'].search([
|
||||
('model', '=', self.test_record._name),
|
||||
('subject', '=', 'Template Subject')
|
||||
], limit=1)
|
||||
self.assertEqual(template.name, "%s: %s" % (self.env['ir.model']._get(self.test_record._name).name, 'Template Subject'))
|
||||
self.assertEqual(template.body_html, '<p>Template Body</p>', 'email_template incorrect body_html')
|
||||
|
||||
|
||||
@tagged('mail_composer')
|
||||
class TestComposerResultsComment(TestMailComposer):
|
||||
""" Test global output of composer used in comment mode. Test notably
|
||||
notification and emails generated during this process. """
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.tests', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_recipients(self):
|
||||
""" Test partner_ids given to composer are given to the final message. """
|
||||
composer = self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_record)
|
||||
).create({
|
||||
'body': '<p>Test Body</p>',
|
||||
'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
|
||||
})
|
||||
composer.send_mail()
|
||||
|
||||
message = self.test_record.message_ids[0]
|
||||
self.assertEqual(message.body, '<p>Test Body</p>')
|
||||
self.assertEqual(message.author_id, self.user_employee.partner_id)
|
||||
self.assertEqual(message.subject, 'Re: %s' % self.test_record.name)
|
||||
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
self.assertEqual(message.partner_ids, self.partner_1 | self.partner_2)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_complete(self):
|
||||
""" Test a posting process using a complex template, holding several
|
||||
additional recipients and attachments.
|
||||
|
||||
This tests notifies: 2 new email_to (+ 1 duplicated), 1 email_cc,
|
||||
test_record followers and partner_admin added in partner_to."""
|
||||
attachment_data = self._generate_attachments_data(2)
|
||||
email_to_1 = 'test.to.1@test.example.com'
|
||||
email_to_2 = 'test.to.2@test.example.com'
|
||||
email_to_3 = 'test.to.1@test.example.com' # duplicate: should not sent twice the email
|
||||
email_cc_1 = 'test.cc.1@test.example.com'
|
||||
self.template.write({
|
||||
'auto_delete': False, # keep sent emails to check content
|
||||
'attachment_ids': [(0, 0, a) for a in attachment_data],
|
||||
'email_to': '%s, %s, %s' % (email_to_1, email_to_2, email_to_3),
|
||||
'email_cc': email_cc_1,
|
||||
'partner_to': '%s, ${object.customer_id.id if object.customer_id else ""}' % self.partner_admin.id,
|
||||
'report_name': 'TestReport for ${object.name}', # test cursor forces html
|
||||
'report_template': self.test_report.id,
|
||||
})
|
||||
attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])])
|
||||
self.assertEqual(len(attachs), 2)
|
||||
|
||||
# ensure initial data
|
||||
self.assertEqual(self.test_record.user_id, self.user_employee_2)
|
||||
self.assertEqual(self.test_record.message_partner_ids, self.partner_employee_2)
|
||||
|
||||
# open a composer and run it in comment mode
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_record, add_web=True,
|
||||
default_template_id=self.template.id)
|
||||
))
|
||||
composer = composer_form.save()
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False), self.mock_mail_app():
|
||||
composer.send_mail()
|
||||
|
||||
# check new partners have been created based on emails given
|
||||
new_partners = self.env['res.partner'].search([
|
||||
('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1])
|
||||
])
|
||||
self.assertEqual(len(new_partners), 3)
|
||||
self.assertEqual(set(new_partners.mapped('email')),
|
||||
set(['test.to.1@test.example.com', 'test.to.2@test.example.com', 'test.cc.1@test.example.com'])
|
||||
)
|
||||
|
||||
# global outgoing: one mail.mail (all customer recipients, then all employee recipients)
|
||||
# and 5 emails, and 1 inbox notification (admin)
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail')
|
||||
self.assertEqual(len(self._mails), 5, 'Should have sent 5 emails, one per recipient')
|
||||
|
||||
# template is sent only to partners (email_to are transformed)
|
||||
message = self.test_record.message_ids[0]
|
||||
self.assertMailMail(self.partner_employee_2, 'sent',
|
||||
mail_message=message,
|
||||
author=self.partner_employee, # author != email_from (template sets only email_from)
|
||||
email_values={
|
||||
'body_content': 'TemplateBody %s' % self.test_record.name,
|
||||
'email_from': self.test_record.user_id.email_formatted, # set by template
|
||||
'subject': 'TemplateSubject %s' % self.test_record.name,
|
||||
'attachments_info': [
|
||||
{'name': '00.txt', 'raw': b'Att00', 'type': 'text/plain'},
|
||||
{'name': '01.txt', 'raw': b'Att01', 'type': 'text/plain'},
|
||||
{'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'},
|
||||
]
|
||||
},
|
||||
fields_values={},
|
||||
)
|
||||
self.assertMailMail(self.test_record.customer_id + new_partners, 'sent',
|
||||
mail_message=message,
|
||||
author=self.partner_employee, # author != email_from (template sets only email_from)
|
||||
email_values={
|
||||
'body_content': 'TemplateBody %s' % self.test_record.name,
|
||||
'email_from': self.test_record.user_id.email_formatted, # set by template
|
||||
'subject': 'TemplateSubject %s' % self.test_record.name,
|
||||
'attachments_info': [
|
||||
{'name': '00.txt', 'raw': b'Att00', 'type': 'text/plain'},
|
||||
{'name': '01.txt', 'raw': b'Att01', 'type': 'text/plain'},
|
||||
{'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'},
|
||||
]
|
||||
},
|
||||
fields_values={},
|
||||
)
|
||||
|
||||
# message is posted and notified admin
|
||||
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
self.assertNotified(message, [{'partner': self.partner_admin, 'is_read': False, 'type': 'inbox'}])
|
||||
# attachments are copied on message and linked to document
|
||||
self.assertEqual(
|
||||
set(message.attachment_ids.mapped('name')),
|
||||
set(['00.txt', '01.txt', 'TestReport for %s.html' % self.test_record.name])
|
||||
)
|
||||
self.assertEqual(set(message.attachment_ids.mapped('res_model')), set([self.test_record._name]))
|
||||
self.assertEqual(set(message.attachment_ids.mapped('res_id')), set(self.test_record.ids))
|
||||
self.assertTrue(all(attach not in message.attachment_ids for attach in attachs), 'Should have copied attachments')
|
||||
|
||||
|
||||
@tagged('mail_composer')
|
||||
class TestComposerResultsMass(TestMailComposer):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestComposerResultsMass, cls).setUpClass()
|
||||
# ensure employee can create partners, necessary for templates
|
||||
cls.user_employee.write({
|
||||
'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)],
|
||||
})
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl(self):
|
||||
self.template.auto_delete = False # keep sent emails to check content
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
default_template_id=self.template.id)
|
||||
))
|
||||
composer = composer_form.save()
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer.send_mail()
|
||||
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
|
||||
|
||||
for record in self.test_records:
|
||||
# message copy is kept
|
||||
message = record.message_ids[0]
|
||||
|
||||
# template is sent directly using customer field, meaning we have recipients
|
||||
self.assertMailMail(record.customer_id, 'sent',
|
||||
mail_message=message,
|
||||
author=self.partner_employee)
|
||||
|
||||
# message content
|
||||
self.assertEqual(message.subject, 'TemplateSubject %s' % record.name)
|
||||
self.assertEqual(message.body, '<p>TemplateBody %s</p>' % record.name)
|
||||
self.assertEqual(message.author_id, self.user_employee.partner_id)
|
||||
# post-related fields are void
|
||||
self.assertEqual(message.subtype_id, self.env['mail.message.subtype'])
|
||||
self.assertEqual(message.partner_ids, self.env['res.partner'])
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_complete(self):
|
||||
""" Test a composer in mass mode with a quite complete template, containing
|
||||
notably email-based recipients and attachments. """
|
||||
attachment_data = self._generate_attachments_data(2)
|
||||
email_to_1 = 'test.to.1@test.example.com'
|
||||
email_to_2 = 'test.to.2@test.example.com'
|
||||
email_to_3 = 'test.to.1@test.example.com' # duplicate: should not sent twice the email
|
||||
email_cc_1 = 'test.cc.1@test.example.com'
|
||||
self.template.write({
|
||||
'auto_delete': False, # keep sent emails to check content
|
||||
'attachment_ids': [(0, 0, a) for a in attachment_data],
|
||||
'email_to': '%s, %s, %s' % (email_to_1, email_to_2, email_to_3),
|
||||
'email_cc': email_cc_1,
|
||||
'partner_to': '%s, ${object.customer_id.id if object.customer_id else ""}' % self.partner_admin.id,
|
||||
'report_name': 'TestReport for ${object.name}', # test cursor forces html
|
||||
'report_template': self.test_report.id,
|
||||
})
|
||||
attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])])
|
||||
self.assertEqual(len(attachs), 2)
|
||||
|
||||
# ensure initial data
|
||||
self.assertEqual(self.test_records.user_id, self.env['res.users'])
|
||||
self.assertEqual(self.test_records.message_partner_ids, self.env['res.partner'])
|
||||
|
||||
# launch composer in mass mode
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
default_template_id=self.template.id)
|
||||
))
|
||||
composer = composer_form.save()
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
composer.send_mail()
|
||||
|
||||
new_partners = self.env['res.partner'].search([
|
||||
('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1])
|
||||
])
|
||||
self.assertEqual(len(new_partners), 3)
|
||||
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 10, 'Should have sent 5 emails per record')
|
||||
|
||||
# hack to use assertEmails: filtering on from/to only is not sufficient to distinguish emails
|
||||
_mails_records = [
|
||||
[mail for mail in self._mails if '%s-%s' % (record.id, record._name) in mail['message_id']]
|
||||
for record in self.test_records
|
||||
]
|
||||
_mails_record2 = [mail for mail in self._mails if '%s-%s' % (self.test_records[1].id, self.test_records._name) in mail['message_id']]
|
||||
|
||||
for record, _mails in zip(self.test_records, _mails_records):
|
||||
# message copy is kept
|
||||
message = record.message_ids[0]
|
||||
|
||||
# template is sent only to partners (email_to are transformed)
|
||||
self._mails = _mails
|
||||
self.assertMailMail(record.customer_id + new_partners + self.partner_admin,
|
||||
'sent',
|
||||
mail_message=message,
|
||||
author=self.partner_employee,
|
||||
email_values={
|
||||
'body_content': 'TemplateBody %s' % record.name,
|
||||
'subject': 'TemplateSubject %s' % record.name,
|
||||
# 'attachments': [
|
||||
# ('00.txt', b'Att00', 'text/plain'),
|
||||
# ('01.txt', b'Att01', 'text/plain'),
|
||||
# ('report.test_mail.mail_test_ticket_test_template.html', b'My second attachment', 'text/plain')
|
||||
# ]
|
||||
}
|
||||
)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_delete(self):
|
||||
self.template.auto_delete = True
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
default_template_id=self.template.id)
|
||||
))
|
||||
composer = composer_form.save()
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer.send_mail()
|
||||
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
|
||||
self.assertEqual(self._new_mails.exists(), self.env['mail.mail'], 'Should have deleted mail.mail records')
|
||||
|
||||
for record in self.test_records:
|
||||
# message copy is kept
|
||||
message = record.message_ids[0]
|
||||
|
||||
# template is sent directly using customer field
|
||||
self.assertSentEmail(self.partner_employee, record.customer_id)
|
||||
|
||||
# message content
|
||||
self.assertEqual(message.subject, 'TemplateSubject %s' % record.name)
|
||||
self.assertEqual(message.body, '<p>TemplateBody %s</p>' % record.name)
|
||||
self.assertEqual(message.author_id, self.user_employee.partner_id)
|
||||
# post-related fields are void
|
||||
self.assertEqual(message.subtype_id, self.env['mail.message.subtype'])
|
||||
self.assertEqual(message.partner_ids, self.env['res.partner'])
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_delete_notif(self):
|
||||
self.template.auto_delete = True
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
default_template_id=self.template.id,
|
||||
default_auto_delete_message=True)
|
||||
))
|
||||
composer = composer_form.save()
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer.send_mail()
|
||||
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
|
||||
self.assertEqual(self._new_mails.exists(), self.env['mail.mail'], 'Should have deleted mail.mail records')
|
||||
|
||||
for record in self.test_records:
|
||||
# message copy is unlinked
|
||||
self.assertEqual(record.message_ids, self.env['mail.message'], 'Should have deleted mail.message records')
|
||||
|
||||
# template is sent directly using customer field
|
||||
self.assertSentEmail(self.partner_employee, record.customer_id)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_recipients(self):
|
||||
""" Test various combinations of recipients: active_domain, active_id,
|
||||
active_ids, ... to ensure fallback behavior are working. """
|
||||
# 1: active_domain
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
default_template_id=self.template.id,
|
||||
active_ids=[],
|
||||
default_use_active_domain=True,
|
||||
default_active_domain=[('id', 'in', self.test_records.ids)])
|
||||
))
|
||||
composer = composer_form.save()
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer.send_mail()
|
||||
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
|
||||
|
||||
for record in self.test_records:
|
||||
# template is sent directly using customer field
|
||||
self.assertSentEmail(self.partner_employee, record.customer_id)
|
||||
|
||||
# 2: active_domain not taken into account if use_active_domain is False
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
default_template_id=self.template.id,
|
||||
default_use_active_domain=False,
|
||||
default_active_domain=[('id', 'in', -1)])
|
||||
))
|
||||
composer = composer_form.save()
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer.send_mail()
|
||||
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
|
||||
|
||||
# 3: fallback on active_id if not active_ids
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
default_template_id=self.template.id,
|
||||
active_ids=[])
|
||||
))
|
||||
composer = composer_form.save()
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
composer.send_mail()
|
||||
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 1, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 1, 'Should have sent 1 email per record')
|
||||
|
||||
# 3: void is void
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
default_model='mail.test.ticket',
|
||||
default_template_id=self.template.id
|
||||
))
|
||||
composer = composer_form.save()
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False), self.assertRaises(ValueError):
|
||||
composer.send_mail()
|
||||
|
||||
@@ -1,345 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base64
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients
|
||||
from odoo.addons.test_mail.models.test_mail_models import MailTestSimple
|
||||
from odoo.tests import tagged
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
|
||||
@tagged('mail_composer')
|
||||
class TestComposer(TestMailCommon, TestRecipients):
|
||||
""" Test Composer integration and results """
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestComposer, cls).setUpClass()
|
||||
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
|
||||
cls._reset_mail_context(cls.test_record)
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_composer_comment(self):
|
||||
composer = self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': self.test_record._name,
|
||||
'default_res_id': self.test_record.id,
|
||||
}).with_user(self.user_employee).create({
|
||||
'body': '<p>Test Body</p>',
|
||||
'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
|
||||
})
|
||||
composer.send_mail()
|
||||
|
||||
message = self.test_record.message_ids[0]
|
||||
self.assertEqual(message.body, '<p>Test Body</p>')
|
||||
self.assertEqual(message.author_id, self.user_employee.partner_id)
|
||||
self.assertEqual(message.subject, 'Re: %s' % self.test_record.name)
|
||||
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
self.assertEqual(message.partner_ids, self.partner_1 | self.partner_2)
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_composer_comment_parent(self):
|
||||
parent = self.test_record.message_post(body='Test')
|
||||
|
||||
self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_parent_id': parent.id
|
||||
}).with_user(self.user_employee).create({
|
||||
'body': '<p>Mega</p>',
|
||||
}).send_mail()
|
||||
|
||||
message = self.test_record.message_ids[0]
|
||||
self.assertEqual(message.body, '<p>Mega</p>')
|
||||
self.assertEqual(message.parent_id, parent)
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_composer_mass_mail(self):
|
||||
test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2'})
|
||||
|
||||
composer = self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_model': self.test_record._name,
|
||||
'default_res_id': False,
|
||||
'active_ids': [self.test_record.id, test_record_2.id]
|
||||
}).with_user(self.user_employee).create({
|
||||
'subject': 'Testing ${object.name}',
|
||||
'body': '<p>${object.name}</p>',
|
||||
'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
|
||||
})
|
||||
composer.with_context({
|
||||
'default_res_id': -1,
|
||||
'active_ids': [self.test_record.id, test_record_2.id]
|
||||
}).send_mail()
|
||||
|
||||
# check mail_mail
|
||||
mails = self.env['mail.mail'].sudo().search([('subject', 'ilike', 'Testing')])
|
||||
for mail in mails:
|
||||
self.assertEqual(mail.recipient_ids, self.partner_1 | self.partner_2,
|
||||
'compose wizard: mail_mail mass mailing: mail.mail in mass mail incorrect recipients')
|
||||
|
||||
# check message on test_record
|
||||
message1 = self.test_record.message_ids[0]
|
||||
self.assertEqual(message1.subject, 'Testing %s' % self.test_record.name)
|
||||
self.assertEqual(message1.body, '<p>%s</p>' % self.test_record.name)
|
||||
|
||||
# check message on test_record_2
|
||||
message1 = test_record_2.message_ids[0]
|
||||
self.assertEqual(message1.subject, 'Testing %s' % test_record_2.name)
|
||||
self.assertEqual(message1.body, '<p>%s</p>' % test_record_2.name)
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_composer_mass_mail_active_domain(self):
|
||||
test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2'})
|
||||
|
||||
self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_model': self.test_record._name,
|
||||
'default_use_active_domain': True,
|
||||
'active_ids': [self.test_record.id],
|
||||
'active_domain': [('name', 'in', ['%s' % self.test_record.name, '%s' % test_record_2.name])],
|
||||
}).with_user(self.user_employee).create({
|
||||
'subject': 'From Composer Test',
|
||||
'body': '${object.name}',
|
||||
}).send_mail()
|
||||
|
||||
self.assertEqual(self.test_record.message_ids[0].subject, 'From Composer Test')
|
||||
self.assertEqual(test_record_2.message_ids[0].subject, 'From Composer Test')
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_message_compose_mass_mail_no_active_domain(self):
|
||||
test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2'})
|
||||
|
||||
self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_model': self.test_record._name,
|
||||
'default_use_active_domain': False,
|
||||
'active_ids': [self.test_record.id],
|
||||
'active_domain': [('name', 'in', ['%s' % self.test_record.name, '%s' % test_record_2.name])],
|
||||
}).with_user(self.user_employee).create({
|
||||
'subject': 'From Composer Test',
|
||||
'body': '${object.name}',
|
||||
}).send_mail()
|
||||
|
||||
self.assertEqual(self.test_record.message_ids[0].subject, 'From Composer Test')
|
||||
self.assertFalse(test_record_2.message_ids.ids)
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_message_compose_portal_ok(self):
|
||||
portal_user = self._create_portal_user()
|
||||
|
||||
with patch.object(MailTestSimple, 'check_access_rights', return_value=True):
|
||||
ComposerPortal = self.env['mail.compose.message'].with_user(portal_user)
|
||||
|
||||
ComposerPortal.with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': self.test_record._name,
|
||||
'default_res_id': self.test_record.id,
|
||||
}).create({
|
||||
'subject': 'Subject',
|
||||
'body': '<p>Body text</p>',
|
||||
'partner_ids': []}).send_mail()
|
||||
|
||||
self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text</p>')
|
||||
self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
|
||||
|
||||
ComposerPortal.with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_parent_id': self.test_record.message_ids.ids[0],
|
||||
}).create({
|
||||
'subject': 'Subject',
|
||||
'body': '<p>Body text 2</p>'}).send_mail()
|
||||
|
||||
self.assertEqual(self.test_record.message_ids[0].body, '<p>Body text 2</p>')
|
||||
self.assertEqual(self.test_record.message_ids[0].author_id, portal_user.partner_id)
|
||||
|
||||
|
||||
@tagged('mail_composer')
|
||||
class TestComposerWTpl(TestMailCommon, TestRecipients):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestComposerWTpl, cls).setUpClass()
|
||||
cls.test_record = cls.env['mail.test.simple'].with_context(cls._test_context).create({'name': 'Test', 'email_from': 'ignasse@example.com'})
|
||||
|
||||
cls.user_employee.write({
|
||||
'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)],
|
||||
})
|
||||
|
||||
cls._attachments = [{
|
||||
'name': 'first.txt',
|
||||
'datas': base64.b64encode(b'My first attachment'),
|
||||
'res_model': 'res.partner',
|
||||
'res_id': cls.user_admin.partner_id.id
|
||||
}, {
|
||||
'name': 'second.txt',
|
||||
'datas': base64.b64encode(b'My second attachment'),
|
||||
'res_model': 'res.partner',
|
||||
'res_id': cls.user_admin.partner_id.id
|
||||
}]
|
||||
|
||||
cls.email_1 = 'test1@example.com'
|
||||
cls.email_2 = 'test2@example.com'
|
||||
cls.email_3 = cls.partner_1.email
|
||||
cls._create_template('mail.test.simple', {
|
||||
'attachment_ids': [(0, 0, cls._attachments[0]), (0, 0, cls._attachments[1])],
|
||||
'partner_to': '%s,%s' % (cls.partner_2.id, cls.user_admin.partner_id.id),
|
||||
'email_to': '%s, %s' % (cls.email_1, cls.email_2),
|
||||
'email_cc': '%s' % cls.email_3,
|
||||
})
|
||||
|
||||
# admin should receive emails
|
||||
cls.user_admin.write({'notification_type': 'email'})
|
||||
# Force the attachments of the template to be in the natural order.
|
||||
cls.email_template.invalidate_cache(['attachment_ids'], ids=cls.email_template.ids)
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_composer_w_template(self):
|
||||
composer = self.env['mail.compose.message'].with_user(self.user_employee).with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': 'mail.test.simple',
|
||||
'default_res_id': self.test_record.id,
|
||||
'default_template_id': self.email_template.id,
|
||||
}).create({'subject': 'Forget me subject', 'body': 'Dummy body'})
|
||||
|
||||
# perform onchange and send emails
|
||||
values = composer.onchange_template_id(self.email_template.id, 'comment', self.test_record._name, self.test_record.id)['value']
|
||||
composer.write(values)
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer.send_mail()
|
||||
|
||||
new_partners = self.env['res.partner'].search([('email', 'in', [self.email_1, self.email_2])])
|
||||
self.assertMailMail(
|
||||
[self.partner_1, self.partner_2, new_partners[0], new_partners[1], self.partner_admin],
|
||||
'sent',
|
||||
author=self.user_employee.partner_id,
|
||||
mail_message=False,
|
||||
check_mail_mail=False,
|
||||
email_values={
|
||||
'subject': 'About %s' % self.test_record.name,
|
||||
'body_content': self.test_record.name,
|
||||
'attachments': [
|
||||
('first.txt', b'My first attachment', 'text/plain'),
|
||||
('second.txt', b'My second attachment', 'text/plain')
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
def test_composer_template_onchange_attachments(self):
|
||||
"""Tests that all attachments are added to the composer,
|
||||
static attachments are not duplicated and while reports are re-generated,
|
||||
and that intermediary attachments are dropped."""
|
||||
|
||||
composer = self.env['mail.compose.message'].with_context(default_attachment_ids=[]).create({})
|
||||
report_template = self.env.ref('web.action_report_externalpreview')
|
||||
template_1 = self.email_template.copy({
|
||||
'report_template': report_template.id,
|
||||
})
|
||||
template_2 = self.email_template.copy({
|
||||
'attachment_ids': False,
|
||||
'report_template': report_template.id,
|
||||
})
|
||||
|
||||
onchange_templates = [template_1, template_2, template_1, False]
|
||||
attachments_onchange = [composer.attachment_ids]
|
||||
# template_1 has two static attachments and one dynamically generated report,
|
||||
# template_2 only has the report, so we should get 3, 1, 3 attachments
|
||||
attachment_numbers = [0, 3, 1, 3, 0]
|
||||
|
||||
for template in onchange_templates:
|
||||
onchange = composer.onchange_template_id(
|
||||
template.id if template else False, 'comment', self.test_record._name, self.test_record.id
|
||||
)
|
||||
composer.update(onchange['value'])
|
||||
attachments_onchange.append(composer.attachment_ids)
|
||||
|
||||
self.assertEqual(
|
||||
[len(attachments) for attachments in attachments_onchange],
|
||||
attachment_numbers,
|
||||
)
|
||||
|
||||
self.assertTrue(
|
||||
len(attachments_onchange[1] & attachments_onchange[3]) == 2,
|
||||
"The two static attachments on the template should be common to the two onchanges"
|
||||
)
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_composer_w_template_mass_mailing(self):
|
||||
test_record_2 = self.env['mail.test.simple'].with_context(self._test_context).create({'name': 'Test2', 'email_from': 'laurie.poiret@example.com'})
|
||||
|
||||
composer = self.env['mail.compose.message'].with_user(self.user_employee).with_context({
|
||||
'default_composition_mode': 'mass_mail',
|
||||
# 'default_notify': True,
|
||||
'default_notify': False,
|
||||
'default_model': 'mail.test.simple',
|
||||
'default_res_id': self.test_record.id,
|
||||
'default_template_id': self.email_template.id,
|
||||
'active_ids': [self.test_record.id, test_record_2.id]
|
||||
}).create({})
|
||||
values = composer.onchange_template_id(self.email_template.id, 'mass_mail', 'mail.test.simple', self.test_record.id)['value']
|
||||
composer.write(values)
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer.send_mail()
|
||||
|
||||
new_partners = self.env['res.partner'].search([('email', 'in', [self.email_1, self.email_2])])
|
||||
# hack to use assertEmails
|
||||
self._mails_record1 = [dict(mail) for mail in self._mails if '%s-%s' % (self.test_record.id, self.test_record._name) in mail['message_id']]
|
||||
self._mails_record2 = [dict(mail) for mail in self._mails if '%s-%s' % (test_record_2.id, test_record_2._name) in mail['message_id']]
|
||||
|
||||
self._mails = self._mails_record1
|
||||
self.assertMailMail(
|
||||
[self.partner_1, self.partner_2, new_partners[0], new_partners[1], self.partner_admin],
|
||||
'sent',
|
||||
author=self.user_employee.partner_id,
|
||||
mail_message=False,
|
||||
check_mail_mail=False,
|
||||
email_values={
|
||||
'subject': 'About %s' % self.test_record.name,
|
||||
'body_content': self.test_record.name,
|
||||
'attachments': [
|
||||
('first.txt', b'My first attachment', 'text/plain'),
|
||||
('second.txt', b'My second attachment', 'text/plain')
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
self._mails = self._mails_record2
|
||||
self.assertMailMail(
|
||||
[self.partner_1, self.partner_2, new_partners[0], new_partners[1], self.partner_admin],
|
||||
'sent',
|
||||
author=self.user_employee.partner_id,
|
||||
mail_message=False,
|
||||
check_mail_mail=False,
|
||||
email_values={
|
||||
'subject': 'About %s' % test_record_2.name,
|
||||
'body_content': test_record_2.name,
|
||||
'attachments': [
|
||||
('first.txt', b'My first attachment', 'text/plain'),
|
||||
('second.txt', b'My second attachment', 'text/plain')
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
message_1 = self.test_record.message_ids[0]
|
||||
message_2 = test_record_2.message_ids[0]
|
||||
|
||||
# messages effectively posted
|
||||
self.assertEqual(message_1.subject, 'About %s' % self.test_record.name)
|
||||
self.assertEqual(message_2.subject, 'About %s' % test_record_2.name)
|
||||
self.assertIn(self.test_record.name, message_1.body)
|
||||
self.assertIn(test_record_2.name, message_2.body)
|
||||
|
||||
def test_composer_template_save(self):
|
||||
self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': 'mail.test.simple',
|
||||
'default_res_id': self.test_record.id,
|
||||
}).create({
|
||||
'subject': 'Forget me subject',
|
||||
'body': '<p>Dummy body</p>'
|
||||
}).save_as_template()
|
||||
# Test: email_template subject, body_html, model
|
||||
last_template = self.env['mail.template'].search([('model', '=', 'mail.test.simple'), ('subject', '=', 'Forget me subject')], limit=1)
|
||||
self.assertEqual(last_template.body_html, '<p>Dummy body</p>', 'email_template incorrect body_html')
|
||||
@@ -113,10 +113,12 @@ class TestMessagePost(TestMailCommon, TestRecipients):
|
||||
# subscribe second employee to the group to test notifications
|
||||
self.test_record.message_subscribe(partner_ids=[self.user_admin.partner_id.id])
|
||||
|
||||
with self.assertSinglePostNotifications([
|
||||
{'partner': self.partner_1, 'type': 'email'},
|
||||
{'partner': self.partner_2, 'type': 'email'},
|
||||
{'partner': self.partner_admin, 'type': 'email'}], {'content': _body}, mail_unlink_sent=True):
|
||||
with self.assertSinglePostNotifications(
|
||||
[{'partner': self.partner_1, 'type': 'email'},
|
||||
{'partner': self.partner_2, 'type': 'email'},
|
||||
{'partner': self.partner_admin, 'type': 'email'}],
|
||||
{'content': _body},
|
||||
mail_unlink_sent=True):
|
||||
msg = self.test_record.with_user(self.user_employee).message_post(
|
||||
body=_body, subject=_subject,
|
||||
message_type='comment', subtype_xmlid='mail.mt_comment',
|
||||
|
||||
@@ -75,6 +75,6 @@ class TestMassMailing(TestMailFullCommon):
|
||||
'http://localhost:%s/mailing/%s/view' % (config['http_port'], mailing.id),
|
||||
email['body'])
|
||||
|
||||
self.assertMailTraces([recipient_info], mailing, recipient, check_mail=True)
|
||||
self.assertMailTraces([recipient_info], mailing, recipient, check_mail=True, author=self.env.user.partner_id)
|
||||
|
||||
self.assertEqual(mailing.ignored, 4)
|
||||
|
||||
@@ -14,7 +14,9 @@ class TestMassMailCommon(MassMailCommon, TestMailCommon):
|
||||
def setUpClass(cls):
|
||||
super(TestMassMailCommon, cls).setUpClass()
|
||||
|
||||
cls.mailing_bl = cls.env['mailing.mailing'].create({
|
||||
# enforce last update by user_marketing to match _process_mass_mailing_queue
|
||||
# taking last writer as user running a batch
|
||||
cls.mailing_bl = cls.env['mailing.mailing'].with_user(cls.user_marketing).create({
|
||||
'name': 'SourceName',
|
||||
'subject': 'MailingSubject',
|
||||
'body_html': '<p>Hello ${object.name}</p>',
|
||||
|
||||
@@ -35,7 +35,8 @@ class TestAutoBlacklist(common.TestMassMailCommon):
|
||||
|
||||
self.assertMailTraces(
|
||||
[{'email': 'test.record.00@test.example.com'}],
|
||||
mailing, target, check_mail=True
|
||||
mailing, target,
|
||||
check_mail=True
|
||||
)
|
||||
|
||||
# call bounced
|
||||
|
||||
Reference in New Issue
Block a user