diff --git a/addons/event/tests/test_event_mail_schedule.py b/addons/event/tests/test_event_mail_schedule.py index 580fd376ff5..511bfcc477b 100644 --- a/addons/event/tests/test_event_mail_schedule.py +++ b/addons/event/tests/test_event_mail_schedule.py @@ -8,63 +8,82 @@ from freezegun import freeze_time from odoo import Command from odoo.addons.event.tests.common import EventCase from odoo.addons.mail.tests.common import MockEmail +from odoo.tests import tagged, users from odoo.tools import formataddr, mute_logger +@tagged('event_mail', 'post_install', '-at_install') class TestMailSchedule(EventCase, MockEmail): - @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models') - def test_event_mail_schedule(self): - """ Test mail scheduling for events """ - self.env.company.write({ - 'name': 'YourCompany', + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.env.company.write({ 'email': 'info@yourcompany.example.com', + 'name': 'YourCompany', }) - event_cron_id = self.env.ref('event.event_mail_scheduler') + cls.event_cron_id = cls.env.ref('event.event_mail_scheduler') # deactivate other schedulers to avoid messing with crons - self.env['event.mail'].search([]).unlink() + cls.env['event.mail'].search([]).unlink() # freeze some datetimes, and ensure more than 1D+1H before event starts # to ease time-based scheduler check # Since `now` is used to set the `create_date` of an event and create_date # has often microseconds, we set it to ensure that the scheduler we still be # launched if scheduled_date == create_date - microseconds - now = datetime(2021, 3, 20, 14, 30, 15, 123456) - event_date_begin = datetime(2021, 3, 22, 8, 0, 0) - event_date_end = datetime(2021, 3, 24, 18, 0, 0) + cls.reference_now = datetime(2021, 3, 20, 14, 30, 15, 123456) + cls.event_date_begin = datetime(2021, 3, 22, 8, 0, 0) + cls.event_date_end = datetime(2021, 3, 24, 18, 0, 0) - with freeze_time(now): + cls.template_subscription_id = cls.env['ir.model.data']._xmlid_to_res_id('event.event_subscription') + cls.template_reminder_id = cls.env['ir.model.data']._xmlid_to_res_id('event.event_reminder') + + with cls.mock_datetime_and_now(cls, cls.reference_now): # create with admin to force create_date - test_event = self.env['event.event'].create({ + cls.test_event = cls.env['event.event'].create({ 'name': 'TestEventMail', - 'create_date': now, - 'user_id': self.user_eventmanager.id, - 'date_begin': event_date_begin, - 'date_end': event_date_end, + 'user_id': cls.user_eventmanager.id, + 'date_begin': cls.event_date_begin, + 'date_end': cls.event_date_end, 'event_mail_ids': [ (0, 0, { # right at subscription 'interval_unit': 'now', 'interval_type': 'after_sub', - 'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_subscription')}), + 'notification_type': 'mail', + 'template_ref': f'mail.template,{cls.template_subscription_id}', + }), (0, 0, { # one hour after subscription 'interval_nbr': 1, 'interval_unit': 'hours', 'interval_type': 'after_sub', - 'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_subscription')}), + 'notification_type': 'mail', + 'template_ref': f'mail.template,{cls.template_subscription_id}', + }), (0, 0, { # 1 days before event 'interval_nbr': 1, 'interval_unit': 'days', 'interval_type': 'before_event', - 'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_reminder')}), + 'notification_type': 'mail', + 'template_ref': f'mail.template,{cls.template_reminder_id}', + }), (0, 0, { # immediately after event 'interval_nbr': 1, 'interval_unit': 'hours', 'interval_type': 'after_event', - 'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_reminder')}), + 'notification_type': 'mail', + 'template_ref': f'mail.template,{cls.template_reminder_id}', + }), ] }) - self.assertEqual(test_event.create_date, now) + + def test_assert_initial_values(self): + """ Ensure base values for tests """ + test_event = self.test_event + + # event data + self.assertEqual(test_event.create_date, self.reference_now) # check subscription scheduler after_sub_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_sub'), ('interval_unit', '=', 'now')]) @@ -82,28 +101,38 @@ class TestMailSchedule(EventCase, MockEmail): # check before event scheduler event_prev_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')]) self.assertEqual(len(event_prev_scheduler), 1, 'event: wrong scheduler creation') - self.assertEqual(event_prev_scheduler.scheduled_date, event_date_begin + relativedelta(days=-1)) + self.assertEqual(event_prev_scheduler.scheduled_date, self.event_date_begin + relativedelta(days=-1)) self.assertFalse(event_prev_scheduler.mail_done) self.assertEqual(event_prev_scheduler.mail_state, 'scheduled') self.assertEqual(event_prev_scheduler.mail_count_done, 0) # check after event scheduler event_next_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_event')]) self.assertEqual(len(event_next_scheduler), 1, 'event: wrong scheduler creation') - self.assertEqual(event_next_scheduler.scheduled_date, event_date_end + relativedelta(hours=1)) + self.assertEqual(event_next_scheduler.scheduled_date, self.event_date_end + relativedelta(hours=1)) self.assertFalse(event_next_scheduler.mail_done) self.assertEqual(event_next_scheduler.mail_state, 'scheduled') self.assertEqual(event_next_scheduler.mail_count_done, 0) + @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models') + @users('user_eventmanager') + def test_event_mail_schedule(self): + """ Test mail scheduling for events """ # create some registrations - with freeze_time(now), self.mock_mail_gateway(): + test_event = self.test_event.with_env(self.env) + now = self.reference_now + schedulers = self.env['event.mail'].search([('event_id', '=', test_event.id)]) + after_sub_scheduler = schedulers.filtered(lambda s: s.interval_type == 'after_sub' and s.interval_unit == 'now') + after_sub_scheduler_2 = schedulers.filtered(lambda s: s.interval_type == 'after_sub' and s.interval_unit == 'hours') + event_prev_scheduler = schedulers.filtered(lambda s: s.interval_type == 'before_event') + event_next_scheduler = schedulers.filtered(lambda s: s.interval_type == 'after_event') + + with self.mock_datetime_and_now(now), self.mock_mail_gateway(): reg1 = self.env['event.registration'].create({ - 'create_date': now, 'event_id': test_event.id, 'name': 'Reg1', 'email': 'reg1@example.com', }) reg2 = self.env['event.registration'].create({ - 'create_date': now, 'event_id': test_event.id, 'name': 'Reg2', 'email': 'reg2@example.com', @@ -180,9 +209,9 @@ class TestMailSchedule(EventCase, MockEmail): self.assertEqual(event_prev_scheduler.mail_state, 'scheduled') # simulate cron running before scheduled date -> should not do anything - now_start = event_date_begin + relativedelta(hours=-25, microsecond=654321) + now_start = self.event_date_begin + relativedelta(hours=-25, microsecond=654321) with freeze_time(now_start), self.mock_mail_gateway(): - event_cron_id.method_direct_trigger() + self.event_cron_id.method_direct_trigger() self.assertFalse(event_prev_scheduler.mail_done) self.assertEqual(event_prev_scheduler.mail_state, 'scheduled') @@ -190,9 +219,9 @@ class TestMailSchedule(EventCase, MockEmail): self.assertEqual(len(self._new_mails), 0) # execute cron to run schedulers after scheduled date - now_start = event_date_begin + relativedelta(hours=-23, microsecond=654321) + now_start = self.event_date_begin + relativedelta(hours=-23, microsecond=654321) with freeze_time(now_start), self.mock_mail_gateway(): - event_cron_id.method_direct_trigger() + self.event_cron_id.method_direct_trigger() # check that scheduler is finished self.assertTrue(event_prev_scheduler.mail_done, 'event: reminder scheduler should have run') @@ -211,9 +240,8 @@ class TestMailSchedule(EventCase, MockEmail): # NEW REGISTRATION EFFECT ON SCHEDULERS # -------------------------------------------------- - with freeze_time(now_start), self.mock_mail_gateway(): + with self.mock_datetime_and_now(now_start), self.mock_mail_gateway(): reg3 = self.env['event.registration'].create({ - 'create_date': now_start, 'event_id': test_event.id, 'name': 'Reg3', 'email': 'reg3@example.com', @@ -232,7 +260,7 @@ class TestMailSchedule(EventCase, MockEmail): # confirm registration -> should trigger registration schedulers # NOTE: currently all schedulers are based on create_date # meaning several communications may be sent in the time time - with freeze_time(now_start + relativedelta(hours=1)), self.mock_mail_gateway(): + with self.mock_datetime_and_now(now_start + relativedelta(hours=1)), self.mock_mail_gateway(): reg3.action_confirm() # verify that subscription scheduler was auto-executed after new registration confirmed @@ -265,9 +293,9 @@ class TestMailSchedule(EventCase, MockEmail): self.assertFalse(event_next_scheduler.mail_done) # execute event reminder scheduler explicitly after its schedule date - new_end = event_date_end + relativedelta(hours=2) - with freeze_time(new_end), self.mock_mail_gateway(): - event_cron_id.method_direct_trigger() + new_end = self.event_date_end + relativedelta(hours=2) + with self.mock_datetime_and_now(new_end), self.mock_mail_gateway(): + self.event_cron_id.method_direct_trigger() # check that scheduler is finished self.assertTrue(event_next_scheduler.mail_done, 'event: reminder scheduler should should have run') @@ -284,6 +312,63 @@ class TestMailSchedule(EventCase, MockEmail): 'email_from': self.user_eventmanager.company_id.email_formatted, }) + @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models') + @users('user_eventmanager') + def test_event_mail_schedule_on_subscription(self): + """ Test emails sent on subscription, notably to avoid bottlenecks """ + test_event = self.test_event.with_env(self.env) + reference_now = self.reference_now + + # remove on subscription, to create hanging registrations + schedulers = self.env['event.mail'].search([('event_id', '=', test_event.id)]) + _sub_scheduler = schedulers.filtered(lambda s: s.interval_type == 'after_sub' and s.interval_unit == 'now') + _sub_scheduler.unlink() + + # consider having hanging registrations, still not processed (e.g. adding + # a new scheduler after) + self.env.invalidate_all() + # com 58, event 36 + with self.assertQueryCount(61), self.mock_datetime_and_now(reference_now), \ + self.mock_mail_gateway(): + _existing = self.env['event.registration'].create([ + { + 'email': f'existing.attendee.{idx}@test.example.com', + 'event_id': test_event.id, + 'name': f'Attendee {idx}', + } for idx in range(5) + ]) + self.assertEqual(len(self._new_mails), 0) + self.assertEqual(self.mail_mail_create_mocked.call_count, 0) + + # add on subscription scheduler, then new registrations ! yay ! check what + # happens with old ones + test_event.write({'event_mail_ids': [ + (0, 0, { # right at subscription + 'interval_unit': 'now', + 'interval_type': 'after_sub', + 'notification_type': 'mail', + 'template_ref': f'mail.template,{self.template_subscription_id}', + }), + ]}) + self.env.invalidate_all() + # com 148, event 134 + with self.assertQueryCount(153), \ + self.mock_datetime_and_now(reference_now + relativedelta(minutes=10)), \ + self.mock_mail_gateway(): + _new = self.env['event.registration'].create([ + { + 'email': f'new.attendee.{idx}@test.example.com', + 'event_id': test_event.id, + 'name': f'New Attendee {idx}', + } for idx in range(2) + ]) + # self.assertEqual(len(self._new_mails), 2) + # self.assertEqual(self.mail_mail_create_mocked.call_count, 2) + self.assertEqual(len(self._new_mails), 7, + 'EventMail: TODO: should be limited to new registrations') + self.assertEqual(self.mail_mail_create_mocked.call_count, 7, + 'EventMail: TODO: should create one mail / new registration') + @mute_logger('odoo.addons.base.models.ir_model', 'odoo.models') def test_unique_event_mail_ids(self): # create event with default event_mail_ids lines @@ -353,7 +438,7 @@ class TestMailSchedule(EventCase, MockEmail): event_date_begin = datetime(2023, 7, 26, 8, 0, 0) event_date_end = datetime(2023, 7, 28, 18, 0, 0) - with freeze_time(now): + with self.mock_datetime_and_now(now): test_event = self.env['event.event'].with_user(self.user_eventmanager).create({ 'name': 'TestEventMail', 'date_begin': event_date_begin, @@ -377,15 +462,13 @@ class TestMailSchedule(EventCase, MockEmail): event_prev_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')]) - with freeze_time(now), self.mock_mail_gateway(): + with self.mock_datetime_and_now(now), self.mock_mail_gateway(): self.env['event.registration'].create({ - 'create_date': now, 'event_id': test_event.id, 'name': 'Reg1', 'email': 'reg1@example.com', }) self.env['event.registration'].create({ - 'create_date': now, 'event_id': test_event.id, 'name': 'Reg2', 'email': 'reg2@example.com', diff --git a/addons/event_sms/tests/test_sms_schedule.py b/addons/event_sms/tests/test_sms_schedule.py index 76aff449248..4b68c01e118 100644 --- a/addons/event_sms/tests/test_sms_schedule.py +++ b/addons/event_sms/tests/test_sms_schedule.py @@ -7,9 +7,10 @@ from odoo import fields from odoo.addons.event.tests.common import EventCase from odoo.addons.phone_validation.tools import phone_validation from odoo.addons.sms.tests.common import SMSCase -from odoo.tests import users +from odoo.tests import tagged, users +@tagged('event_mail') class TestSMSSchedule(EventCase, SMSCase): @classmethod diff --git a/addons/test_event_full/tests/common.py b/addons/test_event_full/tests/common.py index 9c13801a0cc..5115bb648e1 100644 --- a/addons/test_event_full/tests/common.py +++ b/addons/test_event_full/tests/common.py @@ -226,14 +226,15 @@ class TestEventFullCommon(EventCrmCase, TestSalesCommon, MockVisitor): 'is_published': True, } - cls.test_event = cls.env['event.event'].create({ - 'name': 'Test Event', - 'date_begin': datetime.now() + timedelta(days=1), - 'date_end': datetime.now() + timedelta(days=5), - 'date_tz': 'Europe/Brussels', - 'event_type_id': cls.test_event_type.id, - 'is_published': True, - }) + with cls.mock_datetime_and_now(cls, cls.reference_now): + cls.test_event = cls.env['event.event'].create({ + 'name': 'Test Event', + 'date_begin': datetime.now() + timedelta(days=1), + 'date_end': datetime.now() + timedelta(days=5), + 'date_tz': 'Europe/Brussels', + 'event_type_id': cls.test_event_type.id, + 'is_published': True, + }) # update post-synchronize data ticket_1 = cls.test_event.event_ticket_ids.filtered(lambda t: t.name == 'Ticket1') ticket_2 = cls.test_event.event_ticket_ids.filtered(lambda t: t.name == 'Ticket2') diff --git a/addons/test_event_full/tests/test_event_event.py b/addons/test_event_full/tests/test_event_event.py index 97aadabb7b2..a96d3a65c17 100644 --- a/addons/test_event_full/tests/test_event_event.py +++ b/addons/test_event_full/tests/test_event_event.py @@ -77,9 +77,6 @@ class TestEventEvent(TestEventFullCommon): @freeze_time('2021-12-01 11:00:00') @users('event_user') def test_event_seats_and_schedulers(self): - now = datetime.now() # used to force create_date, as sql is not wrapped by freeze gun - self.env.cr._now = now - test_event = self.env['event.event'].browse(self.test_event.ids) ticket_1 = test_event.event_ticket_ids.filtered(lambda ticket: ticket.name == 'Ticket1') ticket_2 = test_event.event_ticket_ids.filtered(lambda ticket: ticket.name == 'Ticket2') @@ -93,14 +90,15 @@ class TestEventEvent(TestEventFullCommon): self.assertFalse(ticket_2.sale_available) # make 9 registrations (let 1 on ticket) - with self.mock_mail_gateway(): + with self.mock_datetime_and_now(self.reference_now), \ + self.mock_mail_gateway(): self.env['event.registration'].create([ - {'create_date': now, - 'email': 'test.customer.%02d@test.example.com' % x, - 'phone': '04560011%02d' % x, - 'event_id': test_event.id, - 'event_ticket_id': ticket_1.id, - 'name': 'Customer %d' % x, + { + 'email': 'test.customer.%02d@test.example.com' % x, + 'phone': '04560011%02d' % x, + 'event_id': test_event.id, + 'event_ticket_id': ticket_1.id, + 'name': 'Customer %d' % x, } for x in range(0, 9) ]) @@ -113,27 +111,29 @@ class TestEventEvent(TestEventFullCommon): self.assertEqual(ticket_2.seats_available, 0) # prevent registration due to ticket limit - with self.assertRaises(exceptions.ValidationError): + with self.mock_datetime_and_now(self.reference_now), \ + self.assertRaises(exceptions.ValidationError): self.env['event.registration'].create([ - {'create_date': now, - 'email': 'additional.customer.%02d@test.example.com' % x, - 'phone': '04560011%02d' % x, - 'event_id': test_event.id, - 'event_ticket_id': ticket_1.id, - 'name': 'Additional Customer %d' % x, + { + 'email': 'additional.customer.%02d@test.example.com' % x, + 'phone': '04560011%02d' % x, + 'event_id': test_event.id, + 'event_ticket_id': ticket_1.id, + 'name': 'Additional Customer %d' % x, } for x in range(0, 2) ]) # make 20 registrations (on free ticket) - with self.mock_mail_gateway(): + with self.mock_datetime_and_now(self.reference_now), \ + self.mock_mail_gateway(): self.env['event.registration'].create([ - {'create_date': now, - 'email': 'other.customer.%02d@test.example.com' % x, - 'phone': '04560011%02d' % x, - 'event_id': test_event.id, - 'event_ticket_id': ticket_2.id, - 'name': 'Other Customer %d' % x, + { + 'email': 'other.customer.%02d@test.example.com' % x, + 'phone': '04560011%02d' % x, + 'event_id': test_event.id, + 'event_ticket_id': ticket_2.id, + 'name': 'Other Customer %d' % x, } for x in range(0, 20) ]) @@ -144,14 +144,15 @@ class TestEventEvent(TestEventFullCommon): self.assertEqual(ticket_2.seats_available, 0) # prevent registration due to event limit - with self.assertRaises(exceptions.ValidationError): + with self.mock_datetime_and_now(self.reference_now), \ + self.assertRaises(exceptions.ValidationError): self.env['event.registration'].create([ - {'create_date': now, - 'email': 'additional.customer.%02d@test.example.com' % x, - 'phone': '04560011%02d' % x, - 'event_id': test_event.id, - 'event_ticket_id': ticket_2.id, - 'name': 'Additional Customer %d' % x, + { + 'email': 'additional.customer.%02d@test.example.com' % x, + 'phone': '04560011%02d' % x, + 'event_id': test_event.id, + 'event_ticket_id': ticket_2.id, + 'name': 'Additional Customer %d' % x, } for x in range(0, 2) ]) diff --git a/addons/test_event_full/tests/test_event_mail.py b/addons/test_event_full/tests/test_event_mail.py index 835a759cd9d..30c2766e804 100644 --- a/addons/test_event_full/tests/test_event_mail.py +++ b/addons/test_event_full/tests/test_event_mail.py @@ -3,11 +3,13 @@ from odoo.addons.test_event_full.tests.common import TestWEventCommon from odoo.exceptions import ValidationError +from odoo.tests import tagged + +@tagged('event_mail') class TestTemplateRefModel(TestWEventCommon): def test_template_ref_model_constraint(self): - test_cases = [ ('mail', 'mail.template', True), ('mail', 'sms.template', False), diff --git a/addons/test_mail/tests/test_mail_template.py b/addons/test_mail/tests/test_mail_template.py index 5acd3ce20d6..985a1c74845 100644 --- a/addons/test_mail/tests/test_mail_template.py +++ b/addons/test_mail/tests/test_mail_template.py @@ -116,50 +116,202 @@ class TestMailTemplate(TestMailTemplateCommon): self.assertEqual(mail.body_html, body_result) self.assertEqual(mail.body, body_result) -@tagged('mail_template', 'multi_lang') + +@tagged('mail_template', 'multi_lang', 'post_install', '-at_install') class TestMailTemplateLanguages(TestMailTemplateCommon): + @classmethod + def setUpClass(cls): + """ Create lang-based records and templates, to test batch and performances + with language involved. """ + super().setUpClass() + + # use test notification layout + cls.test_template.write({ + 'email_layout_xmlid': 'mail.test_layout', + }) + + # double record, one in each lang + cls.test_records = cls.test_record + cls.env['mail.test.lang'].create({ + 'email_from': 'ignasse.es@example.com', + 'lang': 'es_ES', + 'name': 'Test Record 2', + }) + + # pure batch, 100 records + cls.test_records_batch, test_partners = cls._create_records_for_batch( + 'mail.test.lang', 100, + ) + test_partners[:50].lang = 'es_ES' + + # have a template with dynamic templates to check impact + cls.test_template_wreports = cls.test_template.copy({ + 'email_layout_xmlid': 'mail.test_layout', + }) + cls.test_reports = cls.env['ir.actions.report'].create([ + { + 'name': f'Test Report on {cls.test_record._name}', + 'model': cls.test_record._name, + 'print_report_name': "f'TestReport for {object.name}'", + 'report_type': 'qweb-pdf', + 'report_name': 'test_mail.mail_test_ticket_test_template', + }, { + 'name': f'Test Report 2 on {cls.test_record._name}', + 'model': cls.test_record._name, + 'print_report_name': "f'TestReport2 for {object.name}'", + 'report_type': 'qweb-pdf', + 'report_name': 'test_mail.mail_test_ticket_test_template_2', + } + ]) + cls.test_template_wreports.report_template_ids = cls.test_reports + + cls.env.flush_all() + @mute_logger('odoo.addons.mail.models.mail_mail') def test_template_send_email(self): - mail_id = self.test_template.send_mail(self.test_record.id) - mail = self.env['mail.mail'].sudo().browse(mail_id) + """ Test 'send_email' on template on a given record, used notably as + contextual action. """ + self.env.invalidate_all() + with self.with_user(self.user_employee.login), self.assertQueryCount(28): # test_mail: 28 + mail_id = self.test_template.with_env(self.env).send_mail(self.test_record.id) + mail = self.env['mail.mail'].sudo().browse(mail_id) + + self.assertEqual(sorted(mail.attachment_ids.mapped('name')), ['first.txt', 'second.txt']) + self.assertEqual(mail.body_html, + f'

