diff options
| author | Thibault Delavallée <tde@odoo.com> | 2020-05-28 14:46:49 +0000 |
|---|---|---|
| committer | Thibault Delavallée <tde@odoo.com> | 2020-06-04 15:20:08 +0000 |
| commit | 99c2c2ee1e7fc5f6c3298a447ae3845257b44e1c (patch) | |
| tree | e55fc6098a79ead91628fd53b905fe36f26b552a /addons/digest/models | |
| parent | 14abd4fdf714ce280ab07998073328eb4d76bc09 (diff) | |
[REF] digest: move template to QWeb
PURPOSE
Make digest email and tips more appealing. The goals of these tips are
* to encourage the adoption of other apps (Did you know ?);
* to make Odoo look more fun (Fun tips and tricks, young and dynamic style);
* to show social proof and increase trust (emphasis on already existing
projects / customers to);
SPECIFICATIONS
Digest now use qweb views instead of a standard mail.template record for
rendering as
* template is very custom;
* probability of breaking it while edition the mail template is high;
it is easier to maintain and extend in qweb;
* only body was really used, other mail-related fields were not used;
We therefore remove the mail template data and the field used for it on
the digest model. Template is now forced to a Qweb view.
We can also remove the template_id field. Indeed we think that having
different templates for digests is a really advanced use case we do not
want to support.
Finally kpis and action computation is rewritten. It now returns an unique
structure containing all information in a more neutral way, holding values
for 3 columns. Purpose is to be less date-oriented in data construct and
allow people to use digest qweb template to display a 3-columns KPIs content
even if not related to yesterday / last 7 days / last 30 days.
LINKS
Task ID 2197417
Community PR odoo/odoo#51619
Upgrade PR odoo/upgrade#1256
Diffstat (limited to 'addons/digest/models')
| -rw-r--r-- | addons/digest/models/digest.py | 187 |
1 files changed, 128 insertions, 59 deletions
diff --git a/addons/digest/models/digest.py b/addons/digest/models/digest.py index 3c39f49e244..c6e51a4fb9a 100644 --- a/addons/digest/models/digest.py +++ b/addons/digest/models/digest.py @@ -7,7 +7,7 @@ import pytz from datetime import datetime, date from dateutil.relativedelta import relativedelta -from odoo import api, fields, models, tools +from odoo import api, fields, models, tools, _ from odoo.addons.base.models.ir_mail_server import MailDeliveryException from odoo.exceptions import AccessError from odoo.tools.float_utils import float_round @@ -28,10 +28,6 @@ class Digest(models.Model): ('quarterly', 'Quarterly')], string='Periodicity', default='daily', required=True) next_run_date = fields.Date(string='Next Send Date') - template_id = fields.Many2one('mail.template', string='Email Template', - domain="[('model','=','digest.digest')]", - default=lambda self: self.env.ref('digest.digest_mail_template'), - required=True) currency_id = fields.Many2one(related="company_id.currency_id", string='Currency', readonly=False) company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company.id) available_fields = fields.Char(compute='_compute_available_fields') @@ -107,22 +103,51 @@ class Digest(models.Model): to_slowdown = self._check_daily_logs() for digest in self: for user in digest.user_ids: - digest.with_context(digest_slowdown=digest in to_slowdown)._action_send_to_user(user, tips_count=1) + digest.with_context( + digest_slowdown=digest in to_slowdown, + lang=user.lang + )._action_send_to_user(user, tips_count=1) if digest in to_slowdown: digest.write({'periodicity': 'weekly'}) digest.next_run_date = digest._get_next_run_date() def _action_send_to_user(self, user, tips_count=1): - subject = '%s: %s' % (user.company_id.name, self.name) - self.template_id.with_context(user=user, tips_count=tips_count).send_mail( - self.id, - force_send=True, - notif_layout="digest.digest_mail_layout", - raise_exception=True, - email_values={ - 'email_to': user.email, 'subject': subject - } + rendered_body = self.env['mail.render.mixin']._render_template( + 'digest.digest_mail_main', + 'digest.digest', + self.ids, + engine='qweb', + add_context={ + 'company': user.company_id, + 'user': user, + 'tips_count': tips_count, + 'datetime': datetime, # TDE TEMP FIXME + 'display_mobile_banner': True, + 'kpi_data': self.compute_kpis(user.company_id, user), + 'tips': self.compute_tips(user.company_id, user, tips_count=tips_count), + 'preferences': self.compute_preferences(user.company_id, user), + }, + post_process=True + )[self.id] + full_mail = self.env['mail.render.mixin']._render_encapsulate( + 'digest.digest_mail_layout', + rendered_body, + add_context={ + 'company': user.company_id, + 'user': user, + }, ) + # create a mail_mail based on values, without attachments + mail_values = { + 'subject': '%s: %s' % (user.company_id.name, self.name), + 'email_from': self.company_id.partner_id.email_formatted if self.company_id else self.env.user.email_formatted, + 'email_to': user.email_formatted, + 'body_html': full_mail, + 'auto_delete': True, + } + mail = self.env['mail.mail'].sudo().create(mail_values) + mail.send(raise_exception=False) + return True @api.model def _cron_send_digest_email(self): @@ -138,33 +163,64 @@ class Digest(models.Model): # ------------------------------------------------------------ def compute_kpis(self, company, user): + """ Compute KPIs to display in the digest template. It is expected to be + a list of KPIs, each containing values for 3 columns display. + + :return list: result [{ + 'kpi_name': 'kpi_mail_message', + 'kpi_fullname': 'Messages', # translated + 'kpi_action': 'crm.crm_lead_action_pipeline', # xml id of an action to execute + 'kpi_col1': { + 'value': '12.0', + 'margin': 32.36, + 'col_subtitle': 'Yesterday', # translated + }, + 'kpi_col2': { ... }, + 'kpi_col3': { ... }, + }, { ... }] """ self.ensure_one() - res = {} - for tf_name, tf in self._compute_timeframes(company).items(): + digest_fields = self._get_kpi_fields() + invalid_fields = [] + kpis = [ + dict(kpi_name=field_name, + kpi_fullname=self._fields[field_name].string, + kpi_action=False, + kpi_col1=dict(), + kpi_col2=dict(), + kpi_col3=dict(), + ) + for field_name in digest_fields + ] + kpis_actions = self._compute_kpis_actions(company, user) + + for col_index, (tf_name, tf) in enumerate(self._compute_timeframes(company)): digest = self.with_context(start_date=tf[0][0], end_date=tf[0][1]).with_user(user).with_company(company) previous_digest = self.with_context(start_date=tf[1][0], end_date=tf[1][1]).with_user(user).with_company(company) - kpis = {} - for field_name, field in self._fields.items(): - if field.type == 'boolean' and field_name.startswith(('kpi_', 'x_kpi_', 'x_studio_kpi_')) and self[field_name]: - - try: - compute_value = digest[field_name + '_value'] - # Context start and end date is different each time so invalidate to recompute. - digest.invalidate_cache([field_name + '_value']) - previous_value = previous_digest[field_name + '_value'] - # Context start and end date is different each time so invalidate to recompute. - previous_digest.invalidate_cache([field_name + '_value']) - except AccessError: # no access rights -> just skip that digest details from that user's digest email - continue - margin = self._get_margin_value(compute_value, previous_value) - if self._fields[field_name+'_value'].type == 'monetary': - converted_amount = self._format_human_readable_amount(compute_value) - kpis.update({field_name: {field_name: self._format_currency_amount(converted_amount, company.currency_id), 'margin': margin}}) - else: - kpis.update({field_name: {field_name: compute_value, 'margin': margin}}) - - res.update({tf_name: kpis}) - return res + for index, field_name in enumerate(digest_fields): + kpi_values = kpis[index] + kpi_values['kpi_action'] = kpis_actions.get(field_name) + try: + compute_value = digest[field_name + '_value'] + # Context start and end date is different each time so invalidate to recompute. + digest.invalidate_cache([field_name + '_value']) + previous_value = previous_digest[field_name + '_value'] + # Context start and end date is different each time so invalidate to recompute. + previous_digest.invalidate_cache([field_name + '_value']) + except AccessError: # no access rights -> just skip that digest details from that user's digest email + invalid_fields.append(field_name) + continue + margin = self._get_margin_value(compute_value, previous_value) + if self._fields['%s_value' % field_name].type == 'monetary': + converted_amount = self._format_human_readable_amount(compute_value) + compute_value = self._format_currency_amount(converted_amount, company.currency_id) + kpi_values['kpi_col%s' % (col_index + 1)].update({ + 'value': compute_value, + 'margin': margin, + 'col_subtitle': tf_name, + }) + + # filter failed KPIs + return [kpi for kpi in kpis if kpi['kpi_name'] not in invalid_fields] def compute_tips(self, company, user, tips_count=1): tips = self.env['digest.tip'].search([ @@ -175,10 +231,10 @@ class Digest(models.Model): self.env['mail.render.mixin']._render_template(tools.html_sanitize(tip.tip_description), 'digest.tip', tip.ids, post_process=True)[tip.id] for tip in tips ] - tip.user_ids += user + tips.user_ids += user return tip_descriptions - def compute_kpis_actions(self, company, user): + def _compute_kpis_actions(self, company, user): """ Give an optional action to display in digest email linked to some KPIs. :return dict: key: kpi name (field name), value: an action that will be @@ -191,16 +247,23 @@ class Digest(models.Model): :return string: html to put in template """ - preferences = self.env['mail.render.mixin']._render_template( - 'digest.digest_section_preferences', - 'digest.digest', - self.ids, - engine='qweb', - add_context={ - 'company': company, - 'user': user, - }, - post_process=True)[self.id] + preferences = [] + if self._context.get('digest_slowdown'): + preferences.append(_("We have noticed you did not connect these last few days so we've automatically switched your preference to weekly Digests.")) + elif self.periodicity == 'daily' and user.has_group('base.group_erp_manager'): + preferences.append('%s <a href="/digest/%s/set_periodicity?periodicity=weekly" target="_blank" style="color:#875A7B; font-weight: bold;">%s</a>' % ( + _('Prefer a broader overview ?'), + self.id, + _('Switch to weekly Digests') + )) + if user.has_group('base.group_erp_manager'): + preferences.append('%s <a href="/web#view_type=form&model=%s&id=%s" target="_blank" style="color:#875A7B; font-weight: bold;">%s</a>' % ( + _('Want to customize this email?'), + self._name, + self.id, + _('Choose the metrics you care about') + )) + return preferences def _get_next_run_date(self): @@ -221,22 +284,28 @@ class Digest(models.Model): if tz_name: now = pytz.timezone(tz_name).localize(now) start_date = now.date() - return { - 'yesterday': ( + return [ + (_('Yesterday'), ( (start_date + relativedelta(days=-1), start_date), - (start_date + relativedelta(days=-2), start_date + relativedelta(days=-1))), - 'lastweek': ( + (start_date + relativedelta(days=-2), start_date + relativedelta(days=-1))) + ), (_('Last 7 Days'), ( (start_date + relativedelta(weeks=-1), start_date), - (start_date + relativedelta(weeks=-2), start_date + relativedelta(weeks=-1))), - 'lastmonth': ( + (start_date + relativedelta(weeks=-2), start_date + relativedelta(weeks=-1))) + ), (_('Last 30 Days'), ( (start_date + relativedelta(months=-1), start_date), - (start_date + relativedelta(months=-2), start_date + relativedelta(months=-1))), - } + (start_date + relativedelta(months=-2), start_date + relativedelta(months=-1))) + ) + ] # ------------------------------------------------------------ # FORMATTING / TOOLS # ------------------------------------------------------------ + def _get_kpi_fields(self): + return [field_name for field_name, field in self._fields.items() + if field.type == 'boolean' and field_name.startswith(('kpi_', 'x_kpi_', 'x_studio_kpi_')) and self[field_name] + ] + def _get_margin_value(self, value, previous_value=0.0): margin = 0.0 if (value != previous_value) and (value != 0.0 and previous_value != 0.0): |
