summaryrefslogtreecommitdiff
path: root/addons/test_mail/tests/test_mail_composer.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/test_mail/tests/test_mail_composer.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/test_mail/tests/test_mail_composer.py')
-rw-r--r--addons/test_mail/tests/test_mail_composer.py842
1 files changed, 842 insertions, 0 deletions
diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py
new file mode 100644
index 00000000..6c60ea3d
--- /dev/null
+++ b/addons/test_mail/tests/test_mail_composer.py
@@ -0,0 +1,842 @@
+# -*- 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.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
+from odoo.tools import mute_logger
+
+@tagged('mail_composer')
+class TestMailComposer(TestMailCommon, TestRecipients):
+ """ Test Composer internals """
+
+ @classmethod
+ def setUpClass(cls):
+ 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='email', email='eglantine@example.com',
+ name='Eglantine Employee', signature='--\nEglantine')
+ cls.partner_employee_2 = cls.user_employee_2.partner_id
+
+ cls.test_record = cls.env['mail.test.ticket'].with_context(cls._test_context).create({
+ 'name': 'TestRecord',
+ 'customer_id': cls.partner_1.id,
+ 'user_id': cls.user_employee_2.id,
+ })
+ cls.test_records, cls.test_partners = cls._create_records_for_batch('mail.test.ticket', 2)
+
+ cls.test_report = cls.env['ir.actions.report'].create({
+ 'name': 'Test Report on mail test ticket',
+ 'model': 'mail.test.ticket',
+ '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>'
+
+ cls.mail_server = cls.env['ir.mail_server'].create({
+ 'name': 'Dummy Test Server',
+ 'smtp_host': 'smtp.pizza.moc',
+ 'smtp_port': 17,
+ 'smtp_encryption': 'ssl',
+ 'sequence': 666,
+ })
+
+ cls.template = cls.env['mail.template'].create({
+ 'name': 'TestTemplate',
+ 'subject': 'TemplateSubject ${object.name}',
+ '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 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):
+ return [{
+ 'name': '%02d.txt' % x,
+ 'datas': base64.b64encode(b'Att%02d' % x),
+ } for x in range(count)]
+
+ def _get_web_context(self, records, add_web=True, **values):
+ """ Helper to generate composer context. Will make tests a bit less
+ verbose.
+
+ :param add_web: add web context, generally making noise especially in
+ mass mail mode (active_id/ids both present in context)
+ """
+ base_context = {
+ 'default_model': records._name,
+ }
+ if len(records) == 1:
+ base_context['default_composition_mode'] = 'comment'
+ base_context['default_res_id'] = records.id
+ else:
+ base_context['default_composition_mode'] = 'mass_mail'
+ base_context['active_ids'] = records.ids
+ if add_web:
+ base_context['active_model'] = records._name
+ base_context['active_id'] = records[0].id
+ if values:
+ base_context.update(**values)
+ return base_context
+
+
+@tagged('mail_composer')
+class TestComposerForm(TestMailComposer):
+
+ @users('employee')
+ def test_mail_composer_comment(self):
+ composer_form = Form(self.env['mail.compose.message'].with_context(self._get_web_context(self.test_record, add_web=True)))
+ self.assertEqual(
+ composer_form.subject, 'Re: %s' % self.test_record.name,
+ 'MailComposer: comment mode should have default subject Re: record_name')
+ # record name not displayed currently in view
+ # 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_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):
+ composer_form = Form(self.env['mail.compose.message'].with_context(self._get_web_context(self.test_records, add_web=True)))
+ self.assertFalse(composer_form.subject, 'MailComposer: mass mode should have void default subject if no template')
+ # record name not displayed currently in view
+ # self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
+ self.assertFalse(composer_form.no_auto_thread)
+ self.assertEqual(composer_form.composition_mode, 'mass_mail')
+ self.assertEqual(composer_form.model, self.test_records._name)
+
+ @users('employee')
+ def test_mail_composer_mass_wtpl(self):
+ ctx = self._get_web_context(self.test_records, add_web=True, default_template_id=self.template.id)
+ composer_form = Form(self.env['mail.compose.message'].with_context(ctx))
+ self.assertEqual(composer_form.subject, self.template.subject,
+ 'MailComposer: mass mode should have template raw subject if template')
+ self.assertEqual(composer_form.body, self.template.body_html,
+ 'MailComposer: mass mode should have template raw body if template')
+ # record name not displayed currently in view
+ # self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
+ self.assertFalse(composer_form.no_auto_thread)
+ self.assertEqual(composer_form.composition_mode, 'mass_mail')
+ self.assertEqual(composer_form.model, self.test_records._name)
+
+
+@tagged('mail_composer')
+class TestComposerInternals(TestMailComposer):
+
+ @users('employee')
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_attachments_comment(self):
+ """ Test attachments management in comment mode. """
+ 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])])
+ self.assertEqual(len(attachs), 3)
+
+ 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,
+ 'default_template_id': self.template.id,
+ }).create({
+ 'body': '<p>Test Body</p>',
+ })
+ # currently onchange necessary
+ composer.onchange_template_id_wrapper()
+
+ # values coming from template
+ self.assertEqual(len(composer.attachment_ids), 4)
+ for attach in attachs:
+ 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)
+
+ @users('employee')
+ @mute_logger('odoo.addons.mail.models.mail_mail')
+ def test_mail_composer_author(self):
+ """ Test author_id / email_from synchronization, in both comment and mass mail
+ modes. """
+ for composition_mode in ['comment', 'mass_mail']:
+ if composition_mode == 'comment':
+ ctx = self._get_web_context(self.test_record, add_web=False)
+ else:
+ ctx = self._get_web_context(self.test_records, add_web=False)
+ composer = self.env['mail.compose.message'].with_context(ctx).create({
+ 'body': '<p>Test Body</p>',
+ })
+
+ # default values are current user
+ self.assertEqual(composer.author_id, self.env.user.partner_id)
+ self.assertEqual(composer.email_from, self.env.user.email_formatted)
+
+ # author values reset email (FIXME: currently not synchronized)
+ composer.write({'author_id': self.partner_1})
+ self.assertEqual(composer.author_id, self.partner_1)
+ self.assertEqual(composer.email_from, self.env.user.email_formatted)
+ # self.assertEqual(composer.email_from, self.partner_1.email_formatted)
+
+ # changing template should update its email_from
+ composer.write({'template_id': self.template.id, 'author_id': self.env.user.partner_id})
+ # currently onchange necessary
+ composer.onchange_template_id_wrapper()
+ self.assertEqual(composer.author_id, self.env.user.partner_id,
+ 'MailComposer: should take value given by user')
+ if composition_mode == 'comment':
+ self.assertEqual(composer.email_from, self.test_record.user_id.email_formatted,
+ 'MailComposer: should take email_from rendered from template')
+ else:
+ self.assertEqual(composer.email_from, self.template.email_from,
+ 'MailComposer: should take email_from raw from template')
+
+ # manual values are kept over template values
+ composer.write({'email_from': self.test_from})
+ self.assertEqual(composer.author_id, self.env.user.partner_id)
+ self.assertEqual(composer.email_from, self.test_from)
+
+ @users('employee')
+ 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)
+ else:
+ ctx = self._get_web_context(self.test_records, add_web=False)
+
+ # 1. check without template + template update
+ composer = self.env['mail.compose.message'].with_context(ctx).create({
+ 'subject': 'My amazing subject',
+ 'body': '<p>Test Body</p>',
+ })
+
+ # creation values are taken
+ self.assertEqual(composer.subject, 'My amazing subject')
+ self.assertEqual(composer.body, '<p>Test Body</p>')
+ self.assertEqual(composer.mail_server_id.id, False)
+
+ # changing template should update its content
+ composer.write({'template_id': self.template.id})
+ # currently onchange necessary
+ composer.onchange_template_id_wrapper()
+
+ # values come from template
+ if composition_mode == 'comment':
+ self.assertEqual(composer.subject, 'TemplateSubject %s' % self.test_record.name)
+ self.assertEqual(composer.body, '<p>TemplateBody %s</p>' % self.test_record.name)
+ self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
+ else:
+ self.assertEqual(composer.subject, self.template.subject)
+ self.assertEqual(composer.body, self.template.body_html)
+ self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
+
+ # manual values is kept over template
+ composer.write({'subject': 'Back to my amazing subject'})
+ self.assertEqual(composer.subject, 'Back to my amazing subject')
+
+ # reset template should reset values
+ composer.write({'template_id': False})
+ # currently onchange necessary
+ composer.onchange_template_id_wrapper()
+
+ # values are reset
+ if composition_mode == 'comment':
+ self.assertEqual(composer.subject, 'Re: %s' % self.test_record.name)
+ self.assertEqual(composer.body, '')
+ # TDE FIXME: server id is kept, not sure why
+ # self.assertFalse(composer.mail_server_id.id)
+ self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
+ else:
+ # values are reset TDE FIXME: strange for subject
+ self.assertEqual(composer.subject, 'Back to my amazing subject')
+ self.assertEqual(composer.body, '')
+ # TDE FIXME: server id is kept, not sure why
+ # self.assertFalse(composer.mail_server_id.id)
+ self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
+
+ # 2. check with default
+ ctx['default_template_id'] = self.template.id
+ composer = self.env['mail.compose.message'].with_context(ctx).create({
+ 'template_id': self.template.id,
+ })
+ # currently onchange necessary
+ composer.onchange_template_id_wrapper()
+
+ # values come from template
+ if composition_mode == 'comment':
+ self.assertEqual(composer.subject, 'TemplateSubject %s' % self.test_record.name)
+ self.assertEqual(composer.body, '<p>TemplateBody %s</p>' % self.test_record.name)
+ self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
+ else:
+ self.assertEqual(composer.subject, self.template.subject)
+ self.assertEqual(composer.body, self.template.body_html)
+ self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
+
+ # 3. check at create
+ ctx.pop('default_template_id')
+ composer = self.env['mail.compose.message'].with_context(ctx).create({
+ 'template_id': self.template.id,
+ })
+ # currently onchange necessary
+ composer.onchange_template_id_wrapper()
+
+ # values come from template
+ if composition_mode == 'comment':
+ self.assertEqual(composer.subject, 'TemplateSubject %s' % self.test_record.name)
+ self.assertEqual(composer.body, '<p>TemplateBody %s</p>' % self.test_record.name)
+ self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
+ else:
+ self.assertEqual(composer.subject, self.template.subject)
+ self.assertEqual(composer.body, self.template.body_html)
+ self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
+
+ # 4. template + user input
+ ctx['default_template_id'] = self.template.id
+ composer = self.env['mail.compose.message'].with_context(ctx).create({
+ 'subject': 'My amazing subject',
+ 'body': '<p>Test Body</p>',
+ 'mail_server_id': False,
+ })
+
+ # creation values are taken
+ 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_notifications_delete(self):
+ """ Notifications are correctly deleted once sent """
+ 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)]
+ })
+ with self.mock_mail_gateway(mail_unlink_sent=True):
+ composer.send_mail()
+
+ # notifications
+ message = self.test_record.message_ids[0]
+ self.assertEqual(message.notified_partner_ids, self.partner_employee_2 + self.partner_1 + self.partner_2)
+
+ # global outgoing
+ self.assertEqual(len(self._new_mails), 2, 'Should have created 2 mail.mail (1 for users, 1 for customers)')
+ self.assertEqual(len(self._mails), 3, 'Should have sent an email each recipient')
+ self.assertEqual(self._new_mails.exists(), self.env['mail.mail'], 'Should have deleted mail.mail records')
+
+ # ensure ``mail_auto_delete`` context key allow to override this behavior
+ composer = self.env['mail.compose.message'].with_context(
+ self._get_web_context(self.test_record),
+ mail_auto_delete=False,
+ ).create({
+ 'body': '<p>Test Body</p>',
+ 'partner_ids': [(4, self.partner_1.id), (4, self.partner_2.id)]
+ })
+ with self.mock_mail_gateway(mail_unlink_sent=True):
+ composer.send_mail()
+
+ # notifications
+ message = self.test_record.message_ids[0]
+ self.assertEqual(message.notified_partner_ids, self.partner_employee_2 + self.partner_1 + self.partner_2)
+
+ # global outgoing
+ self.assertEqual(len(self._new_mails), 2, 'Should have created 2 mail.mail (1 for users, 1 for customers)')
+ self.assertEqual(len(self._mails), 3, 'Should have sent an email each recipient')
+ self.assertEqual(len(self._new_mails.exists()), 2, 'Should not have deleted mail.mail records')
+
+ @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()