diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index 996f9d63866..fbfa71cad4b 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -1250,6 +1250,10 @@ class MailCommon(common.TransactionCase, MailCase): 'name': 'ELIT', }) cls.user_admin.write({'company_ids': [(4, cls.company_2.id)]}) + cls.company_3 = cls.env['res.company'].create({ + 'email': 'company_3@test.example.com', + 'name': 'Company 3', + }) # employee specific to second company cls.user_employee_c2 = mail_new_test_user( diff --git a/addons/test_mail/tests/test_mail_alias.py b/addons/test_mail/tests/test_mail_alias.py index d13fa75c04c..5e6c7603998 100644 --- a/addons/test_mail/tests/test_mail_alias.py +++ b/addons/test_mail/tests/test_mail_alias.py @@ -9,7 +9,7 @@ from odoo import exceptions from odoo.addons.mail.tests.common import MailCommon from odoo.tests import tagged from odoo.tests.common import users -from odoo.tools import mute_logger +from odoo.tools import formataddr, mute_logger class TestMailAliasCommon(MailCommon): @@ -91,24 +91,30 @@ class TestMailAlias(TestMailAliasCommon): @users('admin') def test_alias_name_unique(self): + """ Check uniqueness constraint on alias names, at create and update. + Also check conflict management with bounce / catchall aliases. """ alias_model_id = self.env['ir.model']._get('mail.test.gateway').id catchall_alias = self.env['ir.config_parameter'].sudo().get_param('mail.catchall.alias') bounce_alias = self.env['ir.config_parameter'].sudo().get_param('mail.bounce.alias') - # test you cannot create aliases matching bounce / catchall + new_mail_alias = self.env['mail.alias'].create({ + 'alias_model_id': alias_model_id, + 'alias_name': 'unused.test.alias', + }) + other_alias = self.env['mail.alias'].create({ + 'alias_model_id': alias_model_id, + 'alias_name': 'other.test.alias', + }) + + # test you cannot create or update aliases matching bounce / catchall with self.assertRaises(exceptions.UserError), self.cr.savepoint(): self.env['mail.alias'].create({'alias_model_id': alias_model_id, 'alias_name': catchall_alias}) with self.assertRaises(exceptions.UserError), self.cr.savepoint(): self.env['mail.alias'].create({'alias_model_id': alias_model_id, 'alias_name': bounce_alias}) - - new_mail_alias = self.env['mail.alias'].create({ - 'alias_model_id': alias_model_id, - 'alias_name': 'unused.test.alias' - }) - other_alias = self.env['mail.alias'].create({ - 'alias_model_id': alias_model_id, - 'alias_name': 'other.test.alias' - }) + with self.assertRaises(exceptions.UserError), self.cr.savepoint(): + new_mail_alias.write({'alias_name': catchall_alias}) + with self.assertRaises(exceptions.UserError), self.cr.savepoint(): + new_mail_alias.write({'alias_name': bounce_alias}) # test that alias names should be unique with self.assertRaises(exceptions.UserError), self.cr.savepoint(): @@ -124,18 +130,26 @@ class TestMailAlias(TestMailAliasCommon): } for alias_name in ('new.alias.1', 'new.alias.2', 'new.alias.1') ]) - with self.assertRaises(psycopg2.errors.UniqueViolation), self.cr.savepoint(), mute_logger('odoo.sql_db'): - new_mail_alias.copy({'alias_name': 'unused.test.alias'}) with self.assertRaises(exceptions.UserError), self.cr.savepoint(): other_alias.write({'alias_name': 'unused.test.alias'}) - # test that re-using catchall and bounce alias raises UserError + # cannot set catchall / bounce to used alias with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - new_mail_alias.write({'alias_name': catchall_alias}) + self.env['ir.config_parameter'].sudo().set_param('mail.catchall.alias', new_mail_alias.alias_name) with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - new_mail_alias.write({'alias_name': bounce_alias}) + self.env['ir.config_parameter'].sudo().set_param('mail.bounce.alias', new_mail_alias.alias_name) - new_mail_alias.write({'alias_name': 'another.unused.test.alias'}) + @users('admin') + def test_alias_name_unique_copy(self): + """ Check uniqueness constraint check when copying aliases """ + alias_model_id = self.env['ir.model']._get('mail.test.gateway').id + new_mail_alias = self.env['mail.alias'].create({ + 'alias_model_id': alias_model_id, + 'alias_name': 'unused.test.alias' + }) + + with mute_logger('odoo.sql_db'), self.assertRaises(psycopg2.errors.UniqueViolation), self.cr.savepoint(): + new_mail_alias.copy({'alias_name': 'unused.test.alias'}) # test that duplicating an alias should have blank name copy_1 = new_mail_alias.copy() @@ -148,12 +162,6 @@ class TestMailAlias(TestMailAliasCommon): with self.assertRaises(exceptions.UserError): (copy_1 + copy_2).write({'alias_name': 'test.alias.other'}) - # cannot set catchall / bounce to used alias - with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - self.env['ir.config_parameter'].sudo().set_param('mail.catchall.alias', new_mail_alias.alias_name) - with self.assertRaises(exceptions.UserError), self.cr.savepoint(): - self.env['ir.config_parameter'].sudo().set_param('mail.bounce.alias', new_mail_alias.alias_name) - @users('admin') @mute_logger('odoo.models.unlink') def test_alias_name_sanitize(self): @@ -238,6 +246,45 @@ class TestMailAlias(TestMailAliasCommon): alias.write({'alias_defaults': "{'custom_field': 'validdict'}"}) +@tagged('mail_alias', 'multi_company') +class TestAliasCompany(TestMailAliasCommon): + """ Test company / alias domain and configuration synchronization """ + + def test_assert_initial_values(self): + """ Test initial setup values: currently all companies share the same + alias configuration as it is unique. """ + self.assertEqual(self.company_admin.catchall_email, f'{self.alias_catchall}@{self.alias_domain}') + self.assertEqual( + self.company_admin.catchall_formatted, + formataddr((self.company_admin.name, f'{self.alias_catchall}@{self.alias_domain}')) + ) + + self.assertEqual(self.company_2.catchall_email, f'{self.alias_catchall}@{self.alias_domain}') + self.assertEqual( + self.company_2.catchall_formatted, + formataddr((self.company_2.name, f'{self.alias_catchall}@{self.alias_domain}')) + ) + + self.assertEqual(self.company_3.catchall_email, f'{self.alias_catchall}@{self.alias_domain}') + self.assertEqual( + self.company_3.catchall_formatted, + formataddr((self.company_3.name, f'{self.alias_catchall}@{self.alias_domain}')) + ) + + @users('erp_manager') + def test_res_company_creation_alias_domain(self): + """ Test alias domain configuration when creating new companies """ + company = self.env['res.company'].create({ + 'email': '"Super Company" ', + 'name': 'Super Company', + }) + company.flush_recordset() + self.assertEqual( + company.catchall_formatted, + formataddr((company.name, f'{self.alias_catchall}@{self.alias_domain}')) + ) + + @tagged('mail_gateway', 'mail_alias', 'multi_company') class TestMailAliasMixin(TestMailAliasCommon): """ Test alias mixin implementation, synchornization of alias records diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index 516e6a44d19..68be256d7c2 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -29,6 +29,8 @@ class TestMailComposer(MailCommon, TestRecipients): @classmethod def setUpClass(cls): super(TestMailComposer, cls).setUpClass() + + # force 'now' to ease test about schedulers cls.reference_now = FieldDatetime.from_string('2022-12-24 12:00:00') cls.env.cr._now = cls.reference_now # force create_date to check schedulers @@ -1184,6 +1186,72 @@ class TestComposerInternals(TestMailComposer): self.assertEqual(template.name, "%s: %s" % (self.env['ir.model']._get(self.test_record._name).name, 'Template Subject')) self.assertEqual(template.body_html, '

