[REF] mail, various: cleanup input/output of generation methods on template and composer

Purpose of this commit is to prepare further improvements in template and
composer rendering methods. First step is to

  * make them private;
  * correctly name parameters (notably rename fields to render_fields to avoid
    collision with odoo.fields);
  * remove the "single / multi" mode. This is a relic of old implementation
    when rendering was done mainly record by record and sometimes by batch.
    Now everything should be batched;
  * split some lines to ease future diff in upcoming commits;
  * improve some docstrings;

Next commits will rewrite and split parts of ``_generate_email``.

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 95859f6fb5
commit a19f2e3390
4 changed files with 37 additions and 43 deletions
+19 -19
View File
@@ -191,7 +191,7 @@ class MailTemplate(models.Model):
# MESSAGE/EMAIL VALUES GENERATION
# ------------------------------------------------------------
def generate_recipients(self, results, res_ids):
def _generate_recipients(self, results, res_ids):
"""Generates the recipients of the template. Default values can ben generated
instead of the template values if requested by template or context.
Emails (email_to, email_cc) can be transformed into partners if requested
@@ -228,24 +228,24 @@ class MailTemplate(models.Model):
results[res_id]['partner_ids'] = partner_ids
return results
def generate_email(self, res_ids, render_fields):
"""Generates an email from the template for given the given model based on
records given by res_ids.
def _generate_template(self, res_ids, render_fields):
""" Render values from template 'self' on records given by 'res_ids'.
Those values are generally used to create a mail.mail or a mail.message.
Model of records is the one defined on template.
:param res_id: id of the record to use for rendering the template (model
is taken from template definition)
:returns: a dict containing all relevant fields for creating a new
mail.mail entry, with one extra key ``attachments``, in the
format [(report_name, data)] where data is base64 encoded.
:param list res_ids: list of record IDs on which template is rendered;
:param list render_fields: list of fields to render on template;
:returns: a dict of (res_ids, values) where values contains all rendered
fields asked in ``render_fields``. Asking for attachments adds an
'attachments' key using the format [(report_name, data)] where data
is base64 encoded. Asking for recipients adds a 'partner_ids' key.
Note that 2many fields contain a list of IDs, not commands.
"""
self.ensure_one()
multi_mode = True
if isinstance(res_ids, int):
res_ids = [res_ids]
multi_mode = False
results = dict()
for lang, (template, template_res_ids) in self._classify_per_lang(res_ids).items():
for _lang, (template, template_res_ids) in self._classify_per_lang(res_ids).items():
for field in render_fields:
generated_field_values = template._render_field(
field, template_res_ids
@@ -254,7 +254,7 @@ class MailTemplate(models.Model):
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)
results = template._generate_recipients(results, template_res_ids)
# update values for all res_ids
for res_id in template_res_ids:
values = results[res_id]
@@ -291,7 +291,7 @@ class MailTemplate(models.Model):
raise UserError(_('Unsupported report type %s found.', report.report_type))
result, report_format = res
# TODO in trunk, change return format to binary to match message_post expected format
# TODO in trunk, change return report_format to binary to match message_post expected format
result = base64.b64encode(result)
if not report_name:
report_name = 'report.' + report_service
@@ -310,7 +310,7 @@ class MailTemplate(models.Model):
results[res_id]['attachment_ids'] += additional_attachments.get('attachment_ids', [])
results[res_id]['attachments'] += additional_attachments.get('attachments', [])
return multi_mode and results or results[res_ids[0]]
return results
# ------------------------------------------------------------
# EMAIL
@@ -342,7 +342,7 @@ class MailTemplate(models.Model):
Attachment = self.env['ir.attachment'] # TDE FIXME: should remove default_type from context
# create a mail_mail based on values, without attachments
values = self.generate_email(
values = self._generate_template(
res_id,
('auto_delete',
'body_html',
@@ -354,7 +354,7 @@ class MailTemplate(models.Model):
'scheduled_date',
'subject',
)
)
)[res_id]
values['recipient_ids'] = [Command.link(pid) for pid in values.get('partner_ids', list())]
values['attachment_ids'] = [Command.link(aid) for aid in values.get('attachment_ids', list())]
values.update(email_values or {})
+10 -19
View File
@@ -390,7 +390,7 @@ class MailComposer(models.TransientModel):
# render all template-based value at once
if mass_mail_mode and self.model:
rendered_values = self.render_message(res_ids)
rendered_values = self._render_message(res_ids)
# compute alias-based reply-to in batch
reply_to_value = dict.fromkeys(res_ids, None)
if mass_mail_mode and not self.reply_to_force_new:
@@ -585,7 +585,7 @@ class MailComposer(models.TransientModel):
if template.mail_server_id:
values['mail_server_id'] = template.mail_server_id.id
elif template_id:
values = self.generate_email_for_composer(
values = self._generate_email_for_composer(
template_id, [res_id],
('attachment_ids',
'body_html',
@@ -649,7 +649,7 @@ class MailComposer(models.TransientModel):
return {'value': values}
def render_message(self, res_ids):
def _render_message(self, res_ids):
"""Generate template-based values of wizard, for the document records given
by res_ids. This method is meant to be inherited by email_template that
will produce a more complete dictionary, using qweb templates.
@@ -669,10 +669,6 @@ class MailComposer(models.TransientModel):
subject, body, email_from and reply_to
"""
self.ensure_one()
multi_mode = True
if isinstance(res_ids, int):
multi_mode = False
res_ids = [res_ids]
subjects = self._render_field('subject', res_ids)
# We want to preserve comments in emails so as to keep mso conditionals
@@ -696,7 +692,7 @@ class MailComposer(models.TransientModel):
# generate template-based values
if self.template_id:
template_values = self.generate_email_for_composer(
template_values = self._generate_email_for_composer(
self.template_id.id, res_ids,
('attachment_ids',
'email_to',
@@ -721,24 +717,19 @@ class MailComposer(models.TransientModel):
# update template values by composer values
template_values[res_id].update(results[res_id])
return multi_mode and template_values or template_values[res_ids[0]]
return template_values
@api.model
def generate_email_for_composer(self, template_id, res_ids, fields):
""" Call email_template.generate_email(), get fields relevant for
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 """
multi_mode = True
if isinstance(res_ids, int):
multi_mode = False
res_ids = [res_ids]
returned_fields = list(fields) + ['partner_ids', 'attachments']
returned_fields = list(render_fields) + ['partner_ids', 'attachments']
values = dict.fromkeys(res_ids, False)
template_values = self.env['mail.template'].with_context(tpl_partners_only=True).browse(template_id).generate_email(res_ids, fields)
template_values = self.env['mail.template'].with_context(tpl_partners_only=True).browse(template_id)._generate_template(res_ids, render_fields)
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 multi_mode and values or values[res_ids[0]]
return values
+4 -4
View File
@@ -55,7 +55,7 @@ class MailTemplatePreview(models.TransientModel):
help="The queue manager will send the email after the date")
body_html = fields.Html('Body', compute='_compute_mail_template_fields', sanitize=False)
attachment_ids = fields.Many2many('ir.attachment', 'Attachments', compute='_compute_mail_template_fields')
# Extra fields info generated by generate_email
# Extra fields info generated by _generate_template
partner_ids = fields.Many2many('res.partner', string='Recipients', compute='_compute_mail_template_fields')
@api.depends('model_id')
@@ -76,10 +76,10 @@ class MailTemplatePreview(models.TransientModel):
self._set_mail_attributes()
else:
copy_depends_values['resource_ref'] = '%s,%s' % (self.resource_ref._name, self.resource_ref.id)
mail_values = mail_template.with_context(template_preview_lang=self.lang).generate_email(
self.resource_ref.id,
mail_values = mail_template.with_context(template_preview_lang=self.lang)._generate_template(
[self.resource_ref.id],
self._MAIL_TEMPLATE_FIELDS
)
)[self.resource_ref.id]
self._set_mail_attributes(values=mail_values)
self.error_msg = False
except (ValueError, UserError) as user_error:
@@ -135,7 +135,7 @@ class ChannelUsersRelation(models.Model):
record_email_values = dict()
for template, records in template_to_records.items():
record_email_values.update(
template.generate_email(
template._generate_template(
records.ids,
['body_html',
'email_from',
@@ -148,6 +148,9 @@ class ChannelUsersRelation(models.Model):
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