From 4d571ec8d7efad3a31f986291ea3f5897f692017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 2 Nov 2021 16:02:07 +0000 Subject: [PATCH] [REF] mail: render only asked fields when generating a template values Currently some value are always computed and added even if not asked by the caller. As asking a template to render some fields is now used notably for a subset of fields (subject and body, from / to, ...) there is no need to automatically add other irrelevant information. Main callers already filtered out returned results (notably composer). However better avoid computing / rendering unnecessary stuff directly at template level. Task-2710804 (Mail: Clean MailThread Posting API) Prepares Task-2088884 (Mail: Use editable computed stored fields in composer) Part-of: odoo/odoo#99482 --- addons/mail/models/mail_template.py | 75 +++++++++++++------ addons/mail/wizard/mail_compose_message.py | 6 +- addons/mail/wizard/mail_template_preview.py | 8 +- .../tests/test_website_sale_cart_recovery.py | 2 +- addons/website_slides/models/slide_channel.py | 34 ++++++--- 5 files changed, 85 insertions(+), 40 deletions(-) diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py index 3908bd303b9..8b037a6e1e1 100644 --- a/addons/mail/models/mail_template.py +++ b/addons/mail/models/mail_template.py @@ -243,21 +243,38 @@ class MailTemplate(models.Model): Note that 2many fields contain a list of IDs, not commands. """ self.ensure_one() + render_fields_set = set(render_fields) + fields_specific = { + 'attachments', # attachments + 'attachment_ids', # attachments + # not rendered (static) + 'auto_delete', + 'mail_server_id', + 'model', + 'res_id', + } - results = dict() + render_results = {} for _lang, (template, template_res_ids) in self._classify_per_lang(res_ids).items(): - for field in render_fields: + # render fields not rendered by sub methods + fields_torender = { + field for field in render_fields + if field not in fields_specific + } + for field in fields_torender: generated_field_values = template._render_field( field, template_res_ids ) for res_id, field_value in generated_field_values.items(): - results.setdefault(res_id, dict())[field] = field_value - # compute recipients - if any(field in render_fields for field in ['email_to', 'partner_to', 'email_cc']): - results = template._generate_recipients(results, template_res_ids) - # update values for all res_ids + render_results.setdefault(res_id, {})[field] = field_value + + # render recipients + if render_fields_set & {'email_cc', 'email_to', 'partner_to'}: + render_results = template._generate_recipients(render_results, template_res_ids) + + # add values static for all res_ids for res_id in template_res_ids: - values = results[res_id] + values = render_results[res_id] if values.get('body_html'): values['body'] = tools.html_sanitize(values['body_html']) # if asked in fields to return, parse generated date into tz agnostic UTC as expected by ORM @@ -267,16 +284,19 @@ class MailTemplate(models.Model): values['scheduled_date'] = parsed_datetime.replace(tzinfo=None) if parsed_datetime else False # technical settings - values.update( - mail_server_id=template.mail_server_id.id or False, - auto_delete=template.auto_delete, - model=template.model, - res_id=res_id or False, - attachment_ids=[attach.id for attach in template.attachment_ids], - ) + if 'attachments' in render_fields or 'attachment_ids' in render_fields: + values['attachment_ids'] = template.attachment_ids.ids + if 'auto_delete' in render_fields: + values['auto_delete'] = template.auto_delete + if 'mail_server_id' in render_fields: + values['mail_server_id'] = template.mail_server_id.id + if 'model' in render_fields: + values['model'] = template.model + if 'res_id' in render_fields: + values['res_id'] = res_id or False - # Add report in attachments: generate once for all template_res_ids - if template.report_template: + # render attachments (report part) + if ('attachments' in render_fields or 'attachment_ids' in render_fields) and template.report_template: for res_id in template_res_ids: attachments = [] report_name = template._render_field('report_name', [res_id])[res_id] @@ -299,18 +319,20 @@ class MailTemplate(models.Model): if not report_name.endswith(ext): report_name += ext attachments.append((report_name, result)) - results[res_id]['attachments'] = attachments + render_results[res_id]['attachments'] = attachments # hook for attachments-specific computation, used currently only for accounting - if 'attachments' in render_fields or 'attachment_ids' in render_fields and hasattr(self.env[self.model], '_process_attachments_for_template_post'): + if ('attachments' in render_fields or 'attachment_ids' in render_fields) and hasattr(self.env[self.model], '_process_attachments_for_template_post'): records_attachments = self.env[self.model].browse(template_res_ids)._process_attachments_for_template_post(template) for res_id, additional_attachments in records_attachments.items(): if not additional_attachments: continue - results[res_id]['attachment_ids'] += additional_attachments.get('attachment_ids', []) - results[res_id]['attachments'] += additional_attachments.get('attachments', []) + if additional_attachments.get('attachment_ids'): + render_results[res_id].setdefault('attachment_ids', []).extend(additional_attachments['attachment_ids']) + if additional_attachments.get('attachments'): + render_results[res_id].setdefault('attachments', []).extend(additional_attachments['attachments']) - return results + return render_results # ------------------------------------------------------------ # EMAIL @@ -343,14 +365,19 @@ class MailTemplate(models.Model): # create a mail_mail based on values, without attachments values = self._generate_template( - res_id, - ('auto_delete', + [res_id], + ('attachments', + 'attachment_ids', + 'auto_delete', 'body_html', 'email_cc', 'email_from', 'email_to', + 'mail_server_id', + 'model', 'partner_to', 'reply_to', + 'res_id', 'scheduled_date', 'subject', ) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 7e35426e066..55867bdf9a4 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -587,7 +587,8 @@ class MailComposer(models.TransientModel): elif template_id: values = self._generate_email_for_composer( template_id, [res_id], - ('attachment_ids', + ('attachments', + 'attachment_ids', 'body_html', 'email_cc', 'email_from', @@ -694,7 +695,8 @@ class MailComposer(models.TransientModel): if self.template_id: template_values = self._generate_email_for_composer( self.template_id.id, res_ids, - ('attachment_ids', + ('attachments', + 'attachment_ids', 'email_to', 'email_cc', 'mail_server_id', diff --git a/addons/mail/wizard/mail_template_preview.py b/addons/mail/wizard/mail_template_preview.py index 1324f4c9e07..2de5f26b975 100644 --- a/addons/mail/wizard/mail_template_preview.py +++ b/addons/mail/wizard/mail_template_preview.py @@ -8,12 +8,14 @@ from odoo.exceptions import UserError class MailTemplatePreview(models.TransientModel): _name = 'mail.template.preview' _description = 'Email Template Preview' - _MAIL_TEMPLATE_FIELDS = ['attachment_ids', + _MAIL_TEMPLATE_FIELDS = ['attachments', + 'attachment_ids', 'body_html', 'subject', 'email_cc', 'email_from', 'email_to', + 'partner_to', 'reply_to', 'scheduled_date', ] @@ -92,6 +94,10 @@ class MailTemplatePreview(models.TransientModel): def _set_mail_attributes(self, values=None): for field in self._MAIL_TEMPLATE_FIELDS: + if field in ('attachments', 'partner_to'): + # partner_to is used to generate partner_ids, handled here below + # attachments are a list of attachments, no usage here + continue field_value = values.get(field, False) if values else self.mail_template_id[field] self[field] = field_value self.partner_ids = values.get('partner_ids', False) if values else False diff --git a/addons/website_sale/tests/test_website_sale_cart_recovery.py b/addons/website_sale/tests/test_website_sale_cart_recovery.py index eae65eacd97..6ce0c152f82 100644 --- a/addons/website_sale/tests/test_website_sale_cart_recovery.py +++ b/addons/website_sale/tests/test_website_sale_cart_recovery.py @@ -121,6 +121,6 @@ class TestWebsiteSaleCartRecoveryServer(TransactionCase): "Each cart recovery mail has been sent exactly once." ) self.assertTrue( - all(order.access_token in sent_mail[order].body for order in orders), + all(order.access_token in sent_mail[order].body_html for order in orders), "Each mail should contain the access token of the corresponding SO." ) diff --git a/addons/website_slides/models/slide_channel.py b/addons/website_slides/models/slide_channel.py index 35f51524ba0..26c03c646a0 100644 --- a/addons/website_slides/models/slide_channel.py +++ b/addons/website_slides/models/slide_channel.py @@ -132,24 +132,34 @@ class ChannelUsersRelation(models.Model): template_to_records.setdefault(template, self.env['slide.channel.partner']) template_to_records[template] += record - record_email_values = dict() + record_email_values = {} for template, records in template_to_records.items(): - record_email_values.update( - template._generate_template( - records.ids, - ['body_html', - 'email_from', - 'partner_to', - 'subject', - ] - ) + record_values = template._generate_template( + records.ids, + ['attachments', + 'attachment_ids', + 'body_html', + 'email_cc', + 'email_from', + 'email_to', + 'mail_server_id', + 'model', + 'partner_to', + 'reply_to', + 'res_id', + 'scheduled_date', + 'subject', + ] ) + for res_id, values in record_values.items(): + # attachments specific not supported currently, only attachment_ids + values.pop('attachments', False) + values['body'] = values.get('body_html') # keep body copy in chatter + record_email_values[res_id] = values mail_mail_values = [] for record in self: email_values = record_email_values.get(record.id) - # attachments specific not supported currently, only attachment_ids - email_values.pop('attachments', False) if not email_values or not email_values.get('partner_ids'): continue