Template Body

', 'email_template incorrect body_html') + @users('erp_manager') + @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') + def test_mail_composer_wtpl_populate_new_recipients_mc(self): + """ Test auto-populate of auto created partner with related record + values when sending a mail with a template, in multi-company environment. + """ + companies = self.company_admin + self.company_2 + test_records = self.env['mail.test.ticket.mc'].create([ + { + 'email_from': f'newpartner{idx}@example.com', + 'company_id': companies[idx].id, + 'customer_id': False, + 'mobile_number': f'+3319900{idx:02d}{idx:02d}', + 'name': f'TestRecord{idx}', + 'phone_number': False, + 'user_id': False, + } for idx in range(2) + ]) + manual_recipients = test_records.mapped('email_from') + template = self.env['mail.template'].create({ + 'email_to': '{{ object.email_from }}', + 'model_id': self.env['ir.model']._get_id(test_records._name), + 'partner_to': False, + }) + + for composition_mode, batch_mode in product( + ('comment', 'mass_mail'), + (True, False) + ): + with self.subTest(composition_mode=composition_mode, batch_mode=batch_mode): + test_records = test_records if batch_mode else test_records[0] + + self.assertFalse( + self.env['res.partner'].search( + [('email_normalized', 'in', manual_recipients)] + ) + ) + ctx = { + 'default_composition_mode': composition_mode, + 'default_model': test_records._name, + 'default_res_ids': test_records.ids, + } + composer = self.env['mail.compose.message'].with_context(ctx).create({ + 'template_id': template.id, + }) + + with self.mock_mail_gateway(): + composer._action_send_mail() + + new_partners = self.env['res.partner'].search([('email_normalized', 'in', manual_recipients)], + order='email') + try: + self.assertEqual( + len(new_partners), len(test_records) + ) + self.assertEqual( + new_partners.mapped('company_id'), + test_records.mapped('company_id') + ) + self.assertEqual( + new_partners.mapped('mobile'), + test_records.mapped('mobile_number') + ) + finally: + new_partners.unlink() + @tagged('mail_composer', 'multi_lang') class TestComposerResultsComment(TestMailComposer, CronMixinCase): diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py index f4989fe8da8..344ebbfbf7e 100644 --- a/addons/test_mail/tests/test_mail_gateway.py +++ b/addons/test_mail/tests/test_mail_gateway.py @@ -228,7 +228,7 @@ class TestMailgateway(MailCommon): record = self.format_and_process(test_mail_data.MAIL_MULTIPART_IMAGE, self.email_from, f'groups@{self.alias_domain}') message = record.message_ids[0] for attachment in message.attachment_ids: - self.assertIn('/web/image/%s' % attachment.id, message.body) + self.assertIn(f'/web/image/{attachment.id}', message.body) self.assertEqual( set(message.attachment_ids.mapped('name')), set(['rosaçée.gif', 'verte!µ.gif', 'orangée.gif'])) @@ -323,7 +323,7 @@ class TestMailgateway(MailCommon): self.assertNotSentEmail() # No notification / bounce should be sent # Email recognized if partner has a formatted email - self.partner_1.write({'email': '"Valid Lelitre" <%s>' % self.partner_1.email}) + self.partner_1.write({'email': f'"Valid Lelitre" <{self.partner_1.email}>'}) record = self.format_and_process(MAIL_TEMPLATE, self.partner_1.email, f'groups@{self.alias_domain}', subject='Test2') self.assertEqual(record.message_ids[0].author_id, self.partner_1, @@ -339,7 +339,7 @@ class TestMailgateway(MailCommon): self.partner_1.write({'email': f'{test_email}, "Valid Lelitre" '}) with self.mock_mail_gateway(): record = self.format_and_process( - MAIL_TEMPLATE, f'"Valid Lelitre" <{test_email}>', 'groups@test.com', subject='Test3') + MAIL_TEMPLATE, f'"Valid Lelitre" <{test_email}>', f'groups@{self.alias_domain}', subject='Test3') self.assertEqual(record.message_ids[0].author_id, self.partner_1, 'message_process: found author based on first found email normalized, even with multi emails') @@ -349,7 +349,7 @@ class TestMailgateway(MailCommon): # Email not recognized if partner has a multi-email (source = std email) with self.mock_mail_gateway(): record = self.format_and_process( - MAIL_TEMPLATE, test_email, 'groups@test.com', subject='Test4') + MAIL_TEMPLATE, test_email, f'groups@{self.alias_domain}', subject='Test4') self.assertEqual(record.message_ids[0].author_id, self.partner_1, 'message_process: found author based on first found email normalized, even with multi emails') @@ -394,18 +394,18 @@ class TestMailgateway(MailCommon): self.assertFalse(record.message_ids[0].author_id) self.assertEqual(record.message_ids[0].email_from, from_1.email_formatted) - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') + @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_route_alias_owner_author_notify(self): """ Make sure users are notified when a reply is sent to an alias address. Alias owner should impact the message creator, but not notifications. """ test_record = self.env['mail.test.ticket'].create({}) author_partner = self.env['res.partner'].create({ 'name': 'Author', - 'email': 'author-partner@test.com', + 'email': f'author-partner@{self.alias_domain}', }) message = self.env['mail.message'].create({ 'body': '

