[IMP] mail: store company/alias environment on mail.message
PURPOSE
Allow alias domains to be multiple, notably to be used in a multi company
environment where each company has its own alias domain.
SPECIFICATIONS
Store 'company_id' and 'alias_domain_id' on 'mail.message' model. It helps
knowing the environment that produced the message, notably
* for layouting: it will be used to improve company (and soon alias domain)
given for the email notification layout;
* for sending: it will be used to better compute mail-related values like
default from, return path, ...
When logging, those fields are kept false as anyway no notification is sent.
No need to fetch extra information.
Some sudo() are included in post_* methods, as portal may go through the
posting method, see 'test_portal_acls' in test_message_post.py file.
Task-36879 (Mail: Support Multi Domains Aliases)
Part-of: odoo/odoo#76734
This commit is contained in:
@@ -111,6 +111,8 @@ class Message(models.Model):
|
||||
model = fields.Char('Related Document Model')
|
||||
res_id = fields.Many2oneReference('Related Document ID', model_field='model')
|
||||
record_name = fields.Char('Message Record Name') # display_name of the related document
|
||||
record_alias_domain_id = fields.Many2one('mail.alias.domain', 'Alias Domain', ondelete='set null')
|
||||
record_company_id = fields.Many2one('res.company', 'Company', ondelete='set null')
|
||||
# characteristics
|
||||
message_type = fields.Selection([
|
||||
('email', 'Email'),
|
||||
|
||||
@@ -2123,6 +2123,10 @@ class MailThread(models.AbstractModel):
|
||||
'partner_ids': partner_ids,
|
||||
})
|
||||
# add default-like values afterwards, to avoid useless queries
|
||||
if 'record_alias_domain_id' not in msg_values:
|
||||
msg_values['record_alias_domain_id'] = self.sudo()._mail_get_alias_domains(default_company=self.env.company)[self.id].id
|
||||
if 'record_company_id' not in msg_values:
|
||||
msg_values['record_company_id'] = self._mail_get_companies(default=self.env.company)[self.id].id
|
||||
if 'reply_to' not in msg_values:
|
||||
msg_values['reply_to'] = self._notify_get_reply_to(default=email_from)[self.id]
|
||||
|
||||
@@ -2597,6 +2601,11 @@ class MailThread(models.AbstractModel):
|
||||
}
|
||||
msg_values.update(msg_kwargs)
|
||||
# add default-like values afterwards, to avoid useless queries
|
||||
if self:
|
||||
if 'record_alias_domain_id' not in msg_values:
|
||||
msg_values['record_alias_domain_id'] = self._mail_get_alias_domains(default_company=self.env.company)[self.id].id
|
||||
if 'record_company_id' not in msg_values:
|
||||
msg_values['record_company_id'] = self._mail_get_companies(default=self.env.company)[self.id].id
|
||||
if 'reply_to' not in msg_values:
|
||||
msg_values['reply_to'] = self._notify_get_reply_to(default=email_from)[self.id if self else False]
|
||||
|
||||
@@ -2689,6 +2698,8 @@ class MailThread(models.AbstractModel):
|
||||
'email_from': email_from,
|
||||
# document
|
||||
'model': self._name,
|
||||
'record_alias_domain_id': False,
|
||||
'record_company_id': False,
|
||||
'record_name': False,
|
||||
# content
|
||||
'attachment_ids': attachment_ids,
|
||||
@@ -2826,6 +2837,8 @@ class MailThread(models.AbstractModel):
|
||||
'model',
|
||||
'parent_id',
|
||||
'partner_ids',
|
||||
'record_alias_domain_id',
|
||||
'record_company_id',
|
||||
'record_name',
|
||||
'reply_to',
|
||||
'reply_to_force_new',
|
||||
|
||||
@@ -121,6 +121,12 @@ class MailComposer(models.TransientModel):
|
||||
res_domain_user_id = fields.Many2one(
|
||||
'res.users', string='Responsible',
|
||||
help='Used as context used to evaluate composer domain')
|
||||
record_alias_domain_id = fields.Many2one(
|
||||
'mail.alias.domain', 'Alias Domain',
|
||||
compute='_compute_record_environment', readonly=False, store=True) # useful only in monorecord comment mode
|
||||
record_company_id = fields.Many2one(
|
||||
'res.company', 'Company',
|
||||
compute='_compute_record_environment', readonly=False, store=True) # useful only in monorecord comment mode
|
||||
record_name = fields.Char(
|
||||
'Record Name',
|
||||
compute='_compute_record_name', readonly=False, store=True) # useful only in monorecord comment mode
|
||||
@@ -387,6 +393,34 @@ class MailComposer(models.TransientModel):
|
||||
elif not active_res_ids and self.env.context.get('active_id'):
|
||||
composer.res_ids = f"{[self.env.context['active_id']]}"
|
||||
|
||||
@api.depends('composition_mode', 'model', 'res_domain', 'res_ids')
|
||||
def _compute_record_environment(self):
|
||||
""" In monorecord mode, fetch record company and the linked alias domain,
|
||||
easing future processing notably at post and notification sending time.
|
||||
|
||||
In batch mode it makes no sense to compute a single company, it will be
|
||||
dynamically generated. """
|
||||
toreset = self.filtered(
|
||||
lambda comp: (comp.record_company_id or comp.record_alias_domain_id) and comp.composition_batch
|
||||
)
|
||||
if toreset:
|
||||
toreset.record_alias_domain_id = False
|
||||
toreset.record_company_id = False
|
||||
|
||||
toupdate = self.filtered(
|
||||
lambda comp: not comp.composition_batch
|
||||
)
|
||||
for composer in toupdate:
|
||||
res_ids = composer._evaluate_res_ids()
|
||||
if composer.model and len(res_ids) == 1:
|
||||
record = self.env[composer.model].browse(res_ids)
|
||||
composer.record_company_id = record._mail_get_companies(
|
||||
default=self.env.company
|
||||
)[record.id]
|
||||
composer.record_alias_domain_id = record._mail_get_alias_domains(
|
||||
default_company=self.env.company
|
||||
)[record.id]
|
||||
|
||||
@api.depends('composition_mode', 'model', 'parent_id', 'res_domain', 'res_ids')
|
||||
def _compute_record_name(self):
|
||||
""" Computation is coming either from parent message, either from the
|
||||
@@ -781,6 +815,8 @@ class MailComposer(models.TransientModel):
|
||||
STA - 'email_add_signature',
|
||||
STA - 'email_layout_xmlid',
|
||||
DYN - 'force_email_lang', # notify parameter
|
||||
STA - 'record_alias_domain_id', # monorecord only
|
||||
STA - 'record_company_id', # monorecord only
|
||||
|
||||
BOTH
|
||||
DYN - 'attachment_ids',
|
||||
@@ -879,6 +915,8 @@ class MailComposer(models.TransientModel):
|
||||
force_send=self.force_send,
|
||||
mail_auto_delete=self.auto_delete,
|
||||
model_description=model_description,
|
||||
record_alias_domain_id=self.record_alias_domain_id.id,
|
||||
record_company_id=self.record_company_id.id,
|
||||
)
|
||||
return values
|
||||
|
||||
@@ -899,6 +937,10 @@ class MailComposer(models.TransientModel):
|
||||
RecordsModel = self.env[self.model].with_prefetch(res_ids)
|
||||
email_mode = self.composition_mode == 'mass_mail'
|
||||
|
||||
# records values
|
||||
companies = RecordsModel.browse(res_ids)._mail_get_companies(default=self.env.company)
|
||||
alias_domains = RecordsModel.browse(res_ids)._mail_get_alias_domains(default_company=self.env.company)
|
||||
|
||||
# langs, used currently only to propagate in comment mode for notification
|
||||
# layout translation
|
||||
langs = self._render_field('lang', res_ids)
|
||||
@@ -916,6 +958,9 @@ class MailComposer(models.TransientModel):
|
||||
'email_from': emails_from[res_id],
|
||||
'scheduled_date': False,
|
||||
'subject': subjects[res_id],
|
||||
# record-specific environment values (company, alias_domain)
|
||||
'record_alias_domain_id': alias_domains[res_id].id,
|
||||
'record_company_id': companies[res_id].id,
|
||||
# some fields are specific to mail
|
||||
**(
|
||||
{
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
<field name="model" invisible="1"/>
|
||||
<field name="model_is_thread" invisible="1"/>
|
||||
<field name="parent_id" invisible="1"/>
|
||||
<field name="record_alias_domain_id" invisible="1"/>
|
||||
<field name="record_company_id" invisible="1"/>
|
||||
<field name="record_name" invisible="1"/>
|
||||
<field name="render_model" invisible="1"/>
|
||||
<field name="res_domain" invisible="1"/>
|
||||
|
||||
@@ -171,6 +171,8 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertFalse(composer_form.mail_server_id)
|
||||
self.assertEqual(composer_form.model, self.test_record._name)
|
||||
self.assertFalse(composer_form.partner_ids)
|
||||
self.assertEqual(composer_form.record_alias_domain_id, self.mail_alias_domain)
|
||||
self.assertEqual(composer_form.record_company_id, self.env.company)
|
||||
self.assertEqual(composer_form.record_name, self.test_record.name, 'MailComposer: comment mode should compute record name')
|
||||
self.assertFalse(composer_form.reply_to)
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
@@ -182,6 +184,32 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertEqual(composer_form.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
self.assertFalse(composer_form.subtype_is_log)
|
||||
|
||||
@users('employee')
|
||||
def test_mail_composer_comment_mc(self):
|
||||
""" Test values specific to multi-company setup, in comment mode, using
|
||||
the Form tool. """
|
||||
# add access to second company to avoid MC rules on ticket model
|
||||
self.env.user.company_ids = [(4, self.company_2.id)]
|
||||
test_record = self.test_record.with_env(self.env)
|
||||
source_company = [
|
||||
self.company_admin,
|
||||
self.company_2,
|
||||
self.env['res.company'],
|
||||
]
|
||||
expected = [
|
||||
(self.company_admin, self.mail_alias_domain),
|
||||
(self.company_2, self.mail_alias_domain_c2),
|
||||
(self.company_admin, self.mail_alias_domain), # env company
|
||||
]
|
||||
for company, (exp_company, exp_alias_domain) in zip(source_company, expected):
|
||||
with self.subTest(cmopany=company):
|
||||
test_record.write({'company_id': company.id})
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_record, add_web=True)
|
||||
))
|
||||
self.assertEqual(composer_form.record_alias_domain_id, exp_alias_domain)
|
||||
self.assertEqual(composer_form.record_company_id, exp_company)
|
||||
|
||||
@users('employee')
|
||||
def test_mail_composer_comment_attachments(self):
|
||||
"""Tests that all attachments are added to the composer, static attachments
|
||||
@@ -251,6 +279,8 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
|
||||
self.assertEqual(composer_form.model, self.test_record._name)
|
||||
self.assertEqual(composer_form.partner_ids[:], self.partner_1)
|
||||
self.assertEqual(composer_form.record_alias_domain_id, self.mail_alias_domain)
|
||||
self.assertEqual(composer_form.record_company_id, self.env.company)
|
||||
self.assertEqual(composer_form.record_name, self.test_record.name, 'MailComposer: comment mode should compute record name')
|
||||
self.assertEqual(composer_form.reply_to, 'info@test.example.com')
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
@@ -283,6 +313,8 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertFalse(composer_form.force_send, 'MailComposer: batch record post use email queue for notifications')
|
||||
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
|
||||
self.assertEqual(composer_form.model, self.test_record._name)
|
||||
self.assertFalse(composer_form.record_alias_domain_id, 'MailComposer: comment in batch mode should have void alias domain')
|
||||
self.assertFalse(composer_form.record_company_id, 'MailComposer: comment in batch mode should have void company')
|
||||
self.assertFalse(composer_form.record_name, 'MailComposer: comment in batch mode should have void record name')
|
||||
self.assertEqual(composer_form.reply_to, self.template.reply_to)
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
@@ -315,6 +347,8 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertFalse(composer_form.force_send, 'MailComposer: batch record post use email queue for notifications')
|
||||
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
|
||||
self.assertEqual(composer_form.model, self.test_record._name)
|
||||
self.assertFalse(composer_form.record_alias_domain_id, 'MailComposer: comment in batch mode should have void alias domain')
|
||||
self.assertFalse(composer_form.record_company_id, 'MailComposer: comment in batch mode should have void company')
|
||||
self.assertFalse(composer_form.record_name, 'MailComposer: comment in batch mode should have void record name')
|
||||
self.assertEqual(composer_form.reply_to, self.template.reply_to)
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
@@ -347,6 +381,8 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
|
||||
self.assertEqual(composer_form.model, self.test_record._name)
|
||||
self.assertFalse(composer_form.partner_ids[:])
|
||||
self.assertFalse(composer_form.record_alias_domain_id)
|
||||
self.assertFalse(composer_form.record_company_id)
|
||||
self.assertFalse(composer_form.record_name)
|
||||
self.assertEqual(composer_form.reply_to, 'info@test.example.com')
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
@@ -375,6 +411,8 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertTrue(composer_form.force_send, 'MailComposer: mass mode sends emails right away')
|
||||
self.assertFalse(composer_form.mail_server_id)
|
||||
self.assertEqual(composer_form.model, self.test_records._name)
|
||||
self.assertFalse(composer_form.record_alias_domain_id, 'MailComposer: mass mode should have void alias domain')
|
||||
self.assertFalse(composer_form.record_company_id, 'MailComposer: mass mode should have void company')
|
||||
self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
|
||||
self.assertFalse(composer_form.reply_to)
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
@@ -404,6 +442,8 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertTrue(composer_form.force_send, 'MailComposer: mass mode sends emails right away')
|
||||
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
|
||||
self.assertEqual(composer_form.model, self.test_records._name)
|
||||
self.assertFalse(composer_form.record_alias_domain_id, 'MailComposer: mass mode should have void alias domain')
|
||||
self.assertFalse(composer_form.record_company_id, 'MailComposer: mass mode should have void company')
|
||||
self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
|
||||
self.assertEqual(composer_form.reply_to, self.template.reply_to)
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
@@ -437,6 +477,8 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertTrue(composer_form.force_send, 'MailComposer: mass mode sends emails right away')
|
||||
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
|
||||
self.assertEqual(composer_form.model, self.test_records._name)
|
||||
self.assertFalse(composer_form.record_alias_domain_id, 'MailComposer: mass mode should have void alias domain')
|
||||
self.assertFalse(composer_form.record_company_id, 'MailComposer: mass mode should have void company')
|
||||
self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
|
||||
self.assertEqual(composer_form.reply_to, self.template.reply_to)
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
@@ -470,6 +512,8 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertTrue(composer_form.force_send, 'MailComposer: mass mode sends emails right away')
|
||||
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
|
||||
self.assertEqual(composer_form.model, self.test_records._name)
|
||||
self.assertFalse(composer_form.record_alias_domain_id, 'MailComposer: mass mode should have void alias domain')
|
||||
self.assertFalse(composer_form.record_company_id, 'MailComposer: mass mode should have void company')
|
||||
self.assertFalse(composer_form.record_name, 'MailComposer: mass mode should have void record name')
|
||||
self.assertEqual(composer_form.reply_to, self.template.reply_to)
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
@@ -1262,6 +1306,12 @@ class TestComposerResultsComment(TestMailComposer, CronMixinCase):
|
||||
""" Ensure class initial data to ease understanding """
|
||||
self.assertTrue(self.template.auto_delete)
|
||||
|
||||
self.assertEqual(len(self.test_record), 1)
|
||||
self.assertEqual(self.test_record.user_id, self.user_employee_2)
|
||||
self.assertEqual(self.test_record.message_partner_ids, self.partner_employee_2)
|
||||
self.assertEqual(self.test_record[0].customer_id.lang, 'en_US')
|
||||
self.assertEqual(self.test_record.company_id, self.company_admin)
|
||||
|
||||
self.assertEqual(len(self.test_records), 2)
|
||||
self.assertEqual(self.test_records.user_id, self.user_employee_2)
|
||||
self.assertEqual(self.test_records.message_partner_ids, self.partner_employee_2)
|
||||
@@ -1844,7 +1894,6 @@ class TestComposerResultsComment(TestMailComposer, CronMixinCase):
|
||||
self.assertEqual(set(message.attachment_ids.mapped('res_id')), set(test_record.ids))
|
||||
self.assertTrue(all(attach not in message.attachment_ids for attach in attachs), 'Should have copied attachments')
|
||||
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_mc(self):
|
||||
@@ -1871,11 +1920,11 @@ class TestComposerResultsComment(TestMailComposer, CronMixinCase):
|
||||
attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])])
|
||||
self.assertEqual(len(attachs), 2)
|
||||
|
||||
for batch, companies, expected_companies in [
|
||||
for batch, companies, expected_companies, expected_alias_domains in [
|
||||
(False, self.company_admin, self.company_admin, self.mail_alias_domain),
|
||||
(
|
||||
False, self.company_admin, self.company_admin
|
||||
), (
|
||||
True, self.company_admin + self.company_2, self.company_admin + self.company_2,
|
||||
self.mail_alias_domain + self.mail_alias_domain_c2,
|
||||
),
|
||||
]:
|
||||
with self.subTest(batch=batch,
|
||||
@@ -1902,8 +1951,8 @@ class TestComposerResultsComment(TestMailComposer, CronMixinCase):
|
||||
new_partner = self.env['res.partner'].search([('email_normalized', '=', 'test.to.1@test.example.com')])
|
||||
self.assertEqual(len(new_partner), 1)
|
||||
# check output, company-specific values mainly for this test
|
||||
for record, exp_company in zip(
|
||||
test_records, expected_companies
|
||||
for record, exp_company, exp_alias_domain in zip(
|
||||
test_records, expected_companies, expected_alias_domains
|
||||
):
|
||||
message = record.message_ids[0]
|
||||
for recipient in [self.partner_employee_2, new_partner, record.customer_id]:
|
||||
@@ -1924,6 +1973,8 @@ class TestComposerResultsComment(TestMailComposer, CronMixinCase):
|
||||
'X-Odoo-Objects': f'{record._name}-{record.id}',
|
||||
},
|
||||
'mail_server_id': self.env['ir.mail_server'],
|
||||
'record_alias_domain_id': exp_alias_domain,
|
||||
'record_company_id': exp_company,
|
||||
'subject': f'TemplateSubject {record.name}',
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user