EnglishBody for {self.test_record.name}

English Layout for Lang Chatter Model') self.assertEqual(mail.email_cc, self.test_template.email_cc) self.assertEqual(mail.email_to, self.test_template.email_to) self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id) - self.assertEqual(mail.subject, 'EnglishSubject for %s' % self.test_record.name) + self.assertEqual(mail.subject, f'EnglishSubject for {self.test_record.name}') + + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_template_send_email_nolayout(self): + """ Test without layout, just to check impact """ + self.test_template.email_layout_xmlid = False + self.env.invalidate_all() + with self.with_user(self.user_employee.login), self.assertQueryCount(21): # test_mail: 21 + mail_id = self.test_template.with_env(self.env).send_mail(self.test_record.id) + mail = self.env['mail.mail'].sudo().browse(mail_id) + + self.assertEqual(sorted(mail.attachment_ids.mapped('name')), ['first.txt', 'second.txt']) + self.assertEqual(mail.body_html, + f'

EnglishBody for {self.test_record.name}

') + self.assertEqual(mail.email_cc, self.test_template.email_cc) + self.assertEqual(mail.email_to, self.test_template.email_to) + self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id) + self.assertEqual(mail.subject, f'EnglishSubject for {self.test_record.name}') + + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_template_send_email_batch(self): + """ Test 'send_email' on template in batch """ + self.env.invalidate_all() + mails = self.env['mail.mail'].sudo() + with self.with_user(self.user_employee.login), self.assertQueryCount(934): # test_mail: 934 + template = self.test_template.with_env(self.env) + for record in self.test_records_batch: + mails += mails.browse(template.send_mail(record.id)) + + self.assertEqual(len(mails), 100) + for idx, (mail, record) in enumerate(zip(mails, self.test_records_batch)): + self.assertEqual(sorted(mail.attachment_ids.mapped('name')), ['first.txt', 'second.txt']) + self.assertEqual(mail.email_cc, self.test_template.email_cc) + self.assertEqual(mail.email_to, self.test_template.email_to) + self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id) + if idx >= 50: + self.assertEqual(mail.subject, f'EnglishSubject for {record.name}') + else: + self.assertEqual(mail.subject, f'SpanishSubject for {record.name}') + + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_template_send_email_wreport(self): + """ Test 'send_email' on template on a given record, used notably as + contextual action, with dynamic reports involved """ + self.env.invalidate_all() + with self.with_user(self.user_employee.login), self.assertQueryCount(107): # test_mail: 106 + mail_id = self.test_template_wreports.with_env(self.env).send_mail(self.test_record.id) + mail = self.env['mail.mail'].sudo().browse(mail_id) + + self.assertEqual( + sorted(mail.attachment_ids.mapped('name')), + [f'TestReport for {self.test_record.name}.html', f'TestReport2 for {self.test_record.name}.html', 'first.txt', 'second.txt'] + ) + self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id) + self.assertEqual(mail.subject, f'EnglishSubject for {self.test_record.name}') + + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_template_send_email_wreport_batch(self): + """ Test 'send_email' on template in batch with dynamic reports """ + self.env.invalidate_all() + mails = self.env['mail.mail'].sudo() + with self.with_user(self.user_employee.login), self.assertQueryCount(1645): # test_mail: 1645 + template = self.test_template_wreports.with_env(self.env) + for record in self.test_records_batch: + mails += mails.browse(template.send_mail(record.id)) + + self.assertEqual(len(mails), 100) + for idx, (mail, record) in enumerate(zip(mails, self.test_records_batch)): + self.assertEqual( + sorted(mail.attachment_ids.mapped('name')), + [f'TestReport for {record.name}.html', f'TestReport2 for {record.name}.html', 'first.txt', 'second.txt'] + ) + self.assertEqual(mail.email_cc, self.test_template.email_cc) + self.assertEqual(mail.email_to, self.test_template.email_to) + self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id) + if idx >= 50: + self.assertEqual(mail.subject, f'EnglishSubject for {record.name}') + self.assertEqual(mail.body_html, + f'