test

', - 'email_from': 'author-partner@test.com', # email sent by author who also has an alias with their email + 'email_from': f'author-partner@{self.alias_domain}', # email sent by author who also has an alias with their email 'message_type': 'email', 'model': test_record._name, 'res_id': test_record.id, @@ -423,7 +423,7 @@ class TestMailgateway(MailCommon): self.assertFalse(self.user_root.active, 'notification logic relies on odoobot being archived') test_users = [self.user_employee, self.user_root] - email_tos = ['author-partner@test.com', 'some_non_aliased_email@test.com'] + email_tos = [f'author-partner@{self.alias_domain}', f'some_non_aliased_email@{self.alias_domain}'] for email_to, test_user in itertools.product(email_tos, test_users): with self.subTest(test_user=test_user, email_to=email_to): with self.mock_mail_gateway(), self.mock_mail_app(): @@ -487,19 +487,19 @@ class TestMailgateway(MailCommon): self.assertSentEmail( f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', ['whatever-2a840@postmaster.twitter.com'], - body_content='

Dear Sender,

The message below could not be accepted by the address %s' % self.alias.display_name.lower() + body_content=f'

Dear Sender,

The message below could not be accepted by the address {self.alias.display_name.lower()}', ) @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') def test_message_process_alias_config_bounced_to(self): """ Check bounce message contains the bouncing alias, not a generic "to" """ self.alias.write({'alias_contact': 'partners'}) - bounce_message_with_alias = '

Dear Sender,

The message below could not be accepted by the address %s' % self.alias.display_name.lower() + bounce_message_with_alias = f'

Dear Sender,

