From 28b4ba4049b4d3d39d34d7c5ec00c6a1e0697790 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?=
Date: Thu, 20 Oct 2022 12:13:11 +0000
Subject: [PATCH] [IMP] mail, various: allow to link multiple reports to
templates
RATIONALE
Improve usage of composer in comment or email mode: support batch-posting in
comment, support more configuration from templates, improve global model.
SPECIFICATIONS
Update report_template field on template model to be a many2many field instead
of a many2one. It allows to attach multiple dynamic reports to a given template
instead of being limited to a single one.
Name should now come from the report itself, which should be considered as
complete by itself. Template cannot override report naming anymore.
Task-2868153 (Mail: Allow multi reports in mail templates)
Part-of: odoo/odoo#99482
---
addons/account/data/mail_template_data.xml | 9 +-
addons/event/data/mail_template_data.xml | 6 +-
addons/loyalty/data/mail_template_data.xml | 6 +-
addons/mail/models/mail_template.py | 74 ++++++++-----
addons/mail/views/mail_template_views.xml | 8 +-
addons/mail/wizard/mail_compose_message.py | 6 +-
addons/mail/wizard/mail_template_preview.py | 6 +-
addons/purchase/data/mail_template_data.xml | 9 +-
addons/repair/data/mail_template_data.xml | 3 +-
addons/sale/data/mail_template_data.xml | 6 +-
addons/stock/data/mail_template_data.xml | 3 +-
addons/survey/data/mail_template_data.xml | 3 +-
.../survey/tests/test_certification_flow.py | 3 +-
addons/test_event_full/tests/common.py | 2 +-
addons/test_mail/data/mail_template_data.xml | 11 ++
addons/test_mail/tests/test_mail_composer.py | 104 +++++++++++-------
addons/website_slides/models/slide_channel.py | 2 +-
17 files changed, 149 insertions(+), 112 deletions(-)
diff --git a/addons/account/data/mail_template_data.xml b/addons/account/data/mail_template_data.xml
index 81eb47ad08e..13f5c49d452 100644
--- a/addons/account/data/mail_template_data.xml
+++ b/addons/account/data/mail_template_data.xml
@@ -54,8 +54,7 @@
-
- Invoice_{{ (object.name or '').replace('/','_') }}{{ object.state == 'draft' and '_draft' or '' }}
+ {{ object.partner_id.lang }}
@@ -84,8 +83,7 @@
-
- {{ (object.name or '').replace('/','-') }}
+ {{ object.partner_id.lang }}
@@ -129,8 +127,7 @@
-
- Credit_note_{{ (object.name or '').replace('/','_') }}{{ object.state == 'draft' and '_draft' or '' }}
+ {{ object.partner_id.lang }}
diff --git a/addons/event/data/mail_template_data.xml b/addons/event/data/mail_template_data.xml
index 1d5de71bdec..03d541a588d 100644
--- a/addons/event/data/mail_template_data.xml
+++ b/addons/event/data/mail_template_data.xml
@@ -22,8 +22,7 @@
-- Mitchell Admin
-
- Foldable Badge - {{ (object.event_id.name or 'Event').replace('/','_') }}
+ {{ object.partner_id.lang }}
@@ -254,8 +253,7 @@
-
- Full Page Ticket - {{ (object.event_id.name or 'Event').replace('/','') }}
+ {{ object.partner_id.lang }}
diff --git a/addons/loyalty/data/mail_template_data.xml b/addons/loyalty/data/mail_template_data.xml
index 4c4a8597811..bba70470e01 100644
--- a/addons/loyalty/data/mail_template_data.xml
+++ b/addons/loyalty/data/mail_template_data.xml
@@ -34,8 +34,7 @@
-
- Your Gift Card
+
@@ -96,8 +95,7 @@
-
- Your Coupon Code
+
diff --git a/addons/mail/models/mail_template.py b/addons/mail/models/mail_template.py
index 25c734bbac0..bccd11d813c 100644
--- a/addons/mail/models/mail_template.py
+++ b/addons/mail/models/mail_template.py
@@ -7,6 +7,7 @@ import logging
from odoo import _, api, fields, models, tools, Command
from odoo.exceptions import UserError
from odoo.tools import is_html_empty
+from odoo.tools.safe_eval import safe_eval, time
_logger = logging.getLogger(__name__)
@@ -63,10 +64,12 @@ class MailTemplate(models.Model):
'attachment_id', 'Attachments',
help="You may attach files to this template, to be added to all "
"emails created from this template")
- report_name = fields.Char('Report Filename', translate=True, prefetch=True,
- help="Name to use for the generated report file (may contain placeholders)\n"
- "The extension can be omitted and will then come from the report type.")
- report_template = fields.Many2one('ir.actions.report', 'Optional report to print and attach')
+ report_template_ids = fields.Many2many(
+ 'ir.actions.report', relation='mail_template_ir_actions_report_rel',
+ column1='mail_template_id',
+ column2='ir_actions_report_id',
+ string='Reports to print and attach',
+ domain="[('model', '=', model)]")
# options
mail_server_id = fields.Many2one('ir.mail_server', 'Outgoing Mail Server', readonly=False,
help="Optional preferred server for outgoing mails. If not set, the highest "
@@ -198,14 +201,14 @@ class MailTemplate(models.Model):
def _generate_template_attachments(self, res_ids, render_fields,
render_results=None):
""" Render attachments of template 'self', returning values for records
- given by 'res_ids'. Note that ``report_template`` returns values for
+ given by 'res_ids'. Note that ``report_template_ids`` returns values for
'attachments', as we have a list of tuple (report_name, base64 value)
for those reports. It is considered as being the job of callers to
transform those attachments into valid ``ir.attachment`` records.
:param list res_ids: list of record IDs on which template is rendered;
:param list render_fields: list of fields to render on template which
- are specific to attachments, e.g. attachment_ids or report_template;
+ are specific to attachments, e.g. attachment_ids or report_template_ids;
:param dict render_results: res_ids-based dictionary of render values.
For each res_id, a dict of values based on render_fields is given
@@ -215,6 +218,11 @@ class MailTemplate(models.Model):
if render_results is None:
render_results = {}
+ # generating reports is done on a per-record basis, better ensure cache
+ # is filled up to avoid rendering and browsing in a loop
+ if res_ids and 'report_template_ids' in render_fields and self.report_template_ids:
+ self.env[self.model].browse(res_ids)
+
for res_id in res_ids:
values = render_results.setdefault(res_id, {})
@@ -223,27 +231,33 @@ class MailTemplate(models.Model):
values['attachment_ids'] = self.attachment_ids.ids
# generate attachments (reports)
- if 'report_template' in render_fields and self.report_template:
- report = self.report_template
-
- if report.report_type in ['qweb-html', 'qweb-pdf']:
- report_content, report_format = self.env['ir.actions.report']._render_qweb_pdf(report, [res_id])
- else:
- render_res = self.env['ir.actions.report']._render(report, [res_id])
- if not render_res:
- raise UserError(_('Unsupported report type %s found.', report.report_type))
- report_content, report_format = render_res
- report_content = base64.b64encode(report_content)
-
- report_name = self._render_field('report_name', [res_id])[res_id]
- if not report_name:
- report_name = 'report.' + report.report_name
- ext = "." + report_format
- if not report_name.endswith(ext):
- report_name += ext
-
- values['attachments'] = [(report_name, report_content)]
- elif 'report_template' in render_fields:
+ if 'report_template_ids' in render_fields and self.report_template_ids:
+ for report in self.report_template_ids:
+ # generate content
+ if report.report_type in ['qweb-html', 'qweb-pdf']:
+ report_content, report_format = self.env['ir.actions.report']._render_qweb_pdf(report, [res_id])
+ else:
+ render_res = self.env['ir.actions.report']._render(report, [res_id])
+ if not render_res:
+ raise UserError(_('Unsupported report type %s found.', report.report_type))
+ report_content, report_format = render_res
+ report_content = base64.b64encode(report_content)
+ # generate name
+ if report.print_report_name:
+ report_name = safe_eval(
+ report.print_report_name,
+ {
+ 'object': self.env[self.model].browse(res_id),
+ 'time': time,
+ }
+ )
+ else:
+ report_name = _('Report')
+ extension = "." + report_format
+ if not report_name.endswith(extension):
+ report_name += extension
+ values.setdefault('attachments', []).append((report_name, report_content))
+ elif 'report_template_ids' in render_fields:
values['attachments'] = []
# hook for attachments-specific computation, used currently only for accounting
@@ -436,7 +450,7 @@ class MailTemplate(models.Model):
'email_cc', # recipients
'email_to', # recipients
'partner_to', # recipients
- 'report_template', # attachments
+ 'report_template_ids', # attachments
'scheduled_date', # specific
# not rendered (static)
'auto_delete',
@@ -482,7 +496,7 @@ class MailTemplate(models.Model):
)
# generate attachments if requested
- if render_fields_set & {'attachment_ids', 'report_template'}:
+ if render_fields_set & {'attachment_ids', 'report_template_ids'}:
template._generate_template_attachments(
template_res_ids,
render_fields_set,
@@ -533,7 +547,7 @@ class MailTemplate(models.Model):
'model',
'partner_to',
'reply_to',
- 'report_template',
+ 'report_template_ids',
'res_id',
'scheduled_date',
'subject',
diff --git a/addons/mail/views/mail_template_views.xml b/addons/mail/views/mail_template_views.xml
index 021e0dde091..0be8580fb0d 100644
--- a/addons/mail/views/mail_template_views.xml
+++ b/addons/mail/views/mail_template_views.xml
@@ -79,8 +79,9 @@
-
-
+
@@ -102,7 +103,6 @@
-
@@ -112,7 +112,7 @@
mail.template
-
+
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index b92fdb65867..3914e6be9d2 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -284,7 +284,7 @@ class MailComposer(models.TransientModel):
'mail_server_id',
'partner_ids',
'reply_to',
- 'report_template',
+ 'report_template_ids',
'scheduled_date',
'subject',
)
@@ -709,7 +709,7 @@ class MailComposer(models.TransientModel):
'email_cc',
'mail_server_id',
'partner_ids',
- 'report_template',
+ 'report_template_ids',
'scheduled_date',
)
)
@@ -906,7 +906,7 @@ class MailComposer(models.TransientModel):
# some fields behave / are named differently on template model
mapping = {
- 'attachments': 'report_template',
+ 'attachments': 'report_template_ids',
'body': 'body_html',
'partner_ids': 'partner_to',
}
diff --git a/addons/mail/wizard/mail_template_preview.py b/addons/mail/wizard/mail_template_preview.py
index 14367e1e250..6eb21551de0 100644
--- a/addons/mail/wizard/mail_template_preview.py
+++ b/addons/mail/wizard/mail_template_preview.py
@@ -15,7 +15,7 @@ class MailTemplatePreview(models.TransientModel):
'email_from',
'email_to',
'partner_to',
- 'report_template',
+ 'report_template_ids',
'reply_to',
'scheduled_date',
]
@@ -94,9 +94,9 @@ class MailTemplatePreview(models.TransientModel):
def _set_mail_attributes(self, values=None):
for field in self._MAIL_TEMPLATE_FIELDS:
- if field in ('partner_to', 'report_template'):
+ if field in ('partner_to', 'report_template_ids'):
# partner_to is used to generate partner_ids, handled here below
- # report_template generates attachments, no usage here
+ # report_template_ids generates attachments, no usage here
continue
field_value = values.get(field, False) if values else self.mail_template_id[field]
self[field] = field_value
diff --git a/addons/purchase/data/mail_template_data.xml b/addons/purchase/data/mail_template_data.xml
index 0b348044df9..3dffc957249 100644
--- a/addons/purchase/data/mail_template_data.xml
+++ b/addons/purchase/data/mail_template_data.xml
@@ -30,8 +30,7 @@
-
- RFQ_{{ (object.name or '').replace('/','_') }}
+ {{ object.partner_id.lang }}
@@ -69,8 +68,7 @@