EnglishBody for {record.name}

English Layout for Lang Chatter Model') + else: + self.assertEqual(mail.subject, f'SpanishSubject for {record.name}') + self.assertEqual(mail.body_html, + f'

SpanishBody for {record.name}

Spanish Layout para Spanish Model Description') @mute_logger('odoo.addons.mail.models.mail_mail') def test_template_translation_lang(self): + """ Test template rendering using lang defined directly on the record """ test_record = self.test_record.with_env(self.env) test_record.write({ 'lang': 'es_ES', }) test_template = self.test_template.with_env(self.env) - test_template.write({ - 'email_layout_xmlid': 'mail.test_layout', - }) mail_id = test_template.send_mail(test_record.id) mail = self.env['mail.mail'].sudo().browse(mail_id) self.assertEqual(mail.body_html, - '

SpanishBody for %s

Spanish Layout para Spanish Model Description' % self.test_record.name) - self.assertEqual(mail.subject, 'SpanishSubject for %s' % self.test_record.name) + f'

SpanishBody for {self.test_record.name}

Spanish Layout para Spanish Model Description') + self.assertEqual(mail.subject, f'SpanishSubject for {self.test_record.name}') @mute_logger('odoo.addons.mail.models.mail_mail') def test_template_translation_partner_lang(self): - test_record = self.env['mail.test.lang'].browse(self.test_record.ids) - customer = self.env['res.partner'].create({ - 'email': 'robert.carlos@test.example.com', - 'lang': 'es_ES', - 'name': 'Roberto Carlos', - }) - test_record.write({ - 'customer_id': customer.id, - }) - test_template = self.env['mail.template'].browse(self.test_template.ids) + """ Test template rendering using lang defined on a sub-record aka + 'partner_id.lang' """ + test_records = self.env['mail.test.lang'].browse(self.test_records.ids) + customers = self.env['res.partner'].create([ + { + 'email': 'roberto.carlos@test.example.com', + 'lang': 'es_ES', + 'name': 'Roberto Carlos', + }, { + 'email': 'rob.charly@test.example.com', + 'lang': 'en_US', + 'name': 'Rob Charly', + } + ]) + test_records[0].write({'customer_id': customers[0].id}) + test_records[1].write({'customer_id': customers[1].id}) - mail_id = test_template.send_mail(test_record.id, email_layout_xmlid='mail.test_layout') - mail = self.env['mail.mail'].sudo().browse(mail_id) - self.assertEqual(mail.body_html, - '