The message below could not be accepted by the address {self.alias.display_name.lower()}' # Bounce is To with self.mock_mail_gateway(): self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'groups@example.com', + MAIL_TEMPLATE, self.email_from, f'groups@{self.alias_domain}', cc='other@gmail.com', subject='Should Bounce') self.assertIn(bounce_message_with_alias, self._mails[0].get('body')) @@ -507,17 +507,17 @@ class TestMailgateway(MailCommon): with self.mock_mail_gateway(): self.format_and_process( MAIL_TEMPLATE, self.email_from, 'other@gmail.com', - cc='groups@example.com', subject='Should Bounce') + cc=f'groups@{self.alias_domain}', subject='Should Bounce') self.assertIn(bounce_message_with_alias, self._mails[0].get('body')) # Bounce is part of To with self.mock_mail_gateway(): self.format_and_process( - MAIL_TEMPLATE, self.email_from, 'other@gmail.com, groups@example.com', + MAIL_TEMPLATE, self.email_from, f'other@gmail.com, groups@{self.alias_domain}', subject='Should Bounce') self.assertIn(bounce_message_with_alias, self._mails[0].get('body')) - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models', 'odoo.sql_db') + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.addons.mail.models.mail_mail', 'odoo.models', 'odoo.sql_db') def test_message_process_alias_config_invalid_defaults(self): """Sending a mail to a misconfigured alias must change its status to invalid + notify sender and responsible.""" test_model_track = self.env['ir.model']._get('mail.test.track') @@ -706,7 +706,7 @@ class TestMailgateway(MailCommon): with self.subTest(partner_email=partner_email): self.partner_1.write({'email': partner_email}) record = self.format_and_process( - MAIL_TEMPLATE, email_from, 'groups@test.com', + MAIL_TEMPLATE, email_from, f'groups@{self.alias_domain}', subject=f'Test for {partner_email}') if passed: @@ -765,7 +765,10 @@ class TestMailgateway(MailCommon): self.assertEqual(record.message_ids[0].create_uid, self.user_root) self.assertEqual(record.message_ids[0].author_id, self.user_employee.partner_id) - record = self.format_and_process(MAIL_TEMPLATE, 'Another name <%s>' % self.user_employee.email, f'groups@{self.alias_domain}', subject='Email OtherName') + record = self.format_and_process( + MAIL_TEMPLATE, f'Another name <{self.user_employee.email}>', + f'groups@{self.alias_domain}', + subject='Email OtherName') self.assertEqual(record.create_uid, self.user_employee) self.assertEqual(record.message_ids[0].subject, 'Email OtherName') self.assertEqual(record.message_ids[0].create_uid, self.user_root) @@ -828,9 +831,9 @@ class TestMailgateway(MailCommon): }) new_rec = self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, - '%s@%s, %s@%s' % (new_alias_2.alias_name, self.alias_domain, self.alias.alias_name, self.alias_domain), + f'{new_alias_2.display_name}, {self.alias.display_name}', subject='Test Subject', - extra='In-Reply-To:\r\n\t%s\n' % self.fake_email.message_id, + extra=f'In-Reply-To:\r\n\t{self.fake_email.message_id}\n', target_model=new_alias_2.alias_model_id.model ) # Forward created a new record in mail.test @@ -855,9 +858,9 @@ class TestMailgateway(MailCommon): }) new_rec = self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, - '%s@%s, %s@%s' % (self.alias.alias_name, self.alias_domain, new_alias_2.alias_name, self.alias_domain), + f'{self.alias.display_name}, {new_alias_2.display_name}', subject='Test Subject', - extra='In-Reply-To:\r\n\t%s\n' % self.fake_email.message_id, + extra=f'In-Reply-To:\r\n\t{self.fake_email.message_id}\n', target_model=new_alias_2.alias_model_id.model ) # Forward created a new record in mail.test @@ -885,9 +888,9 @@ class TestMailgateway(MailCommon): }) new_rec = self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, - '%s@%s, %s@%s' % (new_alias_2.alias_name, self.alias_domain, self.alias.alias_name, self.alias_domain), + f'{new_alias_2.display_name}, {self.alias.display_name}', subject='Test Subject', - extra='In-Reply-To:\r\n\t%s\n' % self.fake_email.message_id, + extra=f'In-Reply-To:\r\n\t{self.fake_email.message_id}\n', target_model=new_alias_2.alias_model_id.model ) # Forward created a new record in mail.test @@ -911,7 +914,7 @@ class TestMailgateway(MailCommon): }) new_rec = self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, - '%s@%s, %s@%s' % (self.alias.alias_name, self.alias_domain, new_alias_2.alias_name, self.alias_domain), + f'{self.alias.display_name}, {new_alias_2.display_name}', subject='Test Subject', target_model=new_alias_2.alias_model_id.model ) @@ -923,44 +926,43 @@ class TestMailgateway(MailCommon): @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models') def test_message_route_alias_with_allowed_domains(self): - """ Incoming email: check that if domains are set in the - optional system parameter `mail.catchall.domain.allowed`, - only incoming emails from these domains will generate records.""" - + """ Incoming email: check that if domains are set in the optional system + parameter `mail.catchall.domain.allowed` only incoming emails from these + domains will generate records.""" MailTestGatewayModel = self.env['mail.test.gateway'] MailTestContainerModel = self.env['mail.test.container'] - allowed_domain = 'hello.com' - not_allowed_domain = 'bonjour.com' - # test@.. will cause the creation of new mail.test new_alias_2 = self.env['mail.alias'].create({ + 'alias_contact': 'everyone', + 'alias_model_id': self.env['ir.model']._get('mail.test.container').id, 'alias_name': 'test', 'alias_user_id': False, - 'alias_model_id': self.env['ir.model']._get('mail.test.container').id, - 'alias_contact': 'everyone', }) - for subject, gateway_created, container_created, alias2_domain, sys_param in [ - # Test with 'mail.catchall.domain.allowed' not set in system parameters - # and with a domain not allowed - ('Test Subject 1', True, True, not_allowed_domain, ""), - # Test with 'mail.catchall.domain.allowed' set in system parameters - # and with a domain not allowed - ('Test Subject 2', True, False, not_allowed_domain, allowed_domain), - # Test with 'mail.catchall.domain.allowed' set in system parameters - # and with a domain allowed - ('Test Subject 3', True, True, allowed_domain, allowed_domain), - ]: - with self.subTest(subject=subject, gateway_created=gateway_created, - container_created=container_created, alias2_domain=alias2_domain, - sys_param=sys_param): - self.env['ir.config_parameter'].set_param('mail.catchall.domain.allowed', sys_param) + allowed_domain = 'hello.com' + for (alias_right_part, allowed_domain), (gateway_created, container_created) in zip( + [ + # Test with 'mail.catchall.domain.allowed' not set in system parameters + # and with a domain not allowed + ('bonjour.com', ""), + # Test with 'mail.catchall.domain.allowed' set in system parameters + # and with a domain not allowed + ('bonjour.com', allowed_domain), + # Test with 'mail.catchall.domain.allowed' set in system parameters + # and with a domain allowed + (allowed_domain, allowed_domain), + ], [ + (True, True), + (True, False), + (True, True), + ] + ): + with self.subTest(alias_right_part=alias_right_part, allowed_domain=allowed_domain): + self.env['ir.config_parameter'].set_param('mail.catchall.domain.allowed', allowed_domain) - email_to = '%s@%s, %s@%s' % ( - self.alias.alias_name, self.alias_domain, - new_alias_2.alias_name, alias2_domain, - ) + subject = f'Test wigh {alias_right_part}-{allowed_domain}' + email_to = f'{self.alias.alias_name}@{self.alias_domain}, {new_alias_2.alias_name}@{alias_right_part}' self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, email_to, @@ -970,16 +972,8 @@ class TestMailgateway(MailCommon): res_alias_1 = MailTestGatewayModel.search([('name', '=', subject)]) res_alias_2 = MailTestContainerModel.search([('name', '=', subject)]) - self.assertEqual( - bool(res_alias_1), gateway_created, - 'message_process (%s): a new mail.test.gateway %s have been created' % - (subject, 'should' if gateway_created else "should not") - ) - self.assertEqual( - bool(res_alias_2), container_created, - 'message_process (%s): a new mail.test.container %s have been created' % - (subject, 'should' if container_created else "should not") - ) + self.assertEqual(bool(res_alias_1), gateway_created) + self.assertEqual(bool(res_alias_2), container_created) # -------------------------------------------------- # Email Management @@ -991,7 +985,7 @@ class TestMailgateway(MailCommon): with self.mock_mail_gateway(): new_recs = self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, - '%s@%s' % (self.alias_bounce, self.alias_domain), + f'{self.alias_bounce}@{self.alias_domain}', subject='Should bounce', ) self.assertFalse(new_recs) @@ -1003,10 +997,7 @@ class TestMailgateway(MailCommon): with self.mock_mail_gateway(): new_recs = self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, - '%s@%s, %s@%s' % ( - self.alias.alias_name, self.alias_domain, - self.alias_bounce, self.alias_domain - ), + f'{self.alias.alias_name}@{self.alias_domain}, {self.alias_bounce}@{self.alias_domain}', subject='Should bounce', ) self.assertFalse(new_recs) @@ -1019,11 +1010,11 @@ class TestMailgateway(MailCommon): with self.mock_mail_gateway(): record = self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, - '"My Super Catchall" <%s@%s>' % (self.alias_catchall, self.alias_domain), + f'"My Super Catchall" <{self.alias_catchall}@{self.alias_domain}', subject='Should Bounce') self.assertFalse(record) self.assertSentEmail( - f'"MAILER-DAEMON" <{self.alias_bounce}@{self.alias_domain}>', + self.mailer_daemon_email, ['whatever-2a840@postmaster.twitter.com'], subject='Re: Should Bounce' ) @@ -1035,7 +1026,7 @@ class TestMailgateway(MailCommon): with self.mock_mail_gateway(): record = self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, - '%s@%s, %s@%s' % (self.alias_catchall, self.alias_domain, self.alias.alias_name, self.alias_domain), + f'{self.alias_catchall}@{self.alias_domain}, {self.alias.alias_name}@{self.alias_domain}', subject='Catchall Not Blocking' ) # Test: one group created @@ -1050,7 +1041,7 @@ class TestMailgateway(MailCommon): with self.mock_mail_gateway(): record = self.format_and_process( MAIL_TEMPLATE, self.partner_1.email_formatted, - '%s@%s, %s@%s' % (self.alias.alias_name, self.alias_domain, self.alias_catchall, self.alias_domain), + f'{self.alias.alias_name}@{self.alias_domain}, {self.alias_catchall}@{self.alias_domain}', subject='Catchall Not Blocking' ) # Test: one group created @@ -1187,10 +1178,10 @@ class TestMailgateway(MailCommon): def test_message_process_bounce_records_channel(self): """ Test blacklist allow to multi-bounce and auto update of discuss.channel """ other_record = self.env['mail.test.gateway'].create({ - 'email_from': 'Another name <%s>' % self.partner_1.email + 'email_from': f'Another name <{self.partner_1.email}>' }) yet_other_record = self.env['mail.test.gateway'].create({ - 'email_from': 'Yet Another name <%s>' % self.partner_1.email.upper() + 'email_from': f'Yet Another name <{self.partner_1.email.upper()}>' }) test_channel = self.env['discuss.channel'].create({ 'name': 'Test', @@ -1207,7 +1198,11 @@ class TestMailgateway(MailCommon): extra = self.fake_email.message_id for i in range(10): - record = self.format_and_process(test_mail_data.MAIL_BOUNCE, 'A third name <%s>' % self.partner_1.email, f'groups@{self.alias_domain}', subject='Undelivered Mail Returned to Sender', extra=extra) + record = self.format_and_process( + test_mail_data.MAIL_BOUNCE, f'A third name <{self.partner_1.email}>', + f'groups@{self.alias_domain}', + subject='Undelivered Mail Returned to Sender', + extra=extra) self.assertFalse(record) self.assertEqual(self.partner_1.message_bounce, 10) self.assertEqual(self.test_record.message_bounce, 0) @@ -1234,7 +1229,7 @@ class TestMailgateway(MailCommon): # Thread formation # -------------------------------------------------- - @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') + @mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink', 'odoo.addons.mail.models.mail_mail', 'odoo.tests') def test_message_process_external_notification_reply(self): """Ensure responses bot messages are discussions.""" bot_notification_message = self._create_gateway_message( @@ -1282,7 +1277,7 @@ class TestMailgateway(MailCommon): init_msg_count = len(self.test_record.message_ids) self.format_and_process( MAIL_TEMPLATE, 'valid.other@gmail.com', f'erroneous@{self.alias_domain}', - subject='Re: news', extra='In-Reply-To:\r\n\t%s\n' % self.fake_email.message_id) + subject='Re: news', extra=f'In-Reply-To:\r\n\t{self.fake_email.message_id}\n') self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) self.assertEqual(self.fake_email.child_ids, self.test_record.message_ids[0]) @@ -1293,7 +1288,7 @@ class TestMailgateway(MailCommon): init_msg_count = len(self.test_record.message_ids) self.format_and_process( MAIL_TEMPLATE, self.email_from, f'erroneous@{self.alias_domain}', - extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id) + extra=f'References: <2233@a.com>\r\n\t<3edss_dsa@b.com> {self.fake_email.message_id}') self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) self.assertEqual(self.fake_email.child_ids, self.test_record.message_ids[0]) @@ -1442,7 +1437,7 @@ class TestMailgateway(MailCommon): init_msg_count = len(self.test_record.message_ids) self.format_and_process( MAIL_TEMPLATE, self.email_from, f'erroneous@{self.alias_domain}', - extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id) + extra=f'References: <2233@a.com>\r\n\t<3edss_dsa@b.com> {self.fake_email.message_id}') self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) self.assertEqual(self.fake_email.child_ids, self.test_record.message_ids[0]) @@ -1463,7 +1458,7 @@ class TestMailgateway(MailCommon): init_msg_count = len(self.test_record.message_ids) self.format_and_process( MAIL_TEMPLATE, self.email_from, f'erroneous@{self.alias_domain}', - extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % buggy_message_id) + extra=f'References: <2233@a.com>\r\n\t<3edss_dsa@b.com> {buggy_message_id}') self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) self.assertEqual(self.fake_email.child_ids, self.test_record.message_ids[0]) @@ -1480,7 +1475,8 @@ class TestMailgateway(MailCommon): init_msg_count = len(self.test_record.message_ids) res_test = self.format_and_process( MAIL_TEMPLATE, self.email_from, f'test.alias@{self.alias_domain}', - subject='My Dear Forward', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, + subject='My Dear Forward', + extra=f'References: <2233@a.com>\r\n\t<3edss_dsa@b.com> {self.fake_email.message_id}', target_model='mail.test.container') self.assertEqual(len(self.test_record.message_ids), init_msg_count) @@ -1500,7 +1496,8 @@ class TestMailgateway(MailCommon): init_msg_count = len(self.test_record.message_ids) res_test = self.format_and_process( MAIL_TEMPLATE, self.email_from, f'test.alias@{self.alias_domain}', - subject='My Dear Forward', extra='References: <2233@a.com>\r\n\t<3edss_dsa@b.com> %s' % self.fake_email.message_id, + subject='My Dear Forward', + extra=f'References: <2233@a.com>\r\n\t<3edss_dsa@b.com> {self.fake_email.message_id}', target_model='mail.test.container') self.assertEqual(len(self.test_record.message_ids), init_msg_count + 1) @@ -1555,7 +1552,7 @@ class TestMailgateway(MailCommon): msgID = '' res_test = self.format_and_process( MAIL_TEMPLATE, self.email_from, record_msg.reply_to, cc='', - subject='Re: Replies to Record', extra='In-Reply-To: %s' % record_msg.message_id, + subject='Re: Replies to Record', extra=f'In-Reply-To: {record_msg.message_id}', msg_id=msgID, target_model='mail.test.simple') incoming_msg = self.env['mail.message'].search([('message_id', '=', msgID)]) self.assertFalse(res_test) @@ -1567,7 +1564,7 @@ class TestMailgateway(MailCommon): msgID = '' res_test = self.format_and_process( MAIL_TEMPLATE, self.email_from, mail_msg.reply_to, cc='', - subject='Re: Replies to Record', extra='In-Reply-To: %s' % mail_msg.message_id, + subject='Re: Replies to Record', extra=f'In-Reply-To: {mail_msg.message_id}', msg_id=msgID, target_model='mail.test.gateway') incoming_msg = self.env['mail.message'].search([('message_id', '=', msgID)]) self.assertEqual(len(res_test), 1) @@ -1592,7 +1589,7 @@ class TestMailgateway(MailCommon): }) record = self.format_and_process( test_mail_data.MAIL_TEMPLATE_EXTRA_HTML, self.email_from, - '%s@%s' % (alias.alias_name, self.alias_catchall), + f'{alias.alias_name}@{self.alias_domain}', subject='base64 image to alias', target_model=target_model, extra_html='', @@ -1615,7 +1612,7 @@ class TestMailgateway(MailCommon): }) record = self.format_and_process( test_mail_data.MAIL_TEMPLATE_EXTRA_HTML, self.email_from, - '%s@%s' % (alias.alias_name, self.alias_catchall), + f'{alias.alias_name}@{self.alias_domain}', subject='base64 image to alias', target_model=target_model, extra_html='', @@ -1635,7 +1632,7 @@ class TestMailgateway(MailCommon): self.assertRaises(ValueError, self.format_and_process, MAIL_TEMPLATE, self.partner_1.email_formatted, f'noone@{self.alias_domain}', subject='spam', - extra='In-Reply-To: <12321321-openerp-%d-mail.test.gateway@%s>' % (self.test_record.id, socket.gethostname())) + extra=f'In-Reply-To: <12321321-openerp-{self.test_record.id}-{self.test_record._name}@{socket.gethostname()}>') # when 6.1 messages are present, compat mode is available # Odoo 10 update: compat mode has been removed and should not work anymore @@ -1645,7 +1642,7 @@ class TestMailgateway(MailCommon): ValueError, self.format_and_process, MAIL_TEMPLATE, self.partner_1.email_formatted, f'noone@{self.alias_domain}>', subject='spam', - extra='In-Reply-To: <12321321-openerp-%d-mail.test.gateway@%s>' % (self.test_record.id, socket.gethostname())) + extra=f'In-Reply-To: <12321321-openerp-{self.test_record.id}-mail.test.gateway@{socket.gethostname()}>') # Test created messages self.assertEqual(len(self.test_record.message_ids), 1) @@ -1707,8 +1704,8 @@ class TestMailgateway(MailCommon): file_content_b64 = base64.b64encode(file_content.encode(encoding or 'utf-8')).decode() record = self.format_and_process(test_mail_data.MAIL_FILE_ENCODING, self.email_from, f'groups@{self.alias_domain}', - subject='Test Charset %s' % encoding or 'Unset', - charset='; charset="%s"' % encoding if encoding else '', + subject=f'Test Charset {encoding or "Unset"}', + charset=f'; charset="{encoding}"' if encoding else '', content=file_content_b64 ) attachment = record.message_ids.attachment_ids @@ -1835,6 +1832,7 @@ class TestMailgateway(MailCommon): # This explains the multiple "�" in the attachment. self.assertIn("Chauss������e de Bruxelles", record.message_ids.attachment_ids.raw.decode()) + @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_process_file_omitted_charset(self): """ For incoming email containing an xml attachment with omitted charset and containing an UTF8 payload we should parse the attachment using UTF-8. @@ -1843,6 +1841,7 @@ class TestMailgateway(MailCommon): self.assertEqual(record.message_ids.attachment_ids.name, 'bis3.xml') self.assertEqual("Chaussée de Bruxelles", record.message_ids.attachment_ids.raw.decode()) + @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_route_reply_model_none(self): """ Test the message routing and reply functionality when the model is None. @@ -1886,10 +1885,11 @@ class TestMailThreadCC(MailCommon): cls.email_from = 'Sylvie Lelitre ' cls.alias = cls.env['mail.alias'].create({ + 'alias_contact': 'everyone', + 'alias_model_id': cls.env['ir.model']._get('mail.test.cc').id, 'alias_name': 'cc_record', 'alias_user_id': False, - 'alias_model_id': cls.env['ir.model']._get('mail.test.cc').id, - 'alias_contact': 'everyone'}) + }) @mute_logger('odoo.addons.mail.models.mail_thread') def test_message_cc_new(self): diff --git a/addons/test_mail/tests/test_mail_multicompany.py b/addons/test_mail/tests/test_mail_multicompany.py index 78819c2658b..eed9d754abf 100644 --- a/addons/test_mail/tests/test_mail_multicompany.py +++ b/addons/test_mail/tests/test_mail_multicompany.py @@ -13,7 +13,7 @@ from odoo.addons.mail.tests.common import MailCommon from odoo.addons.test_mail.tests.common import TestRecipients from odoo.exceptions import AccessError from odoo.tests import tagged, users, HttpCase -from odoo.tools import formataddr, mute_logger +from odoo.tools import mute_logger class TestMailMCCommon(MailCommon, TestRecipients): @@ -69,132 +69,6 @@ class TestMailMCCommon(MailCommon, TestRecipients): @tagged('multi_company') class TestMultiCompanySetup(TestMailMCCommon): - @users('erp_manager') - @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') - def test_mail_composer_wtpl_populate_new_recipient(self): - """ Test auto-populate of auto created partner with related record - values when sending a mail with a template. - """ - companies = self.company_admin + self.company_2 - test_records = self.env['mail.test.ticket.mc'].create([ - { - 'email_from': f'newpartner{idx}@example.com', - 'company_id': companies[idx].id, - 'customer_id': False, - 'mobile_number': f'+3319900{idx:02d}{idx:02d}', - 'name': f'TestRecord{idx}', - 'phone_number': False, - 'user_id': False, - } for idx in range(2) - ]) - manual_recipients = test_records.mapped('email_from') - template = self.env['mail.template'].create({ - 'email_to': '{{ object.email_from }}', - 'model_id': self.env['ir.model']._get_id(test_records._name), - 'partner_to': False, - }) - - for composition_mode, batch_mode in product( - ('comment', 'mass_mail'), - (True, False) - ): - with self.subTest(composition_mode=composition_mode, batch_mode=batch_mode): - test_records = test_records if batch_mode else test_records[0] - - self.assertFalse( - self.env['res.partner'].search( - [('email_normalized', 'in', manual_recipients)] - ) - ) - ctx = { - 'default_composition_mode': composition_mode, - 'default_model': test_records._name, - 'default_res_ids': test_records.ids, - } - composer = self.env['mail.compose.message'].with_context(ctx).create({ - 'template_id': template.id, - }) - - with self.mock_mail_gateway(): - composer._action_send_mail() - - new_partners = self.env['res.partner'].search([('email_normalized', 'in', manual_recipients)], - order='email') - try: - self.assertEqual( - len(new_partners), len(test_records) - ) - self.assertEqual( - new_partners.mapped('company_id'), - test_records.mapped('company_id') - ) - self.assertEqual( - new_partners.mapped('mobile'), - test_records.mapped('mobile_number') - ) - finally: - new_partners.unlink() - - @users('employee') - def test_notify_reply_to_computation(self): - test_record = self.env['mail.test.gateway'].browse(self.test_record.ids) - res = test_record._notify_get_reply_to() - self.assertEqual( - res[test_record.id], - formataddr(( - "%s %s" % (self.user_employee.company_id.name, test_record.name), - "%s@%s" % (self.alias_catchall, self.alias_domain))) - ) - - @users('employee_c2') - def test_notify_reply_to_computation_mc(self): - """ Test reply-to computation in multi company mode. Add notably tests - depending on user and records company_id / company_ids. """ - - # Test1: no company_id field - test_record = self.env['mail.test.gateway'].browse(self.test_record.ids) - res = test_record._notify_get_reply_to() - self.assertEqual( - res[test_record.id], - formataddr(( - "%s %s" % (self.user_employee_c2.company_id.name, test_record.name), - "%s@%s" % (self.alias_catchall, self.alias_domain))) - ) - - # Test2: MC environment get default value from env - self.user_employee_c2.write({'company_ids': [(4, self.user_employee.company_id.id)]}) - test_records = self.env['mail.test.multi.company'].create([ - {'name': 'Test', - 'company_id': self.user_employee.company_id.id}, - {'name': 'Test', - 'company_id': self.user_employee_c2.company_id.id}, - ]) - res = test_records._notify_get_reply_to() - for test_record in test_records: - self.assertEqual( - res[test_record.id], - formataddr(( - "%s %s" % (self.user_employee_c2.company_id.name, test_record.name), - "%s@%s" % (self.alias_catchall, self.alias_domain))) - ) - - # Test3: get company from record (company_id field) - self.user_employee_c2.write({'company_ids': [(4, self.company_3.id)]}) - test_records = self.env['mail.test.multi.company'].create([ - {'name': 'Test1', - 'company_id': self.company_3.id}, - {'name': 'Test2', - 'company_id': self.company_3.id}, - ]) - res = test_records._notify_get_reply_to() - for test_record in test_records: - self.assertEqual( - res[test_record.id], - formataddr(( - "%s %s" % (self.company_3.name, test_record.name), - "%s@%s" % (self.alias_catchall, self.alias_domain))) - ) - @users('employee_c2') @mute_logger('odoo.addons.base.models.ir_rule') def test_post_with_read_access(self): diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py index c6246e29b00..c0f80e566c9 100644 --- a/addons/test_mail/tests/test_message_post.py +++ b/addons/test_mail/tests/test_message_post.py @@ -233,9 +233,9 @@ class TestMailNotifyAPI(TestMessagePostCommon): # employee: access button and link self.assertTrue(emp_info['has_button_access']) for param, value in link_vals.items(): - self.assertIn('%s=%s' % (param, value), emp_info['button_access']['url']) - self.assertIn('model=%s' % base_record._name, emp_info['button_access']['url']) - self.assertIn('res_id=%s' % base_record.id, emp_info['button_access']['url']) + self.assertIn(f'{param}={value}', emp_info['button_access']['url']) + self.assertIn(f'model={base_record._name}', emp_info['button_access']['url']) + self.assertIn(f'res_id={base_record.id}', emp_info['button_access']['url']) self.assertNotIn('body', emp_info['button_access']['url']) self.assertNotIn('subject', emp_info['button_access']['url']) @@ -284,6 +284,61 @@ class TestMailNotifyAPI(TestMessagePostCommon): self.assertFalse(partner_info['has_button_access']) self.assertFalse(emp_info['has_button_access']) + @users('employee_c2') + def test_notify_reply_to_computation_mc(self): + """ Test reply-to computation in multi company mode. Add notably tests + depending on user and records company_id / company_ids. """ + + # Test1: no company_id field: depends on current user browsing + test_record = self.test_record.with_env(self.env) + self.assertEqual( + test_record._notify_get_reply_to()[test_record.id], + formataddr(( + f"{self.user_employee_c2.company_id.name} {test_record.name}", + f"{self.alias_catchall}@{self.alias_domain}")) + ) + test_record_c1 = test_record.with_user(self.user_employee) + self.assertEqual( + test_record_c1._notify_get_reply_to()[test_record_c1.id], + formataddr(( + f"{self.user_employee.company_id.name} {test_record_c1.name}", + f"{self.alias_catchall}@{self.alias_domain}")) + ) + + # Test2: MC environment get default value from env + self.user_employee_c2.write({'company_ids': [(4, self.user_employee.company_id.id)]}) + test_records = self.env['mail.test.multi.company'].create([ + {'name': 'Test', + 'company_id': self.user_employee.company_id.id}, + {'name': 'Test', + 'company_id': self.user_employee_c2.company_id.id}, + ]) + res = test_records._notify_get_reply_to() + for test_record in test_records: + self.assertEqual( + res[test_record.id], + formataddr(( + f"{self.user_employee_c2.company_id.name} {test_record.name}", + f"{self.alias_catchall}@{self.alias_domain}")) + ) + + # Test3: get company from record (company_id field) + self.user_employee_c2.write({'company_ids': [(4, self.company_3.id)]}) + test_records = self.env['mail.test.multi.company'].create([ + {'name': 'Test1', + 'company_id': self.company_3.id}, + {'name': 'Test2', + 'company_id': self.company_3.id}, + ]) + res = test_records._notify_get_reply_to() + for test_record in test_records: + self.assertEqual( + res[test_record.id], + formataddr(( + f"{self.company_3.name} {test_record.name}", + f"{self.alias_catchall}@{self.alias_domain}")) + ) + @tagged('mail_post', 'mail_notify') class TestMessageNotify(TestMessagePostCommon): @@ -331,8 +386,8 @@ class TestMessageNotify(TestMessagePostCommon): self.assertTrue('model=' in admin_mail_body, 'The email sent to admin should contain an access link') admin_access_link = admin_mail_body[ admin_mail_body.index('model='):admin_mail_body.index('/>', admin_mail_body.index('model=')) - 1] - self.assertIn('model=%s' % self.test_record._name, admin_access_link, 'The access link should contain a valid model argument') - self.assertIn('res_id=%d' % self.test_record.id, admin_access_link, 'The access link should contain a valid res_id argument') + self.assertIn(f'model={self.test_record._name}', admin_access_link, 'The access link should contain a valid model argument') + self.assertIn(f'res_id={self.test_record.id}', admin_access_link, 'The access link should contain a valid res_id argument') partner_mails = [x for x in self._mails if self.partner_1.name in x.get('email_to')[0]] self.assertEqual(len(partner_mails), 1, 'There should be exactly one email sent to partner') @@ -397,38 +452,7 @@ class TestMessageNotify(TestMessagePostCommon): @users('employee') @mute_logger('odoo.addons.mail.models.mail_mail') def test_notify_from_user_id(self): - """ Test notify coming from user_id assignment. """ - test_record = self.env['mail.test.track'].create({ - 'company_id': self.env.user.company_id.id, - 'email_from': self.env.user.email_formatted, - 'name': 'Test UserId Track', - 'user_id': False, - }) - self.flush_tracking() - - with self.mock_mail_gateway(), self.mock_mail_app(): - test_record.write({'user_id': self.user_employee_2.id}) - self.flush_tracking() - - self.assertEqual(len(self._new_msgs), 2, 'Should have 2 messages: tracking and assignment') - assign_notif = self._new_msgs.filtered(lambda msg: msg.message_type == 'user_notification') - self.assertTrue(assign_notif) - self.assertMessageFields( - assign_notif, - {'author_id': self.partner_employee, - 'email_from': formataddr((self.partner_employee.name, self.partner_employee.email_normalized)), - 'model': test_record._name, - 'notified_partner_ids': self.partner_employee_2, - 'res_id': test_record.id, - 'subtype_id': self.env.ref('mail.mt_note'), - } - ) - self.assertIn('Dear %s' % self.partner_employee_2.name, assign_notif.body) - - @users('employee') - @mute_logger('odoo.addons.mail.models.mail_mail') - def test_notify_from_user_id_batch(self): - """ Test notify coming from user_id assignment. """ + """ Test notify coming from user_id assignment (in batch) """ test_records, _ = self._create_records_for_batch( 'mail.test.track', 10, { 'company_id': self.env.user.company_id.id, @@ -682,7 +706,7 @@ class TestMessageLog(TestMessagePostCommon): 'is_internal': True, 'model': test_record._name, 'notified_partner_ids': self.env['res.partner'], - 'reply_to': formataddr((self.company_admin.name, '%s@%s' % (self.alias_catchall, self.alias_domain))), + 'reply_to': formataddr((self.company_admin.name, f'{self.alias_catchall}@{self.alias_domain}')), 'res_id': test_record.id, }, 'notif': [], @@ -718,7 +742,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase): 'message_type': 'comment', 'model': test_record._name, 'notified_partner_ids': self.partner_employee_2, - 'reply_to': formataddr(("%s %s" % (self.company_admin.name, test_record.name), '%s@%s' % (self.alias_catchall, self.alias_domain))), + 'reply_to': formataddr((f'{self.company_admin.name} {test_record.name}', f'{self.alias_catchall}@{self.alias_domain}')), 'res_id': test_record.id, 'subtype_id': self.env.ref('mail.mt_comment'), }, @@ -739,6 +763,11 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase): {'partner': self.partner_1, 'type': 'email'}], message_info={ 'content': 'NewBody', + 'email_values': { + 'headers': { + 'Return-Path': f'{self.alias_bounce}@{self.alias_domain}', + }, + }, 'message_values': { 'notified_partner_ids': self.partner_1 + self.partner_employee_2, }, @@ -916,6 +945,57 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase): # notifications emails should not have been deleted: one for customers, one for user self.assertEqual(self.env['mail.mail'].sudo().search_count([('mail_message_id', '=', msg.id)]), 2) + + @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink') + @users('erp_manager') + def test_message_post_mc(self): + """ Test posting in multi-company environment, notably with aliases """ + records = self.env['mail.test.ticket.mc'].create([ + { + 'name': 'No Specific Company', + }, { + 'company_id': self.company_admin.id, + 'name': 'Company1', + }, { + 'company_id': self.company_2.id, + 'name': 'Company2', + }, + ]) + expected_companies = [self.company_2, self.company_admin, self.company_2] + for record, expected_company in zip(records, expected_companies): + with self.subTest(record=record): + with self.assertSinglePostNotifications( + [{'partner': self.partner_employee_2, 'type': 'email'}], + message_info={ + 'content': 'Body', + 'email_values': { + 'headers': { + 'Return-Path': f'{self.alias_bounce}@{self.alias_domain}', + }, + }, + 'mail_mail_values': { + 'headers': repr({ + 'X-Odoo-Objects': f'{record._name}-{record.id}', + }), + }, + 'message_values': { + 'author_id': self.user_erp_manager.partner_id, + 'email_from': formataddr((self.user_erp_manager.name, self.user_erp_manager.email_normalized)), + 'is_internal': False, + 'notified_partner_ids': self.partner_employee_2, + 'reply_to': formataddr( + (f'{expected_company.name} {record.name}', f'{self.alias_catchall}@{self.alias_domain}') + ), + }, + } + ): + _new_message = record.message_post( + body='Body', + message_type='comment', + subtype_xmlid='mail.mt_comment', + partner_ids=[self.partner_employee_2.id], + ) + @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.tests') def test_message_post_recipients_email_field(self): """ Test various combinations of corner case / not standard filling of @@ -1278,7 +1358,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase): [self.partner_1], references_content='openerp-%d-mail.test.simple' % self.test_record.id, # references should be sorted from the oldest to the newest - references='%s %s' % (parent_msg.message_id, msg.message_id), + references=f'{parent_msg.message_id} {msg.message_id}', ) # post a reply to the reply: check parent is the first one @@ -1299,7 +1379,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase): reply_to=msg.reply_to, subject=self.test_record.name, references_content='openerp-%d-mail.test.simple' % self.test_record.id, - references='%s %s' % (parent_msg.message_id, new_msg.message_id), + references=f'{parent_msg.message_id} {new_msg.message_id}', ) @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread') @@ -1323,7 +1403,7 @@ class TestMessagePost(TestMessagePostCommon, CronMixinCase): self.format_and_process( MAIL_TEMPLATE_PLAINTEXT, self.user_admin.email, 'not_my_businesss@example.com', msg_id='<1198923581.41972151344608186800.JavaMail.diff1@agrolait.example.com>', - extra='In-Reply-To:\r\n\t%s\n' % msg.message_id, + extra=f'In-Reply-To:\r\n\t{msg.message_id}\n', target_model='mail.test.simple') reply = test_record.message_ids - msg self.assertTrue(reply) diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index 487ff5f0810..860bc5dd3b2 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -18,7 +18,7 @@ class BaseMailPerformance(MailCommon, TransactionCaseWithUserDemo): def setUpClass(cls): super(BaseMailPerformance, cls).setUpClass() - # creating partners is required notably witn template usage + # creating partners is required notably with template usage cls.user_employee.write({'groups_id': [(4, cls.env.ref('base.group_partner_manager').id)]}) cls.user_test = cls.user_test_inbox = cls.env['res.users'].with_context(cls._test_context).create({ 'name': 'Paulette Testouille',