[REF] mail: move composer 'attachment_ids' field from onchange to computed
RATIONALE Remove _onchange_template_id / defaults usage for composer fields. It lacks control and make value computation hard to understand and predict. PURPOSE Split _onchange_template_id + default_get + get_record_data usage into editable computed stored fields. Each field should have its own method with its triggers (template, composition mode, ...). SPECIFICATIONS Improve composer to correctly compute fields ``attachment_ids`` and reports (``report_template_ids`` on template that generates reports) in compute method. Computation is based on template and composition mode. In monorecord comment mode, template is used to generate attachments based on both attachment_ids of template, and reports coming from report_template_ids. Those are generated based on the current record to display. As template generation returns a list of tuples, new attachments are created on the fly during the compute. In batch or email mode, only attachment_ids from template are used on the composer. Reports will be generated at sending time. When template is removed, attachments are reset. Task-2088884 (Mail: Use editable computed stored fields in composer) Part-of: odoo/odoo#107356
This commit is contained in:
@@ -98,7 +98,8 @@ class MailComposer(models.TransientModel):
|
||||
template_id = fields.Many2one('mail.template', 'Use template', domain="[('model', '=', model)]")
|
||||
attachment_ids = fields.Many2many(
|
||||
'ir.attachment', 'mail_compose_message_ir_attachments_rel',
|
||||
'wizard_id', 'attachment_id', 'Attachments')
|
||||
'wizard_id', 'attachment_id', string='Attachments',
|
||||
compute='_compute_attachment_ids', readonly=False, store=True)
|
||||
email_layout_xmlid = fields.Char('Email Notification Layout', copy=False)
|
||||
email_add_signature = fields.Boolean(default=True)
|
||||
# origin
|
||||
@@ -226,6 +227,48 @@ class MailComposer(models.TransientModel):
|
||||
if not composer.template_id:
|
||||
composer.body = False
|
||||
|
||||
@api.depends('composition_mode', 'model', 'res_domain', 'res_ids', 'template_id')
|
||||
def _compute_attachment_ids(self):
|
||||
""" Computation is based on template and composition mode. In monorecord
|
||||
comment mode, template is used to generate attachments based on both
|
||||
attachment_ids of template, and reports coming from report_template_ids.
|
||||
Those are generated based on the current record to display. As template
|
||||
generation returns a list of tuples, new attachments are created on
|
||||
the fly during the compute.
|
||||
|
||||
In batch or email mode, only attachment_ids from template are used on
|
||||
the composer. Reports will be generated at sending time.
|
||||
|
||||
When template is removed, attachments are reset. """
|
||||
for composer in self:
|
||||
res_ids = composer._evaluate_res_ids() or [0]
|
||||
if (composer.template_id.attachment_ids and
|
||||
(composer.composition_mode == 'mass_mail' or composer.composition_batch)):
|
||||
composer.attachment_ids = composer.template_id.attachment_ids
|
||||
elif composer.template_id and composer.composition_mode == 'comment' and len(res_ids) == 1:
|
||||
rendered_values = composer._generate_template_for_composer(
|
||||
composer.template_id,
|
||||
res_ids,
|
||||
('attachment_ids', 'attachments'),
|
||||
)[res_ids[0]]
|
||||
attachment_ids = rendered_values.get('attachment_ids') or []
|
||||
# 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
|
||||
if rendered_values.get('attachments'):
|
||||
attachment_ids += self.env['ir.attachment'].create([
|
||||
{'name': attach_fname,
|
||||
'datas': attach_datas,
|
||||
'res_model': 'mail.compose.message',
|
||||
'res_id': 0,
|
||||
'type': 'binary', # override default_type from context, possibly meant for another model!
|
||||
} for attach_fname, attach_datas in rendered_values.pop('attachments')
|
||||
]).ids
|
||||
if attachment_ids:
|
||||
composer.attachment_ids = attachment_ids
|
||||
elif not composer.template_id:
|
||||
composer.attachment_ids = False
|
||||
|
||||
@api.depends('composition_mode', 'email_from', 'model',
|
||||
'res_domain', 'res_ids', 'template_id')
|
||||
def _compute_authorship(self):
|
||||
@@ -510,8 +553,6 @@ class MailComposer(models.TransientModel):
|
||||
for field in ()
|
||||
if template[field]
|
||||
)
|
||||
if template.attachment_ids:
|
||||
values['attachment_ids'] = [att.id for att in template.attachment_ids]
|
||||
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
|
||||
@@ -519,40 +560,21 @@ class MailComposer(models.TransientModel):
|
||||
values = self._generate_template_for_composer(
|
||||
self.env['mail.template'].browse(template_id),
|
||||
template_res_ids,
|
||||
('attachment_ids',
|
||||
'report_template_ids',
|
||||
)
|
||||
()
|
||||
)[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 = []
|
||||
Attachment = self.env['ir.attachment']
|
||||
for attach_fname, attach_datas in values.pop('attachments', []):
|
||||
data_attach = {
|
||||
'name': attach_fname,
|
||||
'datas': attach_datas,
|
||||
'res_model': 'mail.compose.message',
|
||||
'res_id': 0,
|
||||
'type': 'binary', # override default_type from context, possibly meant for another model!
|
||||
}
|
||||
attachment_ids.append(Attachment.create(data_attach).id)
|
||||
if values.get('attachment_ids', []) or attachment_ids:
|
||||
values['attachment_ids'] = [Command.set(values.get('attachment_ids', []) + attachment_ids)]
|
||||
else:
|
||||
default_values = self.with_context(
|
||||
default_composition_mode=composition_mode,
|
||||
default_model=model,
|
||||
default_res_ids=res_ids
|
||||
).default_get(['attachment_ids',
|
||||
'composition_mode',
|
||||
).default_get(['composition_mode',
|
||||
'model',
|
||||
'parent_id',
|
||||
'res_ids',
|
||||
])
|
||||
values = dict(
|
||||
(key, default_values[key])
|
||||
for key in ('attachment_ids',
|
||||
) if key in default_values)
|
||||
for key in () if key in default_values)
|
||||
|
||||
# This onchange should return command instead of ids for x2many field.
|
||||
values = self._convert_to_write(values)
|
||||
|
||||
@@ -484,8 +484,6 @@ class TestComposerInternals(TestMailComposer):
|
||||
composer = self.env['mail.compose.message'].with_context(ctx).create({
|
||||
'body': '<p>Test Body</p>',
|
||||
})
|
||||
# currently onchange necessary
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
# values coming from template: attachment_ids + report in comment
|
||||
if composition_mode == 'comment' and not batch:
|
||||
@@ -515,29 +513,22 @@ class TestComposerInternals(TestMailComposer):
|
||||
else:
|
||||
self.assertEqual(composer.attachment_ids, attachs + extra_attach)
|
||||
|
||||
# update with template with void values: values are kept
|
||||
# update with template with void values: values are kept, void
|
||||
# value is not forced in rendering mode as well as when copying
|
||||
# template values
|
||||
composer.write({'template_id': template_void.id})
|
||||
# currently onchange necessary
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.attachment_ids, attachs + extra_attach + generated,
|
||||
'TODO: Values are kept (should be reset ?)')
|
||||
self.assertEqual(composer.attachment_ids, attachs + extra_attach + generated)
|
||||
else:
|
||||
self.assertEqual(composer.attachment_ids, attachs + extra_attach,
|
||||
'TODO: Values are kept (should be reset ?)')
|
||||
self.assertEqual(composer.attachment_ids, attachs + extra_attach)
|
||||
|
||||
# reset template: values are kept
|
||||
# reset template: values are reset
|
||||
composer.write({'template_id': False})
|
||||
# currently onchange necessary
|
||||
composer._onchange_template_id_wrapper()
|
||||
|
||||
if composition_mode == 'comment' and not batch:
|
||||
self.assertEqual(composer.attachment_ids, attachs + extra_attach + generated,
|
||||
'TODO: Values are kept (should be reset ?)')
|
||||
self.assertFalse(composer.attachment_ids)
|
||||
else:
|
||||
self.assertEqual(composer.attachment_ids, attachs + extra_attach,
|
||||
'TODO: Values are kept (should be reset ?)')
|
||||
self.assertFalse(composer.attachment_ids)
|
||||
|
||||
@users('employee')
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
@@ -1098,10 +1089,8 @@ class TestComposerInternals(TestMailComposer):
|
||||
self._get_web_context(self.test_record)
|
||||
).create({
|
||||
'body': '<p>Template Body</p>',
|
||||
'partner_ids': [self.partner_employee_2.id],
|
||||
'template_id': template_1.id,
|
||||
})
|
||||
composer._onchange_template_id_wrapper()
|
||||
composer._action_send_mail()
|
||||
|
||||
self.assertEqual(
|
||||
|
||||
Reference in New Issue
Block a user