summaryrefslogtreecommitdiff
path: root/addons/digest/models
diff options
context:
space:
mode:
authorThibault Delavallée <tde@odoo.com>2020-05-28 14:46:49 +0000
committerThibault Delavallée <tde@odoo.com>2020-06-04 15:20:08 +0000
commit99c2c2ee1e7fc5f6c3298a447ae3845257b44e1c (patch)
treee55fc6098a79ead91628fd53b905fe36f26b552a /addons/digest/models
parent14abd4fdf714ce280ab07998073328eb4d76bc09 (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.py187
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&amp;model=%s&amp;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):