From 09a1647c645a6a8bd49a490d7484fea6aea3ff32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 23 Sep 2022 12:36:01 +0000 Subject: [PATCH] [REF] mail: improve composer call to template values generation Now that template generation methods have been cleaned we can cleanup calls done in composer. We notably * make methods private, improve their naming and docstrings; * better support input / output of asked fields; * be more coherent with composer fields and template fields usage; Task-2710804 (Mail: Clean MailThread Posting API) Prepares Task-2088884 (Mail: Composer Onchange to Editable Computed Stored) Part-of: odoo/odoo#99482 --- addons/mail/wizard/mail_compose_message.py | 84 +++++++++++++------- addons/test_mail/tests/test_mail_composer.py | 5 +- 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 76a23ba305b..5a472894342 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -203,12 +203,11 @@ class MailComposer(models.TransientModel): -> for x2many field, this onchange return command instead of ids """ if template_id and composition_mode == 'mass_mail': + # copy raw template values (not rendered due to mass mode) template = self.env['mail.template'].browse(template_id) values = dict( (field, template[field]) - for field in ('body_html', - 'email_from', - 'mail_server_id', + for field in ('email_from', 'reply_to', 'subject', ) @@ -218,16 +217,20 @@ class MailComposer(models.TransientModel): values['attachment_ids'] = [att.id for att in template.attachment_ids] if template.mail_server_id: 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: - values = self._generate_email_for_composer( - template_id, [res_id], + # render template (mono record, comment mode) and set it as composer values + values = self._generate_template_for_composer( + self.env['mail.template'].browse(template_id), + [res_id], ('attachment_ids', 'body_html', 'email_cc', 'email_from', 'email_to', 'mail_server_id', - 'partner_to', + 'partner_ids', 'reply_to', 'report_template', 'subject', @@ -276,9 +279,6 @@ class MailComposer(models.TransientModel): 'subject', ) if key in default_values) - if values.get('body_html'): - values['body'] = values.pop('body_html') - # This onchange should return command instead of ids for x2many field. values = self._convert_to_write(values) @@ -642,13 +642,14 @@ class MailComposer(models.TransientModel): # generate template-based values if self.template_id: - template_values = self._generate_email_for_composer( - self.template_id.id, res_ids, + template_values = self._generate_template_for_composer( + self.template_id, + res_ids, ('attachment_ids', 'email_to', 'email_cc', 'mail_server_id', - 'partner_to', + 'partner_ids', 'report_template', ) ) @@ -670,26 +671,55 @@ class MailComposer(models.TransientModel): return template_values - @api.model - def _generate_email_for_composer(self, template_id, res_ids, render_fields): - """ Call email_template._generate_template(), get fields relevant for - mail.compose.message, transform email_cc and email_to into partner_ids """ - returned_fields = list(render_fields) + ['partner_ids'] - if 'report_template' in render_fields: - returned_fields.append('attachments') - values = dict.fromkeys(res_ids, False) + def _generate_template_for_composer(self, template, res_ids, render_fields, + find_or_create_partners=True): + """ Generate values based on template and relevant values for the + mail.compose.message wizard. - template_values = self.env['mail.template'].browse(template_id)._generate_template( + :param record template: a mail template, as during onchange mode it + may not be set on self (to remove when removing the onchange); + :param list res_ids: list of record IDs on which template is rendered; + :param list render_fields: list of fields to render on template; + :param boolean find_or_create_partners: transform emails into partners + (see ``Template._generate_template_recipients``); + + :returns: a dict containing all asked fields for each record ID given by + res_ids. Note that + + * 'body' comes from template 'body_html' generation; + * 'attachments' is an additional key coming with 'attachment_ids' due + to report generation (in the format [(report_name, data)] where data + is base64 encoded); + * 'partner_ids' is returned due to recipients generation that gives + partner ids coming from default computation as well as from email + to partner convert (see ``find_or_create_partners``); + """ + self.ensure_one() + + # some fields behave / are named differently on template model + mapping = { + 'attachments': 'report_template', + 'body': 'body_html', + 'partner_ids': 'partner_to', + } + template_fields = {mapping.get(fname, fname) for fname in render_fields} + template_values = template._generate_template( res_ids, - render_fields, + template_fields, find_or_create_partners=True ) - for res_id in res_ids: - res_id_values = dict((field, template_values[res_id][field]) for field in returned_fields if template_values[res_id].get(field)) - res_id_values['body'] = res_id_values.pop('body_html', '') - values[res_id] = res_id_values - return values + exclusion_list = ('email_cc', 'email_to') if find_or_create_partners else () + mapping = {'body_html': 'body'} + render_results = {} + for res_id in res_ids: + render_results[res_id] = { + mapping.get(fname, fname): value + for fname, value in template_values[res_id].items() + if fname not in exclusion_list and value + } + + return render_results # ---------------------------------------------------------------------- # EMAIL MANAGEMENT diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index 93ff0335c3d..eadbda57a5c 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -593,14 +593,13 @@ class TestComposerInternals(TestMailComposer): self.assertEqual(composer.subject, 'Back to my amazing subject for {{ record.name }}') # update with template with void values: void value is not forced in - # rendering mode as well as when copying template values, except for - # body in rendering mode + # 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': - self.assertFalse(composer.body, 'Void template body resets while other fields not, maybe to fix') + self.assertEqual(composer.body, 'Back to my amazing body') self.assertEqual(composer.mail_server_id, self.mail_server_global) self.assertEqual(composer.record_name, 'Manual update') self.assertEqual(composer.subject, 'Back to my amazing subject for {{ record.name }}')