diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index fe4a3fdb836..d336f1fe790 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -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) diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index c0c37ed58c8..1c0581c95b2 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -484,8 +484,6 @@ class TestComposerInternals(TestMailComposer): composer = self.env['mail.compose.message'].with_context(ctx).create({ 'body': '
Test Body
', }) - # 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': 'Template Body
', - 'partner_ids': [self.partner_employee_2.id], 'template_id': template_1.id, }) - composer._onchange_template_id_wrapper() composer._action_send_mail() self.assertEqual(