[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
This commit is contained in:
Thibault Delavallée
2023-01-17 20:58:36 +01:00
parent a19f2e3390
commit 4d571ec8d7
5 changed files with 85 additions and 40 deletions
+51 -24
View File
@@ -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',
)
+4 -2
View File
@@ -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',
+7 -1
View File
@@ -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
@@ -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."
)
+22 -12
View File
@@ -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