SpanishBody for %s

Spanish Layout para Spanish Model Description' % self.test_record.name) - self.assertEqual(mail.subject, 'SpanishSubject for %s' % self.test_record.name) + self.env.invalidate_all() + mails = self.env['mail.mail'].sudo() + with self.with_user(self.user_employee.login), self.assertQueryCount(52): + template = self.test_template.with_env(self.env) + for record in self.test_records: + mails += mails.browse( + template.send_mail(record.id, email_layout_xmlid='mail.test_layout') + ) + + self.assertEqual(mails[0].body_html, + f'

SpanishBody for {test_records[0].name}

Spanish Layout para Spanish Model Description') + self.assertEqual(mails[0].subject, f'SpanishSubject for {test_records[0].name}') + self.assertEqual(mails[1].body_html, + f'

EnglishBody for {test_records[1].name}

English Layout for Lang Chatter Model') + self.assertEqual(mails[1].subject, f'EnglishSubject for {test_records[1].name}') diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index 7382c2a6be6..2cd4f47b9a8 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -286,11 +286,11 @@ class TestBaseAPIPerformance(BaseMailPerformance): # voip module read activity_type during create leading to one less query in enterprise on action_feedback _category = activity.activity_type_id.category - with self.assertQueryCount(admin=13, employee=13): + with self.assertQueryCount(admin=12, employee=12): activity.action_feedback(feedback='Zizisse Done !') @warmup - def test_adv_activity_mixin_batched(self): + def test_activity_mixin_batched(self): records = self.env['mail.test.activity'].create([{'name': 'Test'}] * 10) MailActivity = self.env['mail.activity'].with_context({ 'default_res_model': 'mail.test.activity', @@ -304,7 +304,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): } for record in records]) self.env.invalidate_all() - with self.assertQueryCount(3): + with self.assertQueryCount(2): records.mapped('activity_date_deadline') @users('admin', 'employee') @@ -321,7 +321,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): record.write({'name': 'Dupe write'}) - with self.assertQueryCount(admin=14, employee=14): + with self.assertQueryCount(admin=13, employee=13): record.action_close('Dupe feedback') self.assertEqual(record.activity_ids, self.env['mail.activity']) @@ -347,7 +347,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): record.write({'name': 'Dupe write'}) - with self.assertQueryCount(admin=17, employee=17): # com+tm 16/16 + with self.assertQueryCount(admin=15, employee=15): # com+tm 14/14 record.action_close('Dupe feedback', attachment_ids=attachments.ids) # notifications @@ -373,7 +373,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): 'partner_ids': [(4, customer_id)], }) - with self.assertQueryCount(admin=39, employee=39): # com 35/35 + with self.assertQueryCount(admin=39, employee=39): composer._action_send_mail() @users('admin', 'employee') @@ -394,7 +394,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): 'partner_ids': [(4, customer.id)], }) - with self.assertQueryCount(admin=40, employee=40): # com 35/35 + with self.assertQueryCount(admin=40, employee=40): composer._action_send_mail() @users('admin', 'employee') @@ -418,7 +418,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): composer_form.attachment_ids.add(attachment) composer = composer_form.save() - with self.assertQueryCount(admin=55, employee=55): # tm+com 53/53 + with self.assertQueryCount(admin=55, employee=55): # tm+com 54/54 composer._action_send_mail() # notifications @@ -472,7 +472,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): test_record, test_template = self._create_test_records() test_template.write({'attachment_ids': [(5, 0)]}) - with self.assertQueryCount(admin=27, employee=27): # tm 15/15 / com 25/25 + with self.assertQueryCount(admin=27, employee=27): # tm 16/16 / com 26/26 composer = self.env['mail.compose.message'].with_context({ 'default_composition_mode': 'comment', 'default_model': test_record._name, @@ -496,7 +496,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): def test_mail_composer_w_template_attachments(self): test_record, test_template = self._create_test_records() - with self.assertQueryCount(admin=28, employee=28): # tm 16/16 / com 26/26 + with self.assertQueryCount(admin=28, employee=28): # tm 17/17 / com 27/27 composer = self.env['mail.compose.message'].with_context({ 'default_composition_mode': 'comment', 'default_model': test_record._name, @@ -525,7 +525,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): test_template.write({'attachment_ids': [(5, 0)]}) customer = self.env['res.partner'].browse(self.customer.ids) - with self.assertQueryCount(admin=34, employee=34): # tm 22/22 / com 32/32 + with self.assertQueryCount(admin=34, employee=34): # tm 23/23 / com 33/33 composer_form = Form( self.env['mail.compose.message'].with_context({ 'default_composition_mode': 'comment', @@ -555,7 +555,7 @@ class TestBaseAPIPerformance(BaseMailPerformance): test_record, test_template = self._create_test_records() customer = self.env['res.partner'].browse(self.customer.ids) - with self.assertQueryCount(admin=34, employee=34): # tm 22/22 / com 32/32 + with self.assertQueryCount(admin=34, employee=34): # tm 23/23 / com 32/32 composer_form = Form( self.env['mail.compose.message'].with_context({ 'default_composition_mode': 'comment', @@ -878,7 +878,7 @@ class TestMailAPIPerformance(BaseMailPerformance): record = self.container.with_user(self.env.user) # about 20 (19?) queries per additional customer group - with self.assertQueryCount(admin=61, employee=60): # com 55/54 + with self.assertQueryCount(admin=60, employee=59): record.message_post( body=Markup('

