[REF] mail: cleanup mono/multi record composer behavior
RATIONALE
Improve usage of composer in comment or email mode: support batch-posting in
comment, support more configuration from templates, improve global model.
SPECIFICATIONS
Support a real res_ids field on mail.compose.message model. Instead of relying
on active_ids from context, store it once for all at composer level and use
it in code. Active_ids usage is still done at default_get level, using it to
populate the field.
Improve usage of domain, renamed to res_domain to match other document related
fields naming. Add support of a res_domain_user_id field allowing to set the
user from which the domain should be evaluated.
Composer now runs on a list of IDs. Mass mail mode and comment mode are now
distinct from running on a singleton or on more records. Rendered or raw
mode is not triggered by
* mass mailing mode: always display raw mode, whatever the number of records;
* comment mode: display rendered mode when having a single record (like the
previous comment mode). Display raw mode when having either no records
either at least two records.
Task-3035101 (Mail: Support batch-posting from composer)
Part-of: odoo/odoo#99482
This commit is contained in:
@@ -3509,12 +3509,7 @@ class AccountMove(models.Model):
|
||||
compose_form = self.env.ref('account.account_invoice_send_wizard_form', raise_if_not_found=False)
|
||||
ctx = dict(
|
||||
default_model='account.move',
|
||||
default_res_id=self.id,
|
||||
# For the sake of consistency we need a default_res_model if
|
||||
# default_res_id is set. Not renaming default_model as it can
|
||||
# create many side-effects.
|
||||
default_res_model='account.move',
|
||||
default_use_template=bool(template),
|
||||
default_res_ids=self.ids,
|
||||
default_template_id=template and template.id or False,
|
||||
default_composition_mode='comment',
|
||||
mark_invoice_as_sent=True,
|
||||
|
||||
@@ -430,7 +430,6 @@
|
||||
<field name="context" eval="{
|
||||
'mail_post_autofollow': True,
|
||||
'default_composition_mode': 'comment',
|
||||
'default_use_template': True,
|
||||
'default_template_id': ref('account.mail_template_data_payment_receipt'),
|
||||
'default_email_layout_xmlid': 'mail.mail_notification_light',
|
||||
}"/>
|
||||
@@ -447,7 +446,6 @@
|
||||
<field name="context" eval="{
|
||||
'mail_post_autofollow': True,
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_use_template': True,
|
||||
'default_template_id': ref('account.mail_template_data_payment_receipt'),
|
||||
'default_email_layout_xmlid': 'mail.mail_notification_light',
|
||||
}"/>
|
||||
|
||||
@@ -755,7 +755,6 @@ class Meeting(models.Model):
|
||||
default_composition_mode=default_composition_mode,
|
||||
default_model='calendar.event',
|
||||
default_res_ids=self.ids,
|
||||
default_use_template=bool(template_id),
|
||||
default_template_id=template_id,
|
||||
default_partner_ids=self.partner_ids.ids,
|
||||
mail_tz=self.env.user.tz,
|
||||
|
||||
@@ -735,7 +735,6 @@
|
||||
<field name="target">new</field>
|
||||
<field name="context" eval="{
|
||||
'default_composition_mode': 'comment',
|
||||
'default_use_template': False,
|
||||
}"/>
|
||||
<field name="binding_model_id" ref="model_crm_lead"/>
|
||||
<field name="binding_view_types">form</field>
|
||||
@@ -748,7 +747,6 @@
|
||||
<field name="target">new</field>
|
||||
<field name="context" eval="{
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_use_template': False,
|
||||
}"/>
|
||||
<field name="binding_model_id" ref="model_crm_lead"/>
|
||||
<field name="binding_view_types">list</field>
|
||||
|
||||
@@ -195,9 +195,8 @@ class EventRegistration(models.Model):
|
||||
compose_form = self.env.ref('mail.email_compose_message_wizard_form')
|
||||
ctx = dict(
|
||||
default_model='event.registration',
|
||||
default_res_id=self.id,
|
||||
default_use_template=bool(template),
|
||||
default_template_id=template and template.id,
|
||||
default_res_ids=self.ids,
|
||||
default_template_id=template.id if template else False,
|
||||
default_composition_mode='comment',
|
||||
default_email_layout_xmlid="mail.mail_notification_light",
|
||||
)
|
||||
|
||||
@@ -181,8 +181,7 @@ Do not hesitate to contact your manager or the human resource department.""")
|
||||
compose_form = self.env.ref('mail.email_compose_message_wizard_form', False)
|
||||
ctx = dict(
|
||||
default_model="hr.employee",
|
||||
default_res_id=self.id,
|
||||
default_use_template=bool(template),
|
||||
default_res_ids=self.ids,
|
||||
default_template_id=template.id,
|
||||
default_composition_mode='comment',
|
||||
default_is_log=True,
|
||||
|
||||
@@ -81,8 +81,7 @@ class LoyaltyCard(models.Model):
|
||||
compose_form = self.env.ref('mail.email_compose_message_wizard_form', False)
|
||||
ctx = dict(
|
||||
default_model='loyalty.card',
|
||||
default_res_id=self.id,
|
||||
default_use_template=bool(default_template),
|
||||
default_res_ids=self.ids,
|
||||
default_template_id=default_template and default_template.id,
|
||||
default_composition_mode='comment',
|
||||
default_email_layout_xmlid='mail.mail_notification_light',
|
||||
|
||||
@@ -170,14 +170,18 @@ class MailTemplate(models.Model):
|
||||
def create_action(self):
|
||||
ActWindow = self.env['ir.actions.act_window']
|
||||
view = self.env.ref('mail.email_compose_message_wizard_form')
|
||||
|
||||
for template in self:
|
||||
context = {
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_model': template.model,
|
||||
'default_template_id' : template.id,
|
||||
}
|
||||
button_name = _('Send Mail (%s)', template.name)
|
||||
action = ActWindow.create({
|
||||
'name': button_name,
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'mail.compose.message',
|
||||
'context': "{'default_composition_mode': 'mass_mail', 'default_template_id' : %d, 'default_use_template': True}" % (template.id),
|
||||
'context': repr(context),
|
||||
'view_mode': 'form,tree',
|
||||
'view_id': view.id,
|
||||
'target': 'new',
|
||||
|
||||
@@ -2170,12 +2170,9 @@ class MailThread(models.AbstractModel):
|
||||
|
||||
mails_su = self.env['mail.mail'].sudo()
|
||||
for subset in [self] if template else self:
|
||||
composer_ctx['active_ids'] = subset.ids # still required for mass mail mode
|
||||
if template:
|
||||
composer_ctx['default_res_id'] = subset.ids[0] if subset.ids else 0
|
||||
else:
|
||||
composer_ctx['default_res_ids'] = subset.ids
|
||||
if not template:
|
||||
composer_values['body'] = bodies[subset.id]
|
||||
composer_ctx['default_res_id'] = subset.id
|
||||
|
||||
composer = self.env['mail.compose.message'].with_context(
|
||||
**composer_ctx
|
||||
@@ -2185,9 +2182,9 @@ class MailThread(models.AbstractModel):
|
||||
if template:
|
||||
update_values = composer._onchange_template_id(
|
||||
template.id,
|
||||
composer_values['composition_mode'],
|
||||
'mass_mail',
|
||||
self._name,
|
||||
subset.ids[0] if subset.ids else 0
|
||||
subset.ids,
|
||||
)['value']
|
||||
composer.write(update_values)
|
||||
|
||||
@@ -2253,10 +2250,9 @@ class MailThread(models.AbstractModel):
|
||||
for record in self:
|
||||
if template:
|
||||
composer = self.env['mail.compose.message'].with_context(
|
||||
active_ids=record.ids, # still required for mass mail mode
|
||||
default_composition_mode='comment',
|
||||
default_model=self._name,
|
||||
default_res_id=record.id,
|
||||
default_res_ids=record.ids,
|
||||
default_template_id=template.id,
|
||||
).create({
|
||||
'message_type': message_type,
|
||||
@@ -2270,7 +2266,7 @@ class MailThread(models.AbstractModel):
|
||||
template.id,
|
||||
'comment',
|
||||
self._name,
|
||||
record.id,
|
||||
record.ids,
|
||||
)['value']
|
||||
composer.write(update_values)
|
||||
_mails_as_sudo, messages_as_sudo = composer._action_send_mail()
|
||||
@@ -3049,11 +3045,12 @@ class MailThread(models.AbstractModel):
|
||||
lang = False
|
||||
if force_email_lang:
|
||||
lang = force_email_lang
|
||||
elif {'default_template_id', 'default_model', 'default_res_id'} <= self.env.context.keys():
|
||||
elif {'default_template_id', 'default_model', 'default_res_ids'} <= self.env.context.keys():
|
||||
# TDE FIXME: this whole brol should be cleaned !
|
||||
res_ids = self.env['mail.compose.message']._parse_res_ids(self.env.context['default_res_ids'])
|
||||
template = self.env['mail.template'].browse(self.env.context['default_template_id'])
|
||||
if template and template.lang:
|
||||
lang = template._render_lang([self.env.context['default_res_id']])[self.env.context['default_res_id']]
|
||||
if res_ids and template and template.lang:
|
||||
lang = template._render_lang(res_ids)[res_ids[0]]
|
||||
if not lang:
|
||||
lang = self.env.context.get('lang')
|
||||
|
||||
|
||||
@@ -341,9 +341,8 @@ const KanbanActivity = AbstractField.extend({
|
||||
views: [[false, "form"]],
|
||||
target: "new",
|
||||
context: {
|
||||
default_res_id: this.res_id,
|
||||
default_res_ids: [this.res_id],
|
||||
default_model: this.model,
|
||||
default_use_template: true,
|
||||
default_template_id: templateID,
|
||||
force_email: true,
|
||||
},
|
||||
|
||||
@@ -550,7 +550,7 @@ Model({
|
||||
default_is_log: this.composer.isLog,
|
||||
default_model: this.composer.activeThread.model,
|
||||
default_partner_ids: this.composer.recipients.map((partner) => partner.id),
|
||||
default_res_id: this.composer.activeThread.id,
|
||||
default_res_ids: [this.composer.activeThread.id],
|
||||
default_subtype_xmlid: 'mail.mt_comment',
|
||||
mail_post_autofollow: this.composer.activeThread.hasWriteAccess,
|
||||
};
|
||||
|
||||
@@ -16,9 +16,8 @@ Model({
|
||||
views: [[false, "form"]],
|
||||
target: "new",
|
||||
context: {
|
||||
default_res_id: activity.thread.id,
|
||||
default_res_ids: [activity.thread.id],
|
||||
default_model: activity.thread.model,
|
||||
default_use_template: true,
|
||||
default_subtype_xmlid: 'mail.mt_comment',
|
||||
default_template_id: this.id,
|
||||
force_email: true,
|
||||
|
||||
@@ -619,7 +619,7 @@ QUnit.module("mail", {}, function () {
|
||||
});
|
||||
|
||||
QUnit.test("activity with mail template: preview mail", async function (assert) {
|
||||
assert.expect(10);
|
||||
assert.expect(9);
|
||||
|
||||
const pyEnv = await startServer();
|
||||
const resPartnerId1 = pyEnv["res.partner"].create({});
|
||||
@@ -642,20 +642,16 @@ QUnit.module("mail", {}, function () {
|
||||
patchWithCleanup(env.services.action, {
|
||||
doAction(action) {
|
||||
assert.step("do_action");
|
||||
assert.strictEqual(
|
||||
action.context.default_res_id,
|
||||
resPartnerId1,
|
||||
"Action should have the activity res id as default res id in context"
|
||||
assert.deepEqual(
|
||||
action.context.default_res_ids,
|
||||
[resPartnerId1],
|
||||
"Action should have the activity res id as default res ids in context"
|
||||
);
|
||||
assert.strictEqual(
|
||||
action.context.default_model,
|
||||
"res.partner",
|
||||
"Action should have the activity res model as default model in context"
|
||||
);
|
||||
assert.ok(
|
||||
action.context.default_use_template,
|
||||
"Action should have true as default use_template in context"
|
||||
);
|
||||
assert.strictEqual(
|
||||
action.context.default_template_id,
|
||||
mailTemplateId1,
|
||||
|
||||
@@ -69,10 +69,11 @@ class MockEmail(common.BaseCase, MockSmtplibCase):
|
||||
with self.mock_smtplib_connection(), \
|
||||
patch.object(IrMailServer, 'build_email', autospec=True, wraps=IrMailServer, side_effect=_ir_mail_server_build_email) as build_email_mocked, \
|
||||
patch.object(IrMailServer, 'send_email', autospec=True, wraps=IrMailServer, side_effect=send_email_origin) as send_email_mocked, \
|
||||
patch.object(MailMail, 'create', autospec=True, wraps=MailMail, side_effect=_mail_mail_create), \
|
||||
patch.object(MailMail, 'create', autospec=True, wraps=MailMail, side_effect=_mail_mail_create) as mail_mail_create_mocked, \
|
||||
patch.object(MailMail, 'unlink', autospec=True, wraps=MailMail, side_effect=_mail_mail_unlink):
|
||||
self.build_email_mocked = build_email_mocked
|
||||
self.send_email_mocked = send_email_mocked
|
||||
self.mail_mail_create_mocked = mail_mail_create_mocked
|
||||
yield
|
||||
|
||||
if mail_unlink_sent:
|
||||
|
||||
@@ -84,7 +84,7 @@ class TestMailComposerForm(TestMailComposer):
|
||||
form = Form(self.env['mail.compose.message'].with_context({
|
||||
'default_partner_ids': partner_classic.ids,
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
}))
|
||||
form.body = '<p>Hello</p>'
|
||||
self.assertEqual(
|
||||
@@ -116,7 +116,7 @@ class TestMailComposerForm(TestMailComposer):
|
||||
form = Form(self.env['mail.compose.message'].with_context({
|
||||
'default_partner_ids': (partner_private + partner_classic).ids,
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
}))
|
||||
form.body = '<p>Hello</p>'
|
||||
self.assertEqual(
|
||||
@@ -156,7 +156,7 @@ class TestMailComposerForm(TestMailComposer):
|
||||
_form = Form(self.env['mail.compose.message'].with_context({
|
||||
'default_partner_ids': (self.partner_private + partner_classic).ids,
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
}))
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
@@ -177,7 +177,7 @@ class TestMailComposerForm(TestMailComposer):
|
||||
|
||||
form = Form(self.env['mail.compose.message'].with_context({
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
'default_template_id': template.id,
|
||||
}))
|
||||
|
||||
|
||||
@@ -111,7 +111,6 @@
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_partner_to': '{{ object.id or \'\' }}',
|
||||
'default_subtype_xmlid': 'mail.mt_comment',
|
||||
'default_use_template': False,
|
||||
'default_reply_to_force_new': True,
|
||||
}"/>
|
||||
<field name="binding_model_id" ref="base.model_res_partner"/>
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
|
||||
import ast
|
||||
import base64
|
||||
import re
|
||||
import logging
|
||||
|
||||
from odoo import _, api, fields, models, tools, Command
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import email_re
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _reopen(self, res_id, model, context=None):
|
||||
# save original model in context, because selecting the list of available
|
||||
@@ -41,14 +43,13 @@ class MailComposer(models.TransientModel):
|
||||
_batch_size = 500
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
""" Handle composition mode. Some details about context keys:
|
||||
- comment: default mode, model and ID of a record the user comments
|
||||
- default_model or active_model
|
||||
- default_res_id or active_id
|
||||
- mass_mail: model and IDs of records the user mass-mails
|
||||
- active_ids: record IDs
|
||||
- default_model or active_model
|
||||
def default_get(self, fields_list):
|
||||
""" Handle composition mode and contextual computation, until moving
|
||||
to computed fields. Support active_model / active_id(s) as valid default
|
||||
values, as this comes from standard web client usage.
|
||||
|
||||
Note that supporting active_ids through composer is still done, as we
|
||||
may have to give a huge list of IDs that won't fit into res_ids field.
|
||||
"""
|
||||
# support subtype xmlid, like ``message_post``, when easier than using ``ref``
|
||||
if self.env.context.get('default_subtype_xmlid'):
|
||||
@@ -57,12 +58,15 @@ class MailComposer(models.TransientModel):
|
||||
self.env.context['default_subtype_xmlid']
|
||||
)
|
||||
)
|
||||
# deprecated record context management
|
||||
if 'default_res_id' in self.env.context:
|
||||
raise ValueError(_("Deprecated usage of 'default_res_id', should use 'default_res_ids'."))
|
||||
|
||||
result = super(MailComposer, self).default_get(fields)
|
||||
result = super().default_get(fields_list)
|
||||
|
||||
# author
|
||||
missing_author = 'author_id' in fields and 'author_id' not in result
|
||||
missing_email_from = 'email_from' in fields and 'email_from' not in result
|
||||
missing_author = 'author_id' in fields_list and 'author_id' not in result
|
||||
missing_email_from = 'email_from' in fields_list and 'email_from' not in result
|
||||
if missing_author or missing_email_from:
|
||||
author_id, email_from = self.env['mail.thread']._message_compute_author(result.get('author_id'), result.get('email_from'), raise_on_email=False)
|
||||
if missing_email_from:
|
||||
@@ -70,26 +74,36 @@ class MailComposer(models.TransientModel):
|
||||
if missing_author:
|
||||
result['author_id'] = author_id
|
||||
|
||||
if 'model' in fields and 'model' not in result:
|
||||
# record context management
|
||||
if 'model' in fields_list and 'model' not in result:
|
||||
result['model'] = self.env.context.get('active_model')
|
||||
if 'res_id' in fields and 'res_id' not in result:
|
||||
result['res_id'] = self.env.context.get('active_id')
|
||||
if 'reply_to_mode' in fields and 'reply_to_mode' not in result and result.get('model'):
|
||||
# doesn't support threading
|
||||
if 'res_ids' in fields_list and 'res_ids' not in result:
|
||||
if self.env.context.get('active_ids'):
|
||||
active_res_ids = self._parse_res_ids(self.env.context['active_ids'])
|
||||
# beware, field is limited in storage, usage of active_ids in context still required
|
||||
if active_res_ids and len(active_res_ids) <= self._batch_size:
|
||||
result['res_ids'] = self.env.context['active_ids']
|
||||
elif self.env.context.get('active_id'):
|
||||
result['res_ids'] = [self.env.context['active_id']]
|
||||
else:
|
||||
result['res_ids'] = False
|
||||
# record / parent based computation
|
||||
if result.get('composition_mode') == 'comment' and (set(fields_list) & {'model', 'res_ids', 'partner_ids', 'record_name', 'subject'}):
|
||||
result.update(self.get_record_data(result))
|
||||
|
||||
# threading support check: 'update' requires to use message_update/post
|
||||
if 'reply_to_mode' in fields_list and 'reply_to_mode' not in result and result.get('model'):
|
||||
if result['model'] not in self.env or not hasattr(self.env[result['model']], 'message_post'):
|
||||
result['reply_to_mode'] = 'new'
|
||||
|
||||
if 'active_domain' in self.env.context: # not context.get() because we want to keep global [] domains
|
||||
result['active_domain'] = '%r' % self.env.context.get('active_domain')
|
||||
if result.get('composition_mode') == 'comment' and (set(fields) & set(['model', 'res_id', 'partner_ids', 'record_name', 'subject'])):
|
||||
result.update(self.get_record_data(result))
|
||||
|
||||
# when being in new mode, create_uid is not granted -> ACLs issue may arise
|
||||
if 'create_uid' in fields and 'create_uid' not in result:
|
||||
if 'create_uid' in fields_list and 'create_uid' not in result:
|
||||
result['create_uid'] = self.env.uid
|
||||
|
||||
filtered_result = dict((fname, result[fname]) for fname in result if fname in fields)
|
||||
return filtered_result
|
||||
return {
|
||||
fname: result[fname]
|
||||
for fname in result if fname in fields_list
|
||||
}
|
||||
|
||||
def _partner_ids_domain(self):
|
||||
return expression.OR([
|
||||
@@ -120,11 +134,15 @@ class MailComposer(models.TransientModel):
|
||||
selection=[('comment', 'Post on a document'),
|
||||
('mass_mail', 'Email Mass Mailing')],
|
||||
string='Composition mode', default='comment')
|
||||
composition_batch = fields.Boolean(
|
||||
'Batch composition', compute='_compute_composition_batch') # more than 1 record (raw source)
|
||||
model = fields.Char('Related Document Model')
|
||||
res_id = fields.Integer('Related Document ID')
|
||||
res_ids = fields.Text('Related Document IDs')
|
||||
res_domain = fields.Text('Active domain')
|
||||
res_domain_user_id = fields.Many2one(
|
||||
'res.users', string='Responsible',
|
||||
help='Used as context used to evaluate composer domain')
|
||||
record_name = fields.Char('Message Record Name')
|
||||
use_active_domain = fields.Boolean('Use active domain')
|
||||
active_domain = fields.Text('Active domain', readonly=True)
|
||||
# characteristics
|
||||
message_type = fields.Selection([
|
||||
('comment', 'Comment'),
|
||||
@@ -158,6 +176,35 @@ class MailComposer(models.TransientModel):
|
||||
auto_delete_message = fields.Boolean('Delete Message Copy', help='Do not keep a copy of the email in the document communication history (mass mailing only)')
|
||||
mail_server_id = fields.Many2one('ir.mail_server', 'Outgoing mail server')
|
||||
|
||||
@api.constrains('res_ids')
|
||||
def _check_res_ids(self):
|
||||
""" Check res_ids is a valid list of integers (or Falsy). """
|
||||
for composer in self:
|
||||
composer._evaluate_res_ids()
|
||||
|
||||
@api.constrains('res_domain')
|
||||
def _check_res_domain(self):
|
||||
""" Check domain is a valid domain if set (otherwise it is considered
|
||||
as a Falsy leaf. """
|
||||
for composer in self:
|
||||
composer._evaluate_res_domain()
|
||||
|
||||
@api.depends('res_ids')
|
||||
def _compute_composition_batch(self):
|
||||
""" Determine if batch mode is activated:
|
||||
|
||||
* using res_domain: always batch (even if result is singleton at a
|
||||
given time, it is user and time dependent, hence batch);
|
||||
* res_ids: if more than one item in the list (void and singleton are
|
||||
not batch);
|
||||
"""
|
||||
for composer in self:
|
||||
if composer.res_domain:
|
||||
composer.composition_batch = True
|
||||
continue
|
||||
res_ids = composer._evaluate_res_ids()
|
||||
composer.composition_batch = len(res_ids) > 1 if res_ids else False
|
||||
|
||||
@api.depends('reply_to_force_new')
|
||||
def _compute_reply_to_mode(self):
|
||||
for composer in self:
|
||||
@@ -186,18 +233,19 @@ class MailComposer(models.TransientModel):
|
||||
@api.onchange('template_id')
|
||||
def _onchange_template_id_wrapper(self):
|
||||
self.ensure_one()
|
||||
values = self._onchange_template_id(self.template_id.id, self.composition_mode, self.model, self.res_id)['value']
|
||||
values = self._onchange_template_id(self.template_id.id, self.composition_mode, self.model, self._evaluate_res_ids())['value']
|
||||
for fname, value in values.items():
|
||||
setattr(self, fname, value)
|
||||
|
||||
def _onchange_template_id(self, template_id, composition_mode, model, res_id):
|
||||
def _onchange_template_id(self, template_id, composition_mode, model, res_ids):
|
||||
""" Perform the onchange.
|
||||
|
||||
* mass_mailing: we cannot render, so return the template values
|
||||
* mass_mailing or comment in batch: we cannot render, so return the
|
||||
template values
|
||||
* normal mode: return rendered values
|
||||
-> for x2many field, this onchange return command instead of ids
|
||||
"""
|
||||
if template_id and composition_mode == 'mass_mail':
|
||||
if template_id and (composition_mode == 'mass_mail' or len(res_ids) > 1):
|
||||
# copy raw template values (not rendered due to mass mode)
|
||||
template = self.env['mail.template'].browse(template_id)
|
||||
values = dict(
|
||||
@@ -214,11 +262,13 @@ class MailComposer(models.TransientModel):
|
||||
values['mail_server_id'] = template.mail_server_id.id
|
||||
if not tools.is_html_empty(template.body_html):
|
||||
values['body'] = template.body_html
|
||||
elif template_id:
|
||||
elif template_id and len(res_ids) <= 1:
|
||||
# render template (mono record, comment mode) and set it as composer values
|
||||
# trick to evaluate qweb even when having no records
|
||||
template_res_ids = res_ids if res_ids else [0]
|
||||
values = self._generate_template_for_composer(
|
||||
self.env['mail.template'].browse(template_id),
|
||||
[res_id],
|
||||
template_res_ids,
|
||||
('attachment_ids',
|
||||
'body_html',
|
||||
'email_cc',
|
||||
@@ -230,7 +280,7 @@ class MailComposer(models.TransientModel):
|
||||
'report_template',
|
||||
'subject',
|
||||
)
|
||||
)[res_id]
|
||||
)[template_res_ids[0]]
|
||||
# transform attachments into attachment_ids; not attached to the document because this will
|
||||
# be done further in the posting process, allowing to clean database if email not send
|
||||
attachment_ids = []
|
||||
@@ -250,7 +300,7 @@ class MailComposer(models.TransientModel):
|
||||
default_values = self.with_context(
|
||||
default_composition_mode=composition_mode,
|
||||
default_model=model,
|
||||
default_res_id=res_id
|
||||
default_res_ids=res_ids
|
||||
).default_get(['attachment_ids',
|
||||
'body',
|
||||
'composition_mode',
|
||||
@@ -260,7 +310,7 @@ class MailComposer(models.TransientModel):
|
||||
'parent_id',
|
||||
'partner_ids',
|
||||
'reply_to',
|
||||
'res_id',
|
||||
'res_ids',
|
||||
'subject',
|
||||
])
|
||||
values = dict(
|
||||
@@ -286,31 +336,33 @@ class MailComposer(models.TransientModel):
|
||||
a document (model, res_id). This is based on previously computed default
|
||||
values. """
|
||||
result, record_name, subject = {}, False, False
|
||||
model, res_id = values.get('model'), values.get('res_id')
|
||||
model = values.get('model')
|
||||
res_ids = self._parse_res_ids(values['res_ids']) if values.get('res_ids') else []
|
||||
if values.get('parent_id'):
|
||||
parent = self.env['mail.message'].browse(values.get('parent_id'))
|
||||
if not model:
|
||||
model = parent.model
|
||||
result['model'] = model
|
||||
if not res_id:
|
||||
res_id = parent.res_id
|
||||
result['res_id'] = res_id
|
||||
if not res_ids:
|
||||
res_ids = [parent.res_id]
|
||||
result['res_ids'] = res_ids
|
||||
result['partner_ids'] = values.get('partner_ids', list()) + parent.partner_ids.ids
|
||||
record_name = parent.record_name
|
||||
subject = tools.ustr(parent.subject or '')
|
||||
if (not subject or not record_name) and model and res_id:
|
||||
record = self.env[model].browse(res_id)
|
||||
if (not subject or not record_name) and model and res_ids and len(res_ids) == 1:
|
||||
record = self.env[model].browse(res_ids[0])
|
||||
if not record_name:
|
||||
record_name = record.display_name
|
||||
if not subject:
|
||||
subject = record._message_compute_subject()
|
||||
elif model and res_id:
|
||||
record = self.env[model].browse(res_id)
|
||||
elif model and len(res_ids) == 1:
|
||||
record = self.env[model].browse(res_ids[0])
|
||||
record_name = record.display_name or ''
|
||||
subject = record._message_compute_subject()
|
||||
|
||||
result['record_name'] = record_name
|
||||
result['subject'] = subject
|
||||
if values.get('parent_id') or len(res_ids) == 1: # to be cleanup when moving to computed fields
|
||||
result['record_name'] = record_name
|
||||
result['subject'] = subject
|
||||
|
||||
return result
|
||||
|
||||
@@ -356,18 +408,14 @@ class MailComposer(models.TransientModel):
|
||||
result_mails_su, result_messages = self.env['mail.mail'].sudo(), self.env['mail.message']
|
||||
|
||||
for wizard in self:
|
||||
# Mass Mailing
|
||||
mass_mode = wizard.composition_mode == 'mass_mail'
|
||||
|
||||
# wizard works in batch mode: [res_id] or active_ids or active_domain
|
||||
if mass_mode and wizard.use_active_domain and wizard.model:
|
||||
res_ids = self.env[wizard.model].search(ast.literal_eval(wizard.active_domain)).ids
|
||||
elif mass_mode and wizard.model and self.env.context.get('active_ids'):
|
||||
res_ids = self.env.context['active_ids']
|
||||
if wizard.res_domain:
|
||||
search_domain = wizard._evaluate_res_domain()
|
||||
search_user = wizard.res_domain_user_id or self.env.user
|
||||
res_ids = self.env[wizard.model].with_user(search_user).search(search_domain).ids
|
||||
else:
|
||||
res_ids = [wizard.res_id] if wizard.res_id else []
|
||||
res_ids = wizard._evaluate_res_ids()
|
||||
# in comment mode: raise here as anyway message_post will raise.
|
||||
if not res_ids and not mass_mode:
|
||||
if not res_ids and wizard.composition_mode == 'comment':
|
||||
raise ValueError(
|
||||
_('Mail composer in comment mode should run on at least one record. No records found (model %(model_name)s).',
|
||||
model_name=wizard.model)
|
||||
@@ -386,7 +434,7 @@ class MailComposer(models.TransientModel):
|
||||
self.ensure_one()
|
||||
post_values_all = self._prepare_mail_values(res_ids)
|
||||
ActiveModel = self.env[self.model] if self.model and hasattr(self.env[self.model], 'message_post') else self.env['mail.thread']
|
||||
if len(res_ids) > 1:
|
||||
if self.composition_batch:
|
||||
# do not send emails directly but use the queue instead
|
||||
# add context key to avoid subscribing the author
|
||||
ActiveModel = ActiveModel.with_context(
|
||||
@@ -509,16 +557,17 @@ class MailComposer(models.TransientModel):
|
||||
"""
|
||||
self.ensure_one()
|
||||
email_mode = self.composition_mode == 'mass_mail'
|
||||
rendering_mode = email_mode or self.composition_batch
|
||||
|
||||
# values that do not depend on rendering mode
|
||||
base_values = self._prepare_mail_values_static()
|
||||
|
||||
additional_values_all = {}
|
||||
# rendered based on raw content (wizard or template)
|
||||
if email_mode and self.model:
|
||||
if rendering_mode and self.model:
|
||||
additional_values_all = self._prepare_mail_values_dynamic(res_ids)
|
||||
# wizard content already rendered
|
||||
elif not email_mode:
|
||||
elif not rendering_mode:
|
||||
additional_values_all = self._prepare_mail_values_rendered(res_ids)
|
||||
|
||||
mail_values_all = {
|
||||
@@ -607,17 +656,22 @@ class MailComposer(models.TransientModel):
|
||||
bodies = self._render_field(
|
||||
'body', res_ids,
|
||||
# We want to preserve comments in emails so as to keep mso conditionals
|
||||
options={'preserve_comments': self.composition_mode == 'mass_mail'},
|
||||
options={'preserve_comments': email_mode},
|
||||
)
|
||||
emails_from = self._render_field('email_from', res_ids)
|
||||
|
||||
mail_values_all = {
|
||||
res_id: {
|
||||
'body': bodies[res_id], # should be void
|
||||
'body_html': bodies[res_id] if email_mode else False,
|
||||
'email_from': emails_from[res_id],
|
||||
'res_id': res_id,
|
||||
'subject': subjects[res_id],
|
||||
# some fields are specific to mail or message
|
||||
**(
|
||||
{
|
||||
'auto_delete': self.auto_delete,
|
||||
'body_html': bodies[res_id],
|
||||
'res_id': res_id,
|
||||
} if email_mode else {}),
|
||||
}
|
||||
for res_id in res_ids
|
||||
}
|
||||
@@ -639,6 +693,9 @@ class MailComposer(models.TransientModel):
|
||||
for res_id in res_ids:
|
||||
# remove attachments from template values as they should not be rendered
|
||||
template_values[res_id].pop('attachment_ids', None)
|
||||
# auto_delete is not supported in batch post, only in mass mail
|
||||
if not email_mode:
|
||||
template_values[res_id].pop('auto_delete', False)
|
||||
mail_values_all[res_id].update(template_values[res_id])
|
||||
|
||||
# Handle recipients. Without template, if no partner_ids is given, update
|
||||
@@ -674,25 +731,34 @@ class MailComposer(models.TransientModel):
|
||||
(name, base64.b64decode(enc_cont))
|
||||
for name, enc_cont in mail_values.pop('attachments', [])
|
||||
]
|
||||
mail_values['attachment_ids'] = record._process_attachments_for_post(
|
||||
decoded_attachments,
|
||||
attachment_ids,
|
||||
{'model': 'mail.message', 'res_id': 0}
|
||||
)['attachment_ids']
|
||||
# email_mode: prepare processed attachments as commands for mail.mail
|
||||
if email_mode:
|
||||
mail_values['attachment_ids'] = record._process_attachments_for_post(
|
||||
decoded_attachments,
|
||||
attachment_ids,
|
||||
{'model': 'mail.message', 'res_id': 0}
|
||||
)['attachment_ids']
|
||||
# comment mode: prepare attachments as a list of IDs, to be processed by MailThread
|
||||
else:
|
||||
mail_values['attachments'] = decoded_attachments
|
||||
mail_values['attachment_ids'] = attachment_ids
|
||||
|
||||
# headers
|
||||
mail_values['headers'] = repr(record._notify_by_email_get_headers())
|
||||
if email_mode:
|
||||
mail_values['headers'] = repr(record._notify_by_email_get_headers())
|
||||
|
||||
# recipients: transform partner_ids (field used in mail_message) into
|
||||
# recipient_ids, used by mail_mail
|
||||
recipient_ids_all = mail_values.pop('partner_ids', []) + self.partner_ids.ids
|
||||
mail_values['recipient_ids'] = [(4, pid) for pid in recipient_ids_all]
|
||||
if email_mode:
|
||||
recipient_ids_all = mail_values.pop('partner_ids', []) + self.partner_ids.ids
|
||||
mail_values['recipient_ids'] = [(4, pid) for pid in recipient_ids_all]
|
||||
|
||||
# when having no specific reply_to -> fetch rendered email_from
|
||||
reply_to = reply_to_values.get(res_id)
|
||||
if not reply_to:
|
||||
reply_to = mail_values.get('email_from', False)
|
||||
mail_values['reply_to'] = reply_to
|
||||
if email_mode:
|
||||
reply_to = reply_to_values.get(res_id)
|
||||
if not reply_to:
|
||||
reply_to = mail_values.get('email_from', False)
|
||||
mail_values['reply_to'] = reply_to
|
||||
|
||||
return mail_values_all
|
||||
|
||||
@@ -901,3 +967,63 @@ class MailComposer(models.TransientModel):
|
||||
]
|
||||
}
|
||||
return recipients_info
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# MISC UTILS
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
def _evaluate_res_domain(self):
|
||||
""" Parse composer domain, which can be: an already valid list or
|
||||
tuple (generally in code), a list or tuple as a string (coming from
|
||||
actions). Void strings are considered as a falsy domain.
|
||||
|
||||
:return: an Odoo domain (list of leaves) """
|
||||
self.ensure_one()
|
||||
if isinstance(self.res_domain, (str, bool)) and not self.res_domain:
|
||||
return expression.FALSE_DOMAIN
|
||||
try:
|
||||
domain = self.res_domain
|
||||
if isinstance(self.res_domain, str):
|
||||
domain = ast.literal_eval(domain)
|
||||
|
||||
expression.expression(
|
||||
domain,
|
||||
self.env[self.model],
|
||||
)
|
||||
except (ValueError, AssertionError) as e:
|
||||
raise ValidationError(
|
||||
_("Invalid domain %(domain)r (type %(domain_type)s)",
|
||||
domain=self.res_domain,
|
||||
domain_type=type(self.res_domain))
|
||||
) from e
|
||||
|
||||
return domain
|
||||
|
||||
def _evaluate_res_ids(self):
|
||||
""" Parse composer res_ids, which can be: an already valid list or
|
||||
tuple (generally in code), a list or tuple as a string (coming from
|
||||
actions). Void strings / missing values are evaluated as an empty list.
|
||||
|
||||
Note that 'active_ids' context key is supported at this point as mailing
|
||||
on big ID list would create issues if stored in database.
|
||||
|
||||
:return: a list of IDs (empty list in case of falsy strings)"""
|
||||
self.ensure_one()
|
||||
return self._parse_res_ids(
|
||||
self.res_ids or self.env.context.get('active_ids')
|
||||
) or []
|
||||
|
||||
@api.model
|
||||
def _parse_res_ids(self, res_ids):
|
||||
if tools.is_list_of(res_ids, int) or not res_ids:
|
||||
return res_ids
|
||||
error_msg = _("Invalid res_ids %(res_ids_str)s (type %(res_ids_type)s)",
|
||||
res_ids_str=res_ids,
|
||||
res_ids_type=type(res_ids))
|
||||
try:
|
||||
res_ids = ast.literal_eval(res_ids)
|
||||
except Exception as e:
|
||||
raise ValidationError(error_msg) from e
|
||||
if not tools.is_list_of(res_ids, int):
|
||||
raise ValidationError(error_msg)
|
||||
return res_ids
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<field name="author_id" invisible="1"/>
|
||||
<field name="auto_delete" invisible="1"/>
|
||||
<field name="auto_delete_message" invisible="1"/>
|
||||
<field name="composition_batch" invisible="1"/>
|
||||
<field name="composition_mode" invisible="1"/>
|
||||
<field name="email_layout_xmlid" invisible="1"/>
|
||||
<field name="is_log" invisible="1"/>
|
||||
@@ -21,7 +22,9 @@
|
||||
<field name="parent_id" invisible="1"/>
|
||||
<field name="record_name" invisible="1"/>
|
||||
<field name="render_model" invisible="1"/>
|
||||
<field name="res_id" invisible="1"/>
|
||||
<field name="res_domain" invisible="1"/>
|
||||
<field name="res_domain_user_id" invisible="1"/>
|
||||
<field name="res_ids" invisible="1"/>
|
||||
<field name="subtype_id" invisible="1"/>
|
||||
<!-- visible wizard -->
|
||||
<field name="email_from"
|
||||
|
||||
@@ -1068,17 +1068,21 @@ class MassMailing(models.Model):
|
||||
'record_name': False,
|
||||
'reply_to_force_new': mailing.reply_to_mode == 'new',
|
||||
'subject': mailing.subject,
|
||||
'template_id': None,
|
||||
'template_id': False,
|
||||
}
|
||||
if mailing.reply_to_mode == 'new':
|
||||
composer_values['reply_to'] = mailing.reply_to
|
||||
|
||||
composer = self.env['mail.compose.message'].with_context(active_ids=res_ids).create(composer_values)
|
||||
extra_context = mailing._get_mass_mailing_context()
|
||||
composer = composer.with_context(active_ids=res_ids, **extra_context)
|
||||
composer = self.env['mail.compose.message'].with_context(
|
||||
active_ids=res_ids,
|
||||
default_composition_mode='mass_mail',
|
||||
**mailing._get_mass_mailing_context()
|
||||
).create(composer_values)
|
||||
|
||||
# auto-commit except in testing mode
|
||||
auto_commit = not getattr(threading.current_thread(), 'testing', False)
|
||||
composer._action_send_mail(auto_commit=auto_commit)
|
||||
composer._action_send_mail(
|
||||
auto_commit=not getattr(threading.current_thread(), 'testing', False)
|
||||
)
|
||||
mailing.write({
|
||||
'state': 'done',
|
||||
'sent_date': fields.Datetime.now(),
|
||||
|
||||
@@ -45,7 +45,7 @@ class TestMassMailValues(MassMailCommon):
|
||||
composer = self.env['mail.compose.message'].with_user(self.user_marketing).with_context({
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_model': 'res.partner',
|
||||
'default_res_id': recipient.id,
|
||||
'default_res_ids': recipient.ids,
|
||||
}).create({
|
||||
'subject': 'Mass Mail Responsive',
|
||||
'body': 'I am Responsive body',
|
||||
|
||||
@@ -101,7 +101,7 @@ class MailComposeMessage(models.TransientModel):
|
||||
'body_html': self.body,
|
||||
'campaign_id': self.campaign_id.id,
|
||||
'mailing_model_id': self.env['ir.model']._get(self.model).id,
|
||||
'mailing_domain': self.active_domain,
|
||||
'mailing_domain': self.res_domain if self.res_domain else f"[('id', 'in', {self.res_ids})]",
|
||||
'name': self.mass_mailing_name,
|
||||
'reply_to': self.reply_to if self.reply_to_mode == 'new' else False,
|
||||
'reply_to_mode': self.reply_to_mode,
|
||||
|
||||
@@ -427,7 +427,6 @@
|
||||
<field name="target">new</field>
|
||||
<field name="context" eval="{
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_use_template': False,
|
||||
}"/>
|
||||
<field name="binding_model_id" ref="project.model_project_task"/>
|
||||
<field name="binding_view_types">list</field>
|
||||
@@ -440,7 +439,6 @@
|
||||
<field name="target">new</field>
|
||||
<field name="context" eval="{
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_use_template': False,
|
||||
}"/>
|
||||
<field name="binding_model_id" ref="project.model_project_project"/>
|
||||
<field name="binding_view_types">list</field>
|
||||
|
||||
@@ -419,10 +419,7 @@ class PurchaseOrder(models.Model):
|
||||
ctx = dict(self.env.context or {})
|
||||
ctx.update({
|
||||
'default_model': 'purchase.order',
|
||||
'active_model': 'purchase.order',
|
||||
'active_id': self.ids[0],
|
||||
'default_res_id': self.ids[0],
|
||||
'default_use_template': bool(template_id),
|
||||
'default_res_ids': self.ids,
|
||||
'default_template_id': template_id,
|
||||
'default_composition_mode': 'comment',
|
||||
'default_email_layout_xmlid': "mail.mail_notification_layout_with_responsible_signature",
|
||||
@@ -781,10 +778,7 @@ class PurchaseOrder(models.Model):
|
||||
ctx = dict(self.env.context or {})
|
||||
ctx.update({
|
||||
'default_model': 'purchase.order',
|
||||
'active_model': 'purchase.order',
|
||||
'active_id': self.ids[0],
|
||||
'default_res_id': self.ids[0],
|
||||
'default_use_template': bool(template_id),
|
||||
'default_res_ids': self.ids,
|
||||
'default_template_id': template_id,
|
||||
'default_composition_mode': 'comment',
|
||||
'default_email_layout_xmlid': "mail.mail_notification_layout_with_responsible_signature",
|
||||
|
||||
@@ -332,8 +332,7 @@ class Repair(models.Model):
|
||||
template_id = self.env.ref('repair.mail_template_repair_quotation').id
|
||||
ctx = {
|
||||
'default_model': 'repair.order',
|
||||
'default_res_id': self.id,
|
||||
'default_use_template': bool(template_id),
|
||||
'default_res_ids': self.ids,
|
||||
'default_template_id': template_id,
|
||||
'default_composition_mode': 'comment',
|
||||
'default_email_layout_xmlid': 'mail.mail_notification_light',
|
||||
|
||||
@@ -111,21 +111,20 @@ class ResCompany(models.Model):
|
||||
template = self.env.ref('sale.email_template_edi_sale', False)
|
||||
|
||||
message_composer = self.env['mail.compose.message'].with_context(
|
||||
default_use_template=bool(template),
|
||||
mark_so_as_sent=True,
|
||||
default_email_layout_xmlid='mail.mail_notification_layout_with_responsible_signature',
|
||||
proforma=self.env.context.get('proforma', False),
|
||||
force_email=True,
|
||||
).create({
|
||||
'res_id': sample_sales_order.id,
|
||||
'template_id': template and template.id or False,
|
||||
'model': 'sale.order',
|
||||
'composition_mode': 'comment'
|
||||
'res_ids': sample_sales_order.ids,
|
||||
'template_id': template.id if template else False,
|
||||
'model': sample_sales_order._name,
|
||||
'composition_mode': 'comment',
|
||||
})
|
||||
|
||||
# Simulate the onchange (like trigger in form the view)
|
||||
update_values = message_composer._onchange_template_id(
|
||||
template.id, 'comment', 'sale.order', sample_sales_order.id
|
||||
template.id, 'comment', sample_sales_order._name, sample_sales_order.ids
|
||||
)['value']
|
||||
message_composer.write(update_values)
|
||||
|
||||
|
||||
@@ -713,8 +713,7 @@ class SaleOrder(models.Model):
|
||||
lang = mail_template._render_lang(self.ids)[self.id]
|
||||
ctx = {
|
||||
'default_model': 'sale.order',
|
||||
'default_res_id': self.id,
|
||||
'default_use_template': bool(mail_template),
|
||||
'default_res_ids': self.ids,
|
||||
'default_template_id': mail_template.id if mail_template else None,
|
||||
'default_composition_mode': 'comment',
|
||||
'mark_so_as_sent': True,
|
||||
@@ -877,7 +876,6 @@ class SaleOrder(models.Model):
|
||||
if template.lang:
|
||||
lang = template._render_lang(self.ids)[self.id]
|
||||
ctx = {
|
||||
'default_use_template': bool(template_id),
|
||||
'default_template_id': template_id,
|
||||
'default_order_id': self.id,
|
||||
'mark_so_as_canceled': True,
|
||||
|
||||
@@ -460,7 +460,6 @@
|
||||
<field name="target">new</field>
|
||||
<field name="context" eval="{
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_use_template': False,
|
||||
}"/>
|
||||
<field name="binding_model_id" ref="stock.model_stock_picking"/>
|
||||
<field name="binding_view_types">list</field>
|
||||
|
||||
@@ -904,7 +904,6 @@ class Survey(models.Model):
|
||||
local_context = dict(
|
||||
self.env.context,
|
||||
default_survey_id=self.id,
|
||||
default_use_template=bool(template),
|
||||
default_template_id=template and template.id or False,
|
||||
default_email_layout_xmlid='mail.mail_notification_light',
|
||||
default_send_email=(self.access_mode != 'public'),
|
||||
|
||||
@@ -350,10 +350,9 @@ QUnit.module("test_mail", {}, function () {
|
||||
assert.deepEqual(
|
||||
{
|
||||
default_model: "mail.test.activity",
|
||||
default_res_id: mailTestActivityId2,
|
||||
default_res_ids: [mailTestActivityId2],
|
||||
default_subtype_xmlid: 'mail.mt_comment',
|
||||
default_template_id: mailTemplateId1,
|
||||
default_use_template: true,
|
||||
force_email: true,
|
||||
},
|
||||
action.context
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from unittest.mock import patch
|
||||
from ast import literal_eval
|
||||
from unittest.mock import DEFAULT, patch
|
||||
|
||||
from odoo.addons.mail.tests.common import mail_new_test_user
|
||||
from odoo.addons.mail.wizard.mail_compose_message import MailComposer
|
||||
from odoo.addons.test_mail.models.test_mail_models import MailTestTicket
|
||||
from odoo.addons.test_mail.tests.common import TestMailCommon, TestRecipients
|
||||
from odoo.exceptions import AccessError
|
||||
@@ -84,16 +86,16 @@ class TestMailComposer(TestMailCommon, TestRecipients):
|
||||
"""
|
||||
base_context = {
|
||||
'default_model': records._name,
|
||||
'default_res_ids': records.ids,
|
||||
}
|
||||
if len(records) == 1:
|
||||
base_context['default_composition_mode'] = 'comment'
|
||||
base_context['default_res_id'] = records.id
|
||||
else:
|
||||
base_context['default_composition_mode'] = 'mass_mail'
|
||||
base_context['active_ids'] = records.ids
|
||||
if add_web:
|
||||
base_context['active_model'] = records._name
|
||||
base_context['active_id'] = records[0].id
|
||||
base_context['active_ids'] = records.ids
|
||||
if values:
|
||||
base_context.update(**values)
|
||||
return base_context
|
||||
@@ -111,6 +113,7 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertFalse(composer_form.auto_delete_message)
|
||||
self.assertEqual(composer_form.author_id, self.env.user.partner_id)
|
||||
self.assertFalse(composer_form.body)
|
||||
self.assertFalse(composer_form.composition_batch)
|
||||
self.assertEqual(composer_form.composition_mode, 'comment')
|
||||
self.assertEqual(composer_form.email_from, self.env.user.email_formatted)
|
||||
self.assertFalse(composer_form.mail_server_id)
|
||||
@@ -119,7 +122,7 @@ class TestComposerForm(TestMailComposer):
|
||||
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)
|
||||
self.assertEqual(composer_form.res_id, self.test_record.id)
|
||||
self.assertEqual(literal_eval(composer_form.res_ids), self.test_record.ids)
|
||||
self.assertEqual(composer_form.subject, self.test_record._message_compute_subject())
|
||||
self.assertIn(f'Ticket for {self.test_record.name}', composer_form.subject,
|
||||
'Check effective content')
|
||||
@@ -187,6 +190,7 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertFalse(composer_form.auto_delete_message)
|
||||
self.assertEqual(composer_form.author_id, self.env.user.partner_id)
|
||||
self.assertEqual(composer_form.body, '<p>TemplateBody %s</p>' % self.test_record.name)
|
||||
self.assertFalse(composer_form.composition_batch)
|
||||
self.assertEqual(composer_form.composition_mode, 'comment')
|
||||
self.assertEqual(composer_form.email_from, self.user_employee_2.email_formatted)
|
||||
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
|
||||
@@ -195,10 +199,39 @@ class TestComposerForm(TestMailComposer):
|
||||
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)
|
||||
self.assertEqual(composer_form.res_id, self.test_record.id)
|
||||
self.assertEqual(literal_eval(composer_form.res_ids), self.test_record.ids)
|
||||
self.assertEqual(composer_form.subject, 'TemplateSubject %s' % self.test_record.name)
|
||||
self.assertEqual(composer_form.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
|
||||
@users('employee')
|
||||
def test_mail_composer_comment_wtpl_batch(self):
|
||||
""" Batch mode of composer in comment mode. """
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(
|
||||
self.test_records,
|
||||
add_web=True,
|
||||
default_composition_mode='comment',
|
||||
default_template_id=self.template.id),
|
||||
))
|
||||
# self.assertTrue(composer_form.auto_delete)
|
||||
self.assertFalse(composer_form.auto_delete) # FIXME: currently not taking template value
|
||||
self.assertFalse(composer_form.auto_delete_message)
|
||||
self.assertEqual(composer_form.author_id, self.env.user.partner_id)
|
||||
self.assertEqual(composer_form.body, self.template.body_html,
|
||||
'MailComposer: comment in batch mode should have template raw body if template')
|
||||
self.assertTrue(composer_form.composition_batch)
|
||||
self.assertEqual(composer_form.composition_mode, 'comment')
|
||||
self.assertEqual(composer_form.email_from, self.template.email_from,
|
||||
'MailComposer: comment in batch mode should have template raw email_from if template')
|
||||
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_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)
|
||||
self.assertEqual(literal_eval(composer_form.res_ids), self.test_records.ids)
|
||||
self.assertEqual(composer_form.subject, self.template.subject,
|
||||
'MailComposer: comment in batch mode should have template raw subject if template')
|
||||
|
||||
@users('employee')
|
||||
def test_mail_composer_comment_wtpl_norecords(self):
|
||||
""" Test specific case when running without records, to see the rendering
|
||||
@@ -213,6 +246,7 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertFalse(composer_form.auto_delete_message)
|
||||
self.assertEqual(composer_form.author_id, self.env.user.partner_id)
|
||||
self.assertEqual(composer_form.body, '<p>TemplateBody </p>')
|
||||
self.assertFalse(composer_form.composition_batch)
|
||||
self.assertEqual(composer_form.composition_mode, 'comment')
|
||||
self.assertEqual(composer_form.email_from, self.env.user.partner_id.email_formatted)
|
||||
self.assertEqual(composer_form.mail_server_id, self.mail_server_domain)
|
||||
@@ -221,7 +255,7 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertFalse(composer_form.record_name)
|
||||
self.assertEqual(composer_form.reply_to, 'info@test.example.com')
|
||||
self.assertFalse(composer_form.reply_to_force_new)
|
||||
self.assertFalse(composer_form.res_id)
|
||||
self.assertFalse(composer_form.res_ids)
|
||||
self.assertEqual(composer_form.subject, 'TemplateSubject ')
|
||||
self.assertEqual(composer_form.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
|
||||
@@ -234,6 +268,7 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertFalse(composer_form.auto_delete_message)
|
||||
self.assertEqual(composer_form.author_id, self.env.user.partner_id)
|
||||
self.assertFalse(composer_form.body)
|
||||
self.assertTrue(composer_form.composition_batch)
|
||||
self.assertEqual(composer_form.composition_mode, 'mass_mail')
|
||||
self.assertEqual(composer_form.email_from, self.env.user.email_formatted)
|
||||
self.assertFalse(composer_form.mail_server_id)
|
||||
@@ -241,8 +276,7 @@ class TestComposerForm(TestMailComposer):
|
||||
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)
|
||||
self.assertEqual(composer_form.res_id, self.test_records[0].id,
|
||||
'MailComposer: even in mass mode web active_id presence may add a res_id')
|
||||
self.assertEqual(sorted(literal_eval(composer_form.res_ids)), sorted(self.test_records.ids))
|
||||
self.assertFalse(composer_form.subject, 'MailComposer: mass mode should have void default subject if no template')
|
||||
self.assertEqual(composer_form.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
|
||||
@@ -257,6 +291,7 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertEqual(composer_form.author_id, self.env.user.partner_id)
|
||||
self.assertEqual(composer_form.body, self.template.body_html,
|
||||
'MailComposer: mass mode should have template raw body if template')
|
||||
self.assertTrue(composer_form.composition_batch)
|
||||
self.assertEqual(composer_form.composition_mode, 'mass_mail')
|
||||
self.assertEqual(composer_form.email_from, self.template.email_from,
|
||||
'MailComposer: mass mode should have template raw email_from if template')
|
||||
@@ -265,8 +300,7 @@ class TestComposerForm(TestMailComposer):
|
||||
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)
|
||||
self.assertEqual(composer_form.res_id, self.test_records[0].id,
|
||||
'MailComposer: even in mass mode web active_id presence may add a res_id')
|
||||
self.assertEqual(sorted(literal_eval(composer_form.res_ids)), sorted(self.test_records.ids))
|
||||
self.assertEqual(composer_form.subject, self.template.subject,
|
||||
'MailComposer: mass mode should have template raw subject if template')
|
||||
self.assertEqual(composer_form.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
@@ -286,6 +320,7 @@ class TestComposerForm(TestMailComposer):
|
||||
self.assertEqual(composer_form.author_id, self.env.user.partner_id)
|
||||
self.assertEqual(composer_form.body, self.template.body_html,
|
||||
'MailComposer: mass mode should have template raw body if template')
|
||||
self.assertFalse(composer_form.composition_batch)
|
||||
self.assertEqual(composer_form.composition_mode, 'mass_mail')
|
||||
self.assertEqual(composer_form.email_from, self.template.email_from,
|
||||
'MailComposer: mass mode should have template raw email_from if template')
|
||||
@@ -294,7 +329,7 @@ class TestComposerForm(TestMailComposer):
|
||||
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)
|
||||
self.assertEqual(composer_form.res_id, 0)
|
||||
self.assertFalse(composer_form.res_ids)
|
||||
self.assertEqual(composer_form.subject, self.template.subject,
|
||||
'MailComposer: mass mode should have template raw subject if template')
|
||||
self.assertEqual(composer_form.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
@@ -321,7 +356,8 @@ class TestComposerInternals(TestMailComposer):
|
||||
attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])])
|
||||
self.assertEqual(len(attachs), 3)
|
||||
|
||||
for composition_mode, batch in (('comment', False), ('mass_mail', True)):
|
||||
for composition_mode, batch in (('comment', False), ('comment', True),
|
||||
('mass_mail', False), ('mass_mail', True)):
|
||||
with self.subTest(composition_mode=composition_mode, batch=batch):
|
||||
test_records = self.test_records if batch else self.test_record
|
||||
ctx = self._get_web_context(
|
||||
@@ -337,7 +373,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
# values coming from template: attachment_ids + report in comment
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(len(composer.attachment_ids), 4)
|
||||
for attach in attachs:
|
||||
self.assertIn(attach, composer.attachment_ids)
|
||||
@@ -358,7 +394,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
# currently onchange necessary
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.attachment_ids, attachs + generated,
|
||||
'TODO: Values are kept (should be reset ?)')
|
||||
else:
|
||||
@@ -370,7 +406,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
# currently onchange necessary
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.attachment_ids, attachs + generated,
|
||||
'TODO: Values are kept (should be reset ?)')
|
||||
else:
|
||||
@@ -386,7 +422,8 @@ class TestComposerInternals(TestMailComposer):
|
||||
'email_from': False,
|
||||
})
|
||||
|
||||
for composition_mode, batch in (('comment', False), ('mass_mail', True)):
|
||||
for composition_mode, batch in (('comment', False), ('comment', True),
|
||||
('mass_mail', False), ('mass_mail', True)):
|
||||
with self.subTest(composition_mode=composition_mode, batch=batch):
|
||||
test_records = self.test_records if batch else self.test_record
|
||||
ctx = self._get_web_context(
|
||||
@@ -400,6 +437,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
|
||||
# default values are current user
|
||||
self.assertEqual(composer.author_id, self.env.user.partner_id)
|
||||
self.assertEqual(composer.composition_mode, composition_mode)
|
||||
self.assertEqual(composer.email_from, self.env.user.email_formatted)
|
||||
|
||||
# author values reset email (FIXME: currently not synchronized)
|
||||
@@ -414,12 +452,12 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer._onchange_template_id_wrapper()
|
||||
self.assertEqual(composer.author_id, self.env.user.partner_id,
|
||||
'MailComposer: should take value given by user')
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.email_from, self.test_record.user_id.email_formatted,
|
||||
'MailComposer: should take email_from rendered from template')
|
||||
f'MailComposer: should take email_from rendered from template ({composition_mode}-{batch})')
|
||||
else:
|
||||
self.assertEqual(composer.email_from, self.template.email_from,
|
||||
'MailComposer: should take email_from raw from template')
|
||||
f'MailComposer: should take email_from raw from template ({composition_mode}-{batch})')
|
||||
|
||||
# manual values are kept over template values
|
||||
composer.write({'email_from': self.test_from})
|
||||
@@ -431,7 +469,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer.write({'template_id': template_void.id})
|
||||
# currently onchange necessary
|
||||
composer._onchange_template_id_wrapper()
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.author_id, self.env.user.partner_id)
|
||||
self.assertEqual(composer.email_from, self.test_from)
|
||||
else:
|
||||
@@ -442,7 +480,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer.write({'template_id': False})
|
||||
# currently onchange necessary
|
||||
composer._onchange_template_id_wrapper()
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.author_id, self.env.user.partner_id)
|
||||
self.assertEqual(composer.email_from, self.env.user.email_formatted)
|
||||
else:
|
||||
@@ -458,7 +496,8 @@ class TestComposerInternals(TestMailComposer):
|
||||
'auto_delete': False,
|
||||
})
|
||||
|
||||
for composition_mode, batch in (('comment', False), ('mass_mail', True)):
|
||||
for composition_mode, batch in (('comment', False), ('comment', True),
|
||||
('mass_mail', False), ('mass_mail', True)):
|
||||
with self.subTest(composition_mode=composition_mode, batch=batch):
|
||||
test_records = self.test_records if batch else self.test_record
|
||||
ctx = self._get_web_context(
|
||||
@@ -539,7 +578,8 @@ class TestComposerInternals(TestMailComposer):
|
||||
'subject': False,
|
||||
})
|
||||
|
||||
for composition_mode, batch in (('comment', False), ('mass_mail', True)):
|
||||
for composition_mode, batch in (('comment', False), ('comment', True),
|
||||
('mass_mail', False), ('mass_mail', True)):
|
||||
with self.subTest(composition_mode=composition_mode, batch=batch):
|
||||
test_records = self.test_records if batch else self.test_record
|
||||
ctx = self._get_web_context(
|
||||
@@ -557,11 +597,11 @@ class TestComposerInternals(TestMailComposer):
|
||||
# creation values are taken
|
||||
self.assertEqual(composer.body, '<p>Test Body <t t-out="record.name>/></p>')
|
||||
self.assertEqual(composer.mail_server_id, self.mail_server_global)
|
||||
self.assertEqual(composer.subject, 'My amazing subject for {{ record.name }}')
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.record_name, self.test_record.name)
|
||||
else:
|
||||
self.assertFalse(composer.record_name)
|
||||
self.assertEqual(composer.subject, 'My amazing subject for {{ record.name }}')
|
||||
|
||||
# changing template should update its content
|
||||
composer.write({'template_id': self.template.id})
|
||||
@@ -569,7 +609,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
# values come from template
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.body, f'<p>TemplateBody {self.test_record.name}</p>')
|
||||
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
|
||||
self.assertEqual(composer.record_name, self.test_record.name)
|
||||
@@ -598,7 +638,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
# currently onchange necessary
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.body, '<p>Back to my amazing body <t t-out="record.name>/></p>')
|
||||
self.assertEqual(composer.mail_server_id, self.mail_server_global)
|
||||
self.assertEqual(composer.record_name, 'Manual update')
|
||||
@@ -618,7 +658,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
# values are reset with default_get call, if it returns value
|
||||
# (aka subject for comment mode), and not record_name because
|
||||
# it was forgotten probably
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertFalse(composer.body)
|
||||
# self.assertFalse(composer.mail_server_id.id)
|
||||
self.assertEqual(composer.mail_server_id, self.mail_server_global,
|
||||
@@ -650,7 +690,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
# values come from template
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.body, f'<p>TemplateBody {self.test_record.name}</p>')
|
||||
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
|
||||
self.assertEqual(composer.record_name, self.test_record.name)
|
||||
@@ -670,7 +710,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
# values come from template
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.body, f'<p>TemplateBody {self.test_record.name}</p>')
|
||||
self.assertEqual(composer.mail_server_id, self.template.mail_server_id)
|
||||
self.assertEqual(composer.record_name, self.test_record.name)
|
||||
@@ -713,7 +753,8 @@ class TestComposerInternals(TestMailComposer):
|
||||
'reply_to': False,
|
||||
})
|
||||
|
||||
for composition_mode, batch in (('comment', False), ('mass_mail', True)):
|
||||
for composition_mode, batch in (('comment', False), ('comment', True),
|
||||
('mass_mail', False), ('mass_mail', True)):
|
||||
with self.subTest(composition_mode=composition_mode, batch=batch):
|
||||
self.assertFalse(
|
||||
self.env['res.partner'].search([
|
||||
@@ -769,7 +810,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
)
|
||||
|
||||
# values come from template
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(len(new_partners), 2)
|
||||
self.assertEqual(composer.partner_ids, self.partner_1 + new_partners, 'Template took customer_id as set on record')
|
||||
self.assertEqual(composer.reply_to, 'info@test.example.com', 'Template was rendered')
|
||||
@@ -790,7 +831,7 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
# values are kept, should probably be reset
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.partner_ids, self.partner_admin,
|
||||
'TODO: Values are kept (should be reset ?)')
|
||||
self.assertEqual(composer.reply_to, 'info@test.example.com',
|
||||
@@ -812,11 +853,11 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
# values come from template
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.partner_ids, self.partner_1 + new_partners)
|
||||
else:
|
||||
self.assertFalse(composer.partner_ids)
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.reply_to, "info@test.example.com")
|
||||
else:
|
||||
self.assertEqual(composer.reply_to, self.template.reply_to)
|
||||
@@ -832,11 +873,11 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
# values come from template
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.partner_ids, self.partner_1 + new_partners)
|
||||
else:
|
||||
self.assertFalse(composer.partner_ids)
|
||||
if composition_mode == 'comment':
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.reply_to, "info@test.example.com")
|
||||
else:
|
||||
self.assertEqual(composer.reply_to, self.template.reply_to)
|
||||
@@ -1137,10 +1178,11 @@ class TestComposerResultsComment(TestMailComposer):
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_complete(self):
|
||||
""" Test a posting process using a complex template, holding several
|
||||
additional recipients and attachments.
|
||||
additional recipients and attachments. It is done in monorecord and
|
||||
in batch since this is now supported.
|
||||
|
||||
This tests notifies: 2 new email_to (+ 1 duplicated), 1 email_cc,
|
||||
test_record followers and partner_admin added in partner_to."""
|
||||
test_records followers and partner_admin added in partner_to."""
|
||||
attachment_data = self._generate_attachments_data(2, self.template._name, self.template.id)
|
||||
email_to_1 = 'test.to.1@test.example.com'
|
||||
email_to_2 = 'test.to.2@test.example.com'
|
||||
@@ -1158,82 +1200,97 @@ class TestComposerResultsComment(TestMailComposer):
|
||||
attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])])
|
||||
self.assertEqual(len(attachs), 2)
|
||||
|
||||
# ensure initial data
|
||||
self.assertEqual(self.test_record.user_id, self.user_employee_2)
|
||||
self.assertEqual(self.test_record.message_partner_ids, self.partner_employee_2)
|
||||
for test_records, batch in ((self.test_record, False), (self.test_records, True)):
|
||||
with self.subTest(test_records=test_records, batch=batch):
|
||||
# ensure initial data
|
||||
self.assertEqual(len(test_records.customer_id), len(test_records))
|
||||
self.assertEqual(test_records.user_id, self.user_employee_2)
|
||||
self.assertEqual(test_records.message_partner_ids, self.partner_employee_2)
|
||||
|
||||
# open a composer and run it in comment mode
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_record, add_web=True,
|
||||
default_template_id=self.template.id)
|
||||
))
|
||||
composer = composer_form.save()
|
||||
self.assertFalse(composer.reply_to_force_new, 'Mail: thread-enabled models should use auto thread by default')
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False), self.mock_mail_app():
|
||||
composer._action_send_mail()
|
||||
# open a composer and run it in comment mode
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(test_records, add_web=True,
|
||||
default_composition_mode='comment',
|
||||
default_template_id=self.template.id)
|
||||
))
|
||||
composer = composer_form.save()
|
||||
self.assertFalse(composer.reply_to_force_new, 'Mail: thread-enabled models should use auto thread by default')
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False), self.mock_mail_app():
|
||||
composer._action_send_mail()
|
||||
|
||||
# check new partners have been created based on emails given
|
||||
new_partners = self.env['res.partner'].search([
|
||||
('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1])
|
||||
])
|
||||
self.assertEqual(len(new_partners), 3)
|
||||
self.assertEqual(set(new_partners.mapped('email')),
|
||||
set(['test.to.1@test.example.com', 'test.to.2@test.example.com', 'test.cc.1@test.example.com'])
|
||||
)
|
||||
# check new partners have been created based on emails given
|
||||
new_partners = self.env['res.partner'].search([
|
||||
('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1])
|
||||
])
|
||||
self.assertEqual(len(new_partners), 3)
|
||||
self.assertEqual(set(new_partners.mapped('email')),
|
||||
set(['test.to.1@test.example.com', 'test.to.2@test.example.com', 'test.cc.1@test.example.com'])
|
||||
)
|
||||
|
||||
# global outgoing: one mail.mail (all customer recipients, then all employee recipients)
|
||||
# and 5 emails, and 1 inbox notification (admin)
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail')
|
||||
self.assertEqual(len(self._mails), 5, 'Should have sent 5 emails, one per recipient')
|
||||
# global outgoing: one mail.mail (all customer recipients, then all employee recipients)
|
||||
# and 5 emails, and 1 inbox notification (admin)
|
||||
self.assertEqual(len(self._new_mails), 2 * len(test_records),
|
||||
'Should have created 1 mail.mail per group per record')
|
||||
# monorecord: force_send notifications
|
||||
if not batch:
|
||||
self.assertEqual(len(self._mails), 5, 'Should have sent 5 emails, one per recipient per record')
|
||||
self.assertEqual(self._new_mails.mapped('state'), ['sent'] * len(self._new_mails))
|
||||
# mutlirecord: use email queue
|
||||
else:
|
||||
self.assertEqual(len(self._mails), 0, 'Should have put emails in queue and not sent any emails')
|
||||
self.assertEqual(self._new_mails.mapped('state'), ['outgoing'] * len(self._new_mails))
|
||||
# simulate cron sending emails
|
||||
self.env['mail.mail'].sudo().process_email_queue()
|
||||
|
||||
# template is sent only to partners (email_to are transformed)
|
||||
message = self.test_record.message_ids[0]
|
||||
self.assertMailMail(self.partner_employee_2, 'sent',
|
||||
mail_message=message,
|
||||
author=self.partner_employee, # author != email_from (template sets only email_from)
|
||||
email_values={
|
||||
'body_content': 'TemplateBody %s' % self.test_record.name,
|
||||
'email_from': self.test_record.user_id.email_formatted, # set by template
|
||||
'subject': 'TemplateSubject %s' % self.test_record.name,
|
||||
'attachments_info': [
|
||||
{'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'},
|
||||
{'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'},
|
||||
{'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'},
|
||||
]
|
||||
},
|
||||
fields_values={
|
||||
'mail_server_id': self.mail_server_domain,
|
||||
},
|
||||
)
|
||||
self.assertMailMail(self.test_record.customer_id + new_partners, 'sent',
|
||||
mail_message=message,
|
||||
author=self.partner_employee, # author != email_from (template sets only email_from)
|
||||
email_values={
|
||||
'body_content': 'TemplateBody %s' % self.test_record.name,
|
||||
'email_from': self.test_record.user_id.email_formatted, # set by template
|
||||
'subject': 'TemplateSubject %s' % self.test_record.name,
|
||||
'attachments_info': [
|
||||
{'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'},
|
||||
{'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'},
|
||||
{'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'},
|
||||
]
|
||||
},
|
||||
fields_values={
|
||||
'mail_server_id': self.mail_server_domain,
|
||||
},
|
||||
)
|
||||
# template is sent only to partners (email_to are transformed)
|
||||
for test_record in test_records:
|
||||
message = test_record.message_ids[0]
|
||||
self.assertMailMail(self.partner_employee_2, 'sent',
|
||||
mail_message=message,
|
||||
author=self.partner_employee, # author != email_from (template sets only email_from)
|
||||
email_values={
|
||||
'body_content': f'TemplateBody {test_record.name}',
|
||||
'email_from': test_record.user_id.email_formatted, # set by template
|
||||
'subject': f'TemplateSubject {test_record.name}',
|
||||
'attachments_info': [
|
||||
{'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'},
|
||||
{'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'},
|
||||
{'name': f'TestReport for {test_record.name}.html', 'type': 'text/plain'},
|
||||
]
|
||||
},
|
||||
fields_values={
|
||||
'mail_server_id': self.mail_server_domain,
|
||||
},
|
||||
)
|
||||
self.assertMailMail(test_record.customer_id + new_partners, 'sent',
|
||||
mail_message=message,
|
||||
author=self.partner_employee, # author != email_from (template sets only email_from)
|
||||
email_values={
|
||||
'body_content': f'TemplateBody {test_record.name}',
|
||||
'email_from': test_record.user_id.email_formatted, # set by template
|
||||
'subject': f'TemplateSubject {test_record.name}',
|
||||
'attachments_info': [
|
||||
{'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'},
|
||||
{'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'},
|
||||
{'name': f'TestReport for {test_record.name}.html', 'type': 'text/plain'},
|
||||
]
|
||||
},
|
||||
fields_values={
|
||||
'mail_server_id': self.mail_server_domain,
|
||||
},
|
||||
)
|
||||
|
||||
# message is posted and notified admin
|
||||
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
self.assertNotified(message, [{'partner': self.partner_admin, 'is_read': False, 'type': 'inbox'}])
|
||||
# attachments are copied on message and linked to document
|
||||
self.assertEqual(
|
||||
set(message.attachment_ids.mapped('name')),
|
||||
set(['AttFileName_00.txt', 'AttFileName_01.txt', 'TestReport for %s.html' % self.test_record.name])
|
||||
)
|
||||
self.assertEqual(set(message.attachment_ids.mapped('res_model')), set([self.test_record._name]))
|
||||
self.assertEqual(set(message.attachment_ids.mapped('res_id')), set(self.test_record.ids))
|
||||
self.assertTrue(all(attach not in message.attachment_ids for attach in attachs), 'Should have copied attachments')
|
||||
# message is posted and notified admin
|
||||
self.assertEqual(message.subtype_id, self.env.ref('mail.mt_comment'))
|
||||
self.assertNotified(message, [{'partner': self.partner_admin, 'is_read': False, 'type': 'inbox'}])
|
||||
# attachments are copied on message and linked to document
|
||||
self.assertEqual(
|
||||
set(message.attachment_ids.mapped('name')),
|
||||
set(['AttFileName_00.txt', 'AttFileName_01.txt', f'TestReport for {test_record.name}.html'])
|
||||
)
|
||||
self.assertEqual(set(message.attachment_ids.mapped('res_model')), set([test_record._name]))
|
||||
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')
|
||||
|
||||
|
||||
|
||||
@@ -1563,51 +1620,66 @@ class TestComposerResultsMass(TestMailComposer):
|
||||
@users('employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_recipients(self):
|
||||
""" Test various combinations of recipients: active_domain, active_id,
|
||||
""" Test various combinations of recipients: res_domain, active_id,
|
||||
active_ids, ... to ensure fallback behavior are working. """
|
||||
# 1: active_domain
|
||||
# 1: active ids
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
default_template_id=self.template.id,
|
||||
active_ids=[],
|
||||
default_use_active_domain=True,
|
||||
default_active_domain=[('id', 'in', self.test_records.ids)])
|
||||
active_ids=self.test_records.ids,
|
||||
default_composition_mode='mass_mail',
|
||||
default_model=self.test_records._name,
|
||||
default_template_id=self.template.id,
|
||||
))
|
||||
composer = composer_form.save()
|
||||
self.assertEqual(sorted(literal_eval(composer.res_ids)), sorted(self.test_records.ids))
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer._action_send_mail()
|
||||
|
||||
# should create emails in a single batch
|
||||
self.assertEqual(self.build_email_mocked.call_count, 2, 'One build email per outgoing email')
|
||||
self.assertEqual(self.mail_mail_create_mocked.call_count, 2, 'Emails are anyway created in a singleton loop')
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record based on active_ids')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record based on on active_ids')
|
||||
|
||||
for record in self.test_records:
|
||||
# template is sent directly using customer field, even if author is partner_employee
|
||||
self.assertSentEmail(self.partner_employee_2.email_formatted,
|
||||
record.customer_id)
|
||||
|
||||
# 2: active_domain not taken into account if use_active_domain is False
|
||||
# 2: default_res_ids + active_ids -> res_ids takes lead
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
self._get_web_context(self.test_record, add_web=False,
|
||||
default_composition_mode='mass_mail',
|
||||
default_res_ids=self.test_record.ids,
|
||||
default_template_id=self.template.id,
|
||||
default_use_active_domain=False,
|
||||
default_active_domain=[('id', 'in', -1)])
|
||||
active_ids=self.test_records.ids,
|
||||
)
|
||||
))
|
||||
composer = composer_form.save()
|
||||
self.assertEqual(literal_eval(composer.res_ids), self.test_record.ids)
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer._action_send_mail()
|
||||
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record')
|
||||
self.assertEqual(len(self._new_mails), 1, 'Should have taken default_res_ids (1 record)')
|
||||
self.assertEqual(len(self._mails), 1, 'Should have taken default_res_ids (1 record)')
|
||||
|
||||
# template is sent directly using customer field, even if author is partner_employee
|
||||
self.assertSentEmail(self.partner_employee_2.email_formatted,
|
||||
self.test_record.customer_id)
|
||||
|
||||
# 3: fallback on active_id if not active_ids
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
self._get_web_context(self.test_records, add_web=True,
|
||||
default_template_id=self.template.id,
|
||||
active_ids=[])
|
||||
active_id=self.test_record.id,
|
||||
default_composition_mode='mass_mail',
|
||||
default_model=self.test_records._name,
|
||||
default_template_id=self.template.id,
|
||||
))
|
||||
composer = composer_form.save()
|
||||
self.assertEqual(literal_eval(composer.res_ids), self.test_record.ids)
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=False):
|
||||
composer._action_send_mail()
|
||||
|
||||
@@ -1615,7 +1687,54 @@ class TestComposerResultsMass(TestMailComposer):
|
||||
self.assertEqual(len(self._new_mails), 1, 'Should have created 1 mail.mail per record')
|
||||
self.assertEqual(len(self._mails), 1, 'Should have sent 1 email per record')
|
||||
|
||||
# 3: void is void: raise in comment mode, just don't send anything in mass mail mode
|
||||
# 4: _batch_size limit for active_ids
|
||||
with patch.object(MailComposer, '_batch_size', new=1):
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
active_ids=self.test_records.ids,
|
||||
default_composition_mode='mass_mail',
|
||||
default_model=self.test_records._name,
|
||||
default_template_id=self.template.id,
|
||||
))
|
||||
composer = composer_form.save()
|
||||
self.assertTrue(composer.composition_batch)
|
||||
self.assertEqual(composer.composition_mode, 'mass_mail')
|
||||
self.assertFalse(composer.res_ids)
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer._action_send_mail()
|
||||
|
||||
# should create emails in 2 batches of 1
|
||||
self.assertEqual(self.build_email_mocked.call_count, 2)
|
||||
self.assertEqual(self.mail_mail_create_mocked.call_count, 2)
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record based on active_ids')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record based on on active_ids')
|
||||
|
||||
# 5: mail.batch_size config parameter support, for sending only
|
||||
self.env['ir.config_parameter'].sudo().set_param('mail.batch_size', 1)
|
||||
with patch.object(MailComposer, '_batch_size', new=50):
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
active_ids=self.test_records.ids,
|
||||
default_composition_mode='mass_mail',
|
||||
default_model=self.test_records._name,
|
||||
default_template_id=self.template.id,
|
||||
))
|
||||
composer = composer_form.save()
|
||||
self.assertTrue(composer.composition_batch)
|
||||
self.assertEqual(composer.composition_mode, 'mass_mail')
|
||||
self.assertEqual(sorted(literal_eval(composer.res_ids)), sorted(self.test_records.ids))
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True):
|
||||
composer._action_send_mail()
|
||||
|
||||
# should create emails in 2 batches of 1
|
||||
self.assertEqual(self.build_email_mocked.call_count, 2)
|
||||
self.assertEqual(self.mail_mail_create_mocked.call_count, 2)
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record based on active_ids')
|
||||
self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record based on on active_ids')
|
||||
|
||||
# 6: void is void: raise in comment mode, just don't send anything in mass mail mode
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
default_model='mail.test.ticket',
|
||||
default_template_id=self.template.id
|
||||
@@ -1637,6 +1756,41 @@ class TestComposerResultsMass(TestMailComposer):
|
||||
composer._action_send_mail()
|
||||
self.assertNotSentEmail()
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_recipients_res_domain(self):
|
||||
""" Test specific use case of res_domain usage, in combination with
|
||||
res_domain_user_id allowing to set the evaluation environment for
|
||||
the domain. """
|
||||
def _search_as_employee_2(recordset, *args, **kwargs):
|
||||
""" Mock to return only one record, simulating a specific ir.rule """
|
||||
if recordset.env.uid == self.user_employee_2.id:
|
||||
return self.test_records[0]
|
||||
return DEFAULT
|
||||
|
||||
composer_form = Form(self.env['mail.compose.message'].with_context(
|
||||
default_composition_mode='mass_mail',
|
||||
default_model=self.test_records._name,
|
||||
default_res_domain=[('id', 'in', self.test_records.ids)],
|
||||
default_res_domain_user_id=self.user_employee_2.id,
|
||||
default_template_id=self.template.id,
|
||||
))
|
||||
composer = composer_form.save()
|
||||
self.assertEqual(literal_eval(composer.res_domain), [('id', 'in', self.test_records.ids)])
|
||||
self.assertEqual(composer.res_domain_user_id, self.user_employee_2)
|
||||
|
||||
with self.mock_mail_gateway(mail_unlink_sent=True), \
|
||||
patch.object(MailTestTicket, 'search', autospec=True, side_effect=_search_as_employee_2):
|
||||
composer._action_send_mail()
|
||||
|
||||
# global outgoing
|
||||
self.assertEqual(len(self._new_mails), 1, 'Should have created 1 mail.mail, search done by employee 2')
|
||||
self.assertEqual(len(self._mails), 1, 'Should have sent 1 email, search done by employee 2')
|
||||
|
||||
# template is sent directly using customer field, whatever the author
|
||||
self.assertSentEmail(self.partner_employee_2.email_formatted,
|
||||
self.test_records[0].customer_id)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail')
|
||||
def test_mail_composer_wtpl_reply_to_force_new(self):
|
||||
|
||||
@@ -341,7 +341,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
composer = self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
}).create({
|
||||
'body': '<p>Test Body</p>',
|
||||
'partner_ids': [(4, customer_id)],
|
||||
@@ -361,7 +361,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
composer = self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
}).create({
|
||||
'attachment_ids': attachments.ids,
|
||||
'body': '<p>Test Body</p>',
|
||||
@@ -383,7 +383,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
})
|
||||
)
|
||||
composer_form.body = '<p>Test Body</p>'
|
||||
@@ -408,9 +408,9 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
|
||||
with self.assertQueryCount(__system__=3, employee=3):
|
||||
composer = self.env['mail.compose.message'].with_context({
|
||||
'active_ids': test_records.ids,
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_model': test_records._name,
|
||||
'default_res_ids': test_records.ids,
|
||||
'default_template_id': test_template.id,
|
||||
}).create({})
|
||||
composer._onchange_template_id_wrapper()
|
||||
@@ -430,7 +430,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
composer = self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
'mail_auto_delete': False,
|
||||
}).create({
|
||||
'body': '<p>Test Body</p>',
|
||||
@@ -451,7 +451,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
composer = self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
'default_template_id': test_template.id,
|
||||
}).create({})
|
||||
composer._onchange_template_id_wrapper()
|
||||
@@ -476,7 +476,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
composer = self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
'default_template_id': test_template.id,
|
||||
}).create({})
|
||||
composer._onchange_template_id_wrapper()
|
||||
@@ -507,7 +507,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
'default_template_id': test_template.id,
|
||||
})
|
||||
)
|
||||
@@ -537,7 +537,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
|
||||
self.env['mail.compose.message'].with_context({
|
||||
'default_composition_mode': 'comment',
|
||||
'default_model': test_record._name,
|
||||
'default_res_id': test_record.id,
|
||||
'default_res_ids': test_record.ids,
|
||||
'default_template_id': test_template.id,
|
||||
})
|
||||
)
|
||||
@@ -883,9 +883,9 @@ class TestMailComplexPerformance(BaseMailPerformance):
|
||||
|
||||
with self.assertQueryCount(__system__=3, employee=3):
|
||||
composer = self.env['mail.compose.message'].with_context({
|
||||
'active_ids': test_records.ids,
|
||||
'default_composition_mode': 'mass_mail',
|
||||
'default_model': test_records._name,
|
||||
'default_res_ids': test_records.ids,
|
||||
'default_template_id': test_template.id,
|
||||
}).create({})
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
@@ -218,9 +218,8 @@ class TestPortalFlow(MailCommon, HttpCase):
|
||||
'default_composition_mode': 'comment',
|
||||
'default_email_layout_xmlid': 'mail.mail_notification_layout_with_responsible_signature',
|
||||
'default_model': self.record_portal._name,
|
||||
'default_res_id': self.record_portal.id,
|
||||
'default_res_ids': self.record_portal.ids,
|
||||
'default_template_id': template_id,
|
||||
'default_use_template': template_id,
|
||||
'force_email': True,
|
||||
'lang': '{{ object.partner_id.lang }}',
|
||||
})
|
||||
|
||||
@@ -167,7 +167,7 @@ class WebsiteVisitor(models.Model):
|
||||
def _prepare_message_composer_context(self):
|
||||
return {
|
||||
'default_model': 'res.partner',
|
||||
'default_res_id': self.partner_id.id,
|
||||
'default_res_ids': self.partner_id.ids,
|
||||
'default_partner_ids': [self.partner_id.id],
|
||||
}
|
||||
|
||||
@@ -178,7 +178,6 @@ class WebsiteVisitor(models.Model):
|
||||
visitor_composer_ctx = self._prepare_message_composer_context()
|
||||
compose_form = self.env.ref('mail.email_compose_message_wizard_form', False)
|
||||
compose_ctx = dict(
|
||||
default_use_template=False,
|
||||
default_composition_mode='comment',
|
||||
)
|
||||
compose_ctx.update(**visitor_composer_ctx)
|
||||
|
||||
@@ -352,12 +352,10 @@ class SaleOrder(models.Model):
|
||||
'context': {
|
||||
'default_composition_mode': 'mass_mail' if len(self.ids) > 1 else 'comment',
|
||||
'default_email_layout_xmlid': 'mail.mail_notification_layout_with_responsible_signature',
|
||||
'default_res_id': self.ids[0],
|
||||
'default_res_ids': self.ids,
|
||||
'default_model': 'sale.order',
|
||||
'default_use_template': bool(template_id),
|
||||
'default_template_id': template_id,
|
||||
'website_sale_send_recovery_email': True,
|
||||
'active_ids': self.ids,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -651,7 +651,6 @@ class Channel(models.Model):
|
||||
local_context = dict(
|
||||
self.env.context,
|
||||
default_channel_id=self.id,
|
||||
default_use_template=bool(template),
|
||||
default_template_id=template and template.id or False,
|
||||
default_email_layout_xmlid='website_slides.mail_notification_channel_invite',
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user