Test Post Performances

'), message_type='comment', @@ -896,7 +896,7 @@ class TestMailAPIPerformance(BaseMailPerformance): template = self.env.ref('test_mail.mail_test_container_tpl') # about 20 (19 ?) queries per additional customer group - with self.assertQueryCount(admin=70, employee=69): # com 62/61 + with self.assertQueryCount(admin=69, employee=68): record.message_post_with_source( template, message_type='comment', @@ -920,7 +920,7 @@ class TestMailAPIPerformance(BaseMailPerformance): 'default_template_id': test_template.id, }).create({}) - with self.assertQueryCount(admin=151, employee=151): # com 141/141 + with self.assertQueryCount(admin=151, employee=151): messages_as_sudo = test_records.message_post_with_source( 'test_mail.mail_template_simple_test', render_values={'partner': self.user_test.partner_id}, @@ -1006,7 +1006,7 @@ class TestMailAPIPerformance(BaseMailPerformance): customer_id = self.customer.id user_id = self.user_portal.id - with self.assertQueryCount(admin=102, employee=102): + with self.assertQueryCount(admin=101, employee=101): rec = self.env['mail.test.ticket'].create({ 'name': 'Test', 'container_id': container_id, @@ -1035,7 +1035,7 @@ class TestMailAPIPerformance(BaseMailPerformance): rec1 = rec.with_context(active_test=False) # to see inactive records self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id) self.assertEqual(len(rec1.message_ids), 1) - with self.assertQueryCount(admin=63, employee=63): + with self.assertQueryCount(admin=62, employee=62): rec.write({ 'name': 'Test2', 'container_id': self.container.id, @@ -1072,7 +1072,7 @@ class TestMailAPIPerformance(BaseMailPerformance): rec1 = rec.with_context(active_test=False) # to see inactive records self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id) - with self.assertQueryCount(admin=70, employee=70): + with self.assertQueryCount(admin=69, employee=69): rec.write({ 'name': 'Test2', 'container_id': container_id, @@ -1264,7 +1264,7 @@ class TestMailFormattersPerformance(BaseMailPerformance): """ messages_all = self.messages_all.with_env(self.env) - with self.assertQueryCount(employee=26): # test_mail: 25 + with self.assertQueryCount(employee=26): res = messages_all.message_format() self.assertEqual(len(res), 2*2) @@ -1277,7 +1277,7 @@ class TestMailFormattersPerformance(BaseMailPerformance): def test_message_format_single(self): message = self.messages_all[0].with_env(self.env) - with self.assertQueryCount(employee=23): # test_mail: 22 + with self.assertQueryCount(employee=23): res = message.message_format() self.assertEqual(len(res), 1) @@ -1382,7 +1382,7 @@ class TestPerformance(BaseMailPerformance): attachments = self.env['ir.attachment'].with_user(self.env.user).create(self.test_attachments_vals) # enable_logging = self.cr._enable_logging() if self.warm else nullcontext() # with self.assertQueryCount(employee=63), enable_logging: - with self.assertQueryCount(employee=69): # com 63 + with self.assertQueryCount(employee=68): record_container.with_context({}).message_post( body=Markup('

Test body

'), subject='Test Subject', diff --git a/addons/test_mail_full/tests/test_mail_performance.py b/addons/test_mail_full/tests/test_mail_performance.py index 9d5fc1840a1..3220d968199 100644 --- a/addons/test_mail_full/tests/test_mail_performance.py +++ b/addons/test_mail_full/tests/test_mail_performance.py @@ -91,7 +91,7 @@ class TestMailPerformance(FullBaseMailPerformance): record_ticket = self.env['mail.test.ticket.mc'].browse(self.record_ticket.ids) attachments = self.env['ir.attachment'].create(self.test_attachments_vals) - with self.assertQueryCount(employee=101): # test_mail_full: 95 + with self.assertQueryCount(employee=99): # test_mail_full: 95 new_message = record_ticket.message_post( attachment_ids=attachments.ids, body=Markup('

Test Content

'),