From 99c2c2ee1e7fc5f6c3298a447ae3845257b44e1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= <tde@odoo.com>
Date: Thu, 28 May 2020 14:46:49 +0000
Subject: [PATCH] [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
---
 addons/account/models/digest.py             |   4 +-
 addons/crm/models/digest.py                 |   4 +-
 addons/digest/__manifest__.py               |   1 -
 addons/digest/data/digest_data.xml          | 199 +++++++++++++++++--
 addons/digest/data/digest_template_data.xml | 209 --------------------
 addons/digest/data/digest_tips_data.xml     |   2 +-
 addons/digest/models/digest.py              | 187 ++++++++++++------
 addons/digest/views/digest_views.xml        |   2 -
 addons/hr_recruitment/models/digest.py      |   4 +-
 addons/im_livechat/models/digest.py         |   4 +-
 addons/point_of_sale/models/digest.py       |   4 +-
 addons/project/models/digest.py             |   4 +-
 addons/sale_management/models/digest.py     |   4 +-
 addons/website_sale/models/digest.py        |   4 +-
 14 files changed, 333 insertions(+), 299 deletions(-)
 delete mode 100644 addons/digest/data/digest_template_data.xml

diff --git a/addons/account/models/digest.py b/addons/account/models/digest.py
index da7d673053b3..d3a798ebb251 100644
--- a/addons/account/models/digest.py
+++ b/addons/account/models/digest.py
@@ -27,7 +27,7 @@ class Digest(models.Model):
             query_res = self._cr.fetchone()
             record.kpi_account_total_revenue_value = query_res and query_res[0] or 0.0
 
-    def compute_kpis_actions(self, company, user):
-        res = super(Digest, self).compute_kpis_actions(company, user)
+    def _compute_kpis_actions(self, company, user):
+        res = super(Digest, self)._compute_kpis_actions(company, user)
         res['kpi_account_total_revenue'] = 'account.action_move_out_invoice_type&menu_id=%s' % self.env.ref('account.menu_finance').id
         return res
diff --git a/addons/crm/models/digest.py b/addons/crm/models/digest.py
index 5ec2bd07add1..acb529f1a32d 100644
--- a/addons/crm/models/digest.py
+++ b/addons/crm/models/digest.py
@@ -37,8 +37,8 @@ class Digest(models.Model):
                 ('company_id', '=', company.id)
             ])
 
-    def compute_kpis_actions(self, company, user):
-        res = super(Digest, self).compute_kpis_actions(company, user)
+    def _compute_kpis_actions(self, company, user):
+        res = super(Digest, self)._compute_kpis_actions(company, user)
         res['kpi_crm_lead_created'] = 'crm.crm_lead_action_pipeline&menu_id=%s' % self.env.ref('crm.crm_menu_root').id
         res['kpi_crm_opportunities_won'] = 'crm.crm_lead_action_pipeline&menu_id=%s' % self.env.ref('crm.crm_menu_root').id
         if user.has_group('crm.group_use_lead'):
diff --git a/addons/digest/__manifest__.py b/addons/digest/__manifest__.py
index b27fbd29ed74..6f4cc562e161 100644
--- a/addons/digest/__manifest__.py
+++ b/addons/digest/__manifest__.py
@@ -16,7 +16,6 @@ Send KPI Digests periodically
     'data': [
         'security/ir.model.access.csv',
         'data/digest_data.xml',
-        'data/digest_template_data.xml',
         'data/digest_tips_data.xml',
         'data/ir_cron_data.xml',
         'data/res_config_settings_data.xml',
diff --git a/addons/digest/data/digest_data.xml b/addons/digest/data/digest_data.xml
index 80678308c795..d4f292884184 100644
--- a/addons/digest/data/digest_data.xml
+++ b/addons/digest/data/digest_data.xml
@@ -49,20 +49,197 @@
          </style>
     </head>
     <body>
-        <t t-raw="message.body"/>
+        <t t-raw="body"/>
     </body>
 </html>
     </template>
 
-    <!-- DIGEST PART: PREFERENCES -->
-    <template id="digest_section_preferences">
-<p t-if="object._context.get('digest_slowdown')" class="font-12" style="font-size: 15px; color: #6b6d70">
-    We've noticed you did not connect these last few days so we've automatically switched your preference to weekly Digests.
-</p>
-<p t-elif="user.has_group('base.group_erp_manager') and object.periodicity == 'daily'" class="font-12" style="font-size: 15px; color: #6b6d70">
-    Prefer a broader overview ?
-    <a t-att-href="'/digest/%s/set_periodicity?periodicity=weekly' % object.id" target="_blank" style="color:#875A7B; font-weight: bold;">Switch to weekly Digests.</a>
-</p>
-<p t-else=""/>
+    <!-- DIGEST MAIN TEMPLATE -->
+    <template id="digest_mail_main">
+<div style="margin:0; padding:0">
+    <table cellspacing="0" cellpadding="0" align="center" border="0" bgcolor="#eeeeee" style="width:100%; font-family: Arial,Helvetica,Verdana,sans-serif;">
+        <tr class="mobile-hide" bgcolor="#875a7b"><td height="70"> </td></tr>
+        <tr bgcolor="#875a7b">
+            <td align="center" valign="top">
+                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border: 1px solid #eeeeee;width: 100%; max-width: 650px; padding: 30px 30px">
+                    <tr>
+                        <td>
+                            <table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
+                                <tr>
+                                    <td class="mobile-w-75" style="text-align: left;">
+                                        <span class="font-16" style="color:#8F8F8F;font-weight:bold;font-size: 20px; line-height: 30px;" t-field="company.name"/>
+                                        <br/>
+                                        <span class="font-18" style="color:#282f33;font-weight:bold;font-size: 25px; line-height: 35px; margin-right: 5px;" t-field="object.name"/>
+                                        <span class="d-block font-12" style="color:#8F8F8F;font-size: 15px;"><t t-esc="datetime.today().strftime('%B %d, %Y')"/></span>
+                                    </td>
+                                    <td valign="top" style="text-align:right;">
+                                        <a href="/web/login" target="_blank" style="display:block;background-color:#875a7b;line-height:40px;text-decoration: none;color: #ffffff;border-radius: 3px;font-size:13px;font-weight:bold;text-align:center;">Connect</a>
+                                    </td>
+                                </tr>
+                            </table>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+    <table t-if="tips" cellspacing="0" cellpadding="0" align="center" border="0" bgcolor="#eeeeee" style="width:100%; font-family: Arial,Helvetica,Verdana,sans-serif;">
+        <tr>
+            <td align="center" valign="top">
+                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border: 1px solid #eeeeee; border-bottom: none;width: 100%; max-width: 650px; padding:30px 30px">
+                    <tr>
+                        <td>
+                            <table t-foreach="tips" t-as="tip" cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
+                                <tr>
+                                    <td valign="top">
+                                        <t t-raw="tip"/>
+                                    </td>
+                                </tr>
+                            </table>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+    <table t-if="kpi_data" cellspacing="0" cellpadding="0" align="center" border="0" bgcolor="#eeeeee" style="width:100%; font-family: Arial,Helvetica,Verdana,sans-serif;">
+        <tr>
+            <td align="center" valign="top">
+                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border: 1px solid #eeeeee; border-bottom: none;width: 100%; max-width: 650px; padding:30px 30px">
+                    <tr t-foreach="kpi_data" t-as="kpi_info">
+                        <td>
+                            <table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
+                                <tr>
+                                    <td align="left" style="border-bottom: 1px solid #eeeeee;">
+                                        <span style="color:#282f33;font-size: 15px; font-weight: bold; line-height: 30px" t-esc="kpi_info['kpi_fullname']"/>
+                                        <a t-if="kpi_info['kpi_action']" t-att-href="'/web#action=%s' % kpi_info['kpi_action']"
+                                            style="float:right; background-color:#875A7B;margin-left: 5px; padding: 5px 12px 5px 12px; text-decoration: none; color: #ffffff; border-radius: 3px; font-size:13px; font-weight:bold; margin-bottom: 5px;">
+                                            View
+                                        </a>
+                                    </td>
+                                </tr>
+                                <tr>
+                                    <td>
+                                        <table cellspacing="0" cellpadding="0" align="center" border="0" style="width:100%; padding: 20px 0;">
+                                            <tr valign="top">
+                                                <td class="mobile-w-33" width="196" style=" text-align: center;">
+                                                    <t t-call="digest.digest_tool_kpi">
+                                                        <t t-set="kpi_value" t-value="kpi_info['kpi_col1']['value']"/>
+                                                        <t t-set="kpi_margin" t-value="kpi_info['kpi_col1']['margin']"/>
+                                                        <t t-set="kpi_subtitle" t-value="kpi_info['kpi_col1']['col_subtitle']"/>
+                                                    </t>
+                                                </td>
+                                                <td class="mobile-w-33" width="196" style=" text-align: center;">
+                                                    <t t-call="digest.digest_tool_kpi">
+                                                        <t t-set="kpi_value" t-value="kpi_info['kpi_col2']['value']"/>
+                                                        <t t-set="kpi_margin" t-value="kpi_info['kpi_col2']['margin']"/>
+                                                        <t t-set="kpi_subtitle" t-value="kpi_info['kpi_col2']['col_subtitle']"/>
+                                                    </t>
+                                                </td>
+                                                <td class="mobile-w-33" width="196" style=" text-align: center;">
+                                                    <t t-call="digest.digest_tool_kpi">
+                                                        <t t-set="kpi_value" t-value="kpi_info['kpi_col3']['value']"/>
+                                                        <t t-set="kpi_margin" t-value="kpi_info['kpi_col3']['margin']"/>
+                                                        <t t-set="kpi_subtitle" t-value="kpi_info['kpi_col3']['col_subtitle']"/>
+                                                    </t>
+                                                </td>
+                                            </tr>
+                                        </table>
+                                    </td>
+                                </tr>
+                            </table>
+                        </td>
+                    </tr>
+                </table>
+                <table t-if="preferences" bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border-left: 1px solid #eeeeee; border-right: 1px solid #eeeeee; width: 100%; max-width: 650px; padding:30px 30px">
+                    <tr>
+                        <td bgcolor="#fafafa" width="100%" style="padding-top: 20px; padding-bottom: 20px;" align="center">
+                            <p t-foreach="preferences" t-as="preference" class="font-12" style="font-size: 15px; color: #6b6d70">
+                                <t t-raw="preference"/>
+                            </p>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+    <table t-if="display_mobile_banner" cellspacing="0" cellpadding="0" align="center" border="0" bgcolor="#eeeeee" style="width:100%; font-family: Arial,Helvetica,Verdana,sans-serif;">
+        <tr>
+            <td align="center" valign="top">
+                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border: 1px solid #eeeeee; width: 100%; max-width: 650px; padding:30px 30px">
+                    <tr valign="top">
+                        <td>
+                            <t t-call="digest.digest_section_mobile"/>
+                        </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+    <table cellspacing="0" cellpadding="0" align="center" border="0" bgcolor="#eeeeee" style="width:100%; font-family: Arial,Helvetica,Verdana,sans-serif;">
+        <tr>
+            <td align="center" valign="top">
+                <table bgcolor="#eeeeee" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="margin-top:30px;font-size: 15px; text-align:center; width: 100%; max-width: 650px;">
+                    <tr>
+                        <td style="color:#8F8F8F;font-weight:bold;font-size: 20px; line-height: 30px;" t-esc="company.name"/>
+                    </tr>
+                    <tr>
+                        <td style="color:#8F8F8F;font-size: 12px; line-height: 30px;">Powered by <a href="https://www.odoo.com" target="_blank" style="color:#875A7B">Odoo</a>.</td>
+                    </tr>
+                    <tr>
+                        <td height="30"> </td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+</div>
     </template>
+
+
+    <!--                     DIGEST PARTS                    -->
+
+    <!-- MOBILE BANNER -->
+    <template id="digest_section_mobile">
+<table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
+    <tr>
+       <td valign="top" width="168">
+            <img src="https://www.odoo.com/web/image/24717933/odoo-mobile.png" alt="Odoo Mobile" width="168"/>
+        </td>
+        <td valign="top">
+            <p class="font-16" style="color:#2d2a26;font-size: 18px;line-height: 23px; margin-top: 20px">Run your business from anywhere with <br class="mobile-hide" /><b>Odoo Mobile</b>.</p>
+            <a href="https://play.google.com/store/apps/details?id=com.odoo.mobile" target="_blank"><img src="https://www.odoo.com/digest/static/src/img/google_play.png" style="display:inline-block;height: 40px; margin-bottom: 5px"/></a>
+            <span class="mobile-hide"> </span>
+            <a href="https://itunes.apple.com/us/app/odoo/id1272543640" target="_blank"><img src="https://www.odoo.com/digest/static/src/img/app_store.png" style="display:inline-block;height: 40px; margin-bottom: 5px"/></a>
+
+        </td>
+    </tr>
+</table>
+    </template>
+
+
+    <!--                     DIGEST TOOLS                    -->
+
+    <!-- KPI DISPLAY -->
+    <template id="digest_tool_kpi">
+<span class="font-25" style="color:#282f33;font-size: 35px; font-weight: bold; text-decoration: none; line-height: 36px;">
+    <t t-esc="kpi_value"/>
+</span><br/>
+<span style="color:#888888;display: inline-block; font-size: 12px; line-height: 18px; text-transform: uppercase;">
+    <t t-esc="kpi_subtitle"/>
+</span>
+<t t-if="kpi_margin != 0.0">
+    <t t-if="kpi_margin &gt; 0.0">
+        <div style="background-color:#d5f1e2; border: 1px solid #c4ecd7; margin: 10px auto;width: 67px; line-height:27px; text-decoration: none; color: #17613a; border-radius: 5px; font-size:13px;">
+            â–² <t t-esc="'%.2f' % kpi_margin"/>
+        </div>
+    </t>
+    <t t-elif="kpi_margin &lt; 0.0">
+        <div style="background-color:#f7dddc; border: 1px solid #f4cfce; margin: 10px auto;width: 67px; line-height:27px; text-decoration: none; color: #712b29; border-radius: 5px; font-size:13px;">
+            â–¼ <t t-esc="'%.2f' % kpi_margin"/>
+        </div>
+    </t>
+</t>
+    </template>
+
 </data></odoo>
diff --git a/addons/digest/data/digest_template_data.xml b/addons/digest/data/digest_template_data.xml
deleted file mode 100644
index 448fa8b20339..000000000000
--- a/addons/digest/data/digest_template_data.xml
+++ /dev/null
@@ -1,209 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<odoo>
-    <record id="digest_mail_template" model="mail.template">
-        <field name="name">Digest: Default main template</field>
-        <field name="model_id" ref="digest.model_digest_digest"/>
-        <field name="subject">${'%s: %s' % (ctx.get('user', user).company_id.name, object.name)}</field>
-        <field name="email_from">${('%s' % (object.company_id.partner_id.email_formatted or user.email_formatted))|safe}</field>
-        <field name="body_html" type="html">
-<div style="margin:0; padding:0">
-    % set user = ctx.get('user', user)
-    % set company = user.company_id
-    % set data = object.compute_kpis(company, user)
-    % set tips = object.compute_tips(company, user, tips_count=ctx.get('tips_count', 1))
-    % set kpi_actions = object.compute_kpis_actions(company, user)
-    % set preferences = object.compute_preferences(company, user)
-    % set kpis = data.yesterday.keys()
-    <table cellspacing="0" cellpadding="0" align="center" border="0" bgcolor="#eeeeee" style="width:100%; font-family: Arial,Helvetica,Verdana,sans-serif;">
-        <tr class="mobile-hide" bgcolor="#875a7b"><td height="70"> </td></tr>
-        <tr bgcolor="#875a7b">
-            <td align="center" valign="top">
-                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border: 1px solid #eeeeee;width: 100%; max-width: 650px; padding: 30px 30px">
-                    <tr>
-                        <td>
-                            <table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
-                                <tr>
-                                    <td class="mobile-w-75" style="text-align: left;">
-                                        <span class="font-16" style="color:#8F8F8F;font-weight:bold;font-size: 20px; line-height: 30px;">${company.name}</span>
-                                        <br/>
-                                        <span class="font-18" style="color:#282f33;font-weight:bold;font-size: 25px; line-height: 35px; margin-right: 5px;">${object.name}</span>
-                                        <span class="d-block font-12" style="color:#8F8F8F;font-size: 15px;">${datetime.date.today().strftime('%B %d, %Y')}</span>
-                                    </td>
-                                    <td valign="top" style="text-align:right;">
-                                        <a href="/web/login" target="_blank" style="display:block;background-color:#875a7b;line-height:40px;text-decoration: none;color: #ffffff;border-radius: 3px;font-size:13px;font-weight:bold;text-align:center;">Connect</a>
-                                    </td>
-                                </tr>
-                            </table>
-                        </td>
-                    </tr>
-                </table>
-            </td>
-        </tr>
-    </table>
-     % if tips:
-    <table cellspacing="0" cellpadding="0" align="center" border="0" bgcolor="#eeeeee" style="width:100%; font-family: Arial,Helvetica,Verdana,sans-serif;">
-        <tr>
-            <td align="center" valign="top">
-                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border: 1px solid #eeeeee; border-bottom: none;width: 100%; max-width: 650px; padding:30px 30px">
-                    <tr>
-                        <td>
-                            % for tip in tips:
-                            <table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
-                                <tr>
-                                    <td valign="top">
-                                        ${tip | safe}
-                                    </td>
-                                </tr>
-                            </table>
-                            % endfor
-                        </td>
-                    </tr>
-                </table>
-            </td>
-        </tr>
-    </table>
-    % endif
-    <table cellspacing="0" cellpadding="0" align="center" border="0" bgcolor="#eeeeee" style="width:100%; font-family: Arial,Helvetica,Verdana,sans-serif;">
-        <tr>
-            <td align="center" valign="top">
-                % for kpi in kpis:
-                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border: 1px solid #eeeeee; border-bottom: none;width: 100%; max-width: 650px; padding:30px 30px">
-                    <tr>
-                        <td>
-                            <table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
-                                <tr>
-                                    <td align="left" style="border-bottom: 1px solid #eeeeee;">
-                                        <span style="color:#282f33;font-size: 15px; font-weight: bold; line-height: 30px">${object.fields_get()[kpi]['string']}</span>
-                                        %if kpi in kpi_actions:
-                                        <a href="/web#action=${kpi_actions[kpi]}" style=" float:right; background-color:#875A7B;margin-left: 5px; padding: 5px 12px 5px 12px; text-decoration: none; color: #ffffff; border-radius: 3px; font-size:13px; font-weight:bold; margin-bottom: 5px;">View</a>
-                                        %endif
-                                    </td>
-                                </tr>
-                                <tr>
-                                    <td>
-                                        <table cellspacing="0" cellpadding="0" align="center" border="0" style="width:100%; padding: 20px 0;">
-                                            <tr valign="top">
-                                                <td class="mobile-w-33" width="196" style=" text-align: center;">
-                                                    <span class="font-25" style="color:#282f33;font-size: 35px; font-weight: bold; text-decoration: none; line-height: 36px;">${data['yesterday'][kpi][kpi]}</span><br/>
-                                                    <span style="color:#888888;display: inline-block; font-size: 12px; line-height: 18px; text-transform: uppercase;">Yesterday</span>
-                                                    % if data['yesterday'][kpi]['margin'] != 0.0:
-                                                        % if data['yesterday'][kpi]['margin'] is greaterthan(0.0):
-                                                    <div style="background-color:#d5f1e2; border: 1px solid #c4ecd7; margin: 10px auto;width: 67px; line-height:27px; text-decoration: none; color: #17613a; border-radius: 5px; font-size:13px;">
-                                                        â–² ${"%.2f" % data['yesterday'][kpi]['margin']}
-                                                    </div>
-                                                        %endif
-                                                        % if data['yesterday'][kpi]['margin'] is lessthan(0.0):
-                                                    <div style="background-color:#f7dddc; border: 1px solid #f4cfce; margin: 10px auto;width: 67px; line-height:27px; text-decoration: none; color: #712b29; border-radius: 5px; font-size:13px;">
-                                                        â–¼ ${"%.2f" % data['yesterday'][kpi]['margin']}
-                                                    </div>
-                                                        %endif
-                                                    %endif
-                                                </td>
-                                                <td class="mobile-w-33" width="196" style=" text-align: center;">
-                                                    <span class="font-25" style="color:#282f33;font-size: 35px; font-weight: bold; text-decoration: none; line-height: 36px;">${data['lastweek'][kpi][kpi]}</span><br/>
-                                                    <span style="color:#888888;display: inline-block; font-size: 12px; line-height: 18px; text-transform: uppercase;">Last 7 Days</span>
-                                                    % if data['lastweek'][kpi]['margin'] != 0.0:
-                                                        % if data['lastweek'][kpi]['margin'] is greaterthan(0.0):
-                                                    <div style="background-color:#d5f1e2; border: 1px solid #c4ecd7; margin: 10px auto;width: 67px; line-height:27px; text-decoration: none; color: #17613a; border-radius: 5px; font-size:13px;">
-                                                        â–² ${"%.2f" % data['lastweek'][kpi]['margin']}
-                                                    </div>
-                                                        %endif
-                                                        % if data['lastweek'][kpi]['margin'] is lessthan(0.0):
-                                                    <div style="background-color:#f7dddc; border: 1px solid #f4cfce; margin: 10px auto;width: 67px; line-height:27px; text-decoration: none; color: #712b29; border-radius: 5px; font-size:13px;">
-                                                        â–¼ ${"%.2f" % data['lastweek'][kpi]['margin']}
-                                                    </div>
-                                                        %endif
-                                                    %endif
-                                                </td>
-                                                <td class="mobile-w-33" width="196" style=" text-align: center;">
-                                                    <span class="font-25" style="color:#282f33;font-size: 35px; font-weight: bold; text-decoration: none; line-height: 36px">${data['lastmonth'][kpi][kpi]}</span><br/>
-                                                    <span style="color:#888888;display: inline-block; font-size: 12px; line-height: 18px; text-transform: uppercase;">Last 30 Days</span>
-                                                    % if data['lastmonth'][kpi]['margin'] != 0.0:
-                                                    % if data['lastmonth'][kpi]['margin'] is greaterthan(0.0):
-                                                    <div style="background-color:#d5f1e2; border: 1px solid #c4ecd7; margin: 10px auto;width: 67px; line-height:27px; text-decoration: none; color: #17613a; border-radius: 5px; font-size:13px;">
-                                                        â–² ${"%.2f" % data['lastmonth'][kpi]['margin']}
-                                                    </div>
-                                                        %endif
-                                                        % if data['lastmonth'][kpi]['margin'] is lessthan(0.0):
-                                                    <div style="background-color:#f7dddc; border: 1px solid #f4cfce; margin: 10px auto;width: 67px; line-height:27px; text-decoration: none; color: #712b29; border-radius: 5px; font-size:13px;">
-                                                        â–¼ ${"%.2f" % data['lastmonth'][kpi]['margin']}
-                                                    </div>
-                                                        %endif
-                                                    %endif
-                                                </td>
-                                            </tr>
-                                        </table>
-                                    </td>
-                                </tr>
-                            </table>
-                        </td>
-                    </tr>
-                </table>
-                % endfor
-                % if preferences:
-                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border-left: 1px solid #eeeeee; border-right: 1px solid #eeeeee; width: 100%; max-width: 650px; padding:30px 30px">
-                    <tr>
-                        <td bgcolor="#fafafa" width="100%" height="50" align="center">
-                            ${preferences | safe}
-                        </td>
-                    </tr>
-                </table>
-                % endif
-                % if user.has_group('base.group_system'):
-                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border-left: 1px solid #eeeeee; border-right: 1px solid #eeeeee; width: 100%; max-width: 650px; padding:30px 30px">
-                    <tr>
-                        <td bgcolor="#fafafa" width="100%" height="50" align="center">
-                            <p class="font-12" style="font-size: 15px; color: #6b6d70">Want to customize this email?
-                                <a href="/web#view_type=form&amp;model=digest.digest&amp;id=${object.id}" target="_blank" style="color:#875A7B; font-weight: bold;">Choose the metrics you care about</a></p>
-                        </td>
-                    </tr>
-                </table>
-                % endif
-            </td>
-        </tr>
-        <tr>
-            <td align="center" valign="top">
-                <table bgcolor="#ffffff" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="border: 1px solid #eeeeee; width: 100%; max-width: 650px; padding:30px 30px">
-                    <tr valign="top">
-                        <td>
-                            <table cellspacing="0" cellpadding="0" border="0" width="580" align="center" style="width:100%; max-width:580px;">
-                                <tr>
-                                   <td valign="top" width="168">
-                                        <img src="https://www.odoo.com/web/image/24717933/odoo-mobile.png" alt="Odoo Mobile" width="168"/>
-                                    </td>
-                                    <td valign="top">
-                                        <p class="font-16" style="color:#2d2a26;font-size: 18px;line-height: 23px; margin-top: 20px">Run your business from anywhere with <br class="mobile-hide" /><b>Odoo Mobile</b>.</p>
-                                        <a href="https://play.google.com/store/apps/details?id=com.odoo.mobile" target="_blank"><img src="https://www.odoo.com/digest/static/src/img/google_play.png" style="display:inline-block;height: 40px; margin-bottom: 5px"/></a>
-                                        <span class="mobile-hide"> </span>
-                                        <a href="https://itunes.apple.com/us/app/odoo/id1272543640" target="_blank"><img src="https://www.odoo.com/digest/static/src/img/app_store.png" style="display:inline-block;height: 40px; margin-bottom: 5px"/></a>
-
-                                    </td>
-                                </tr>
-                            </table>
-                        </td>
-                    </tr>
-                </table>
-            </td>
-        </tr>
-        <tr>
-            <td align="center" valign="top">
-                <table bgcolor="#eeeeee" cellspacing="0" cellpadding="0" width="650" align="center" border="0" style="margin-top:30px;font-size: 15px; text-align:center; width: 100%; max-width: 650px;">
-                    <tr>
-                        <td style="color:#8F8F8F;font-weight:bold;font-size: 20px; line-height: 30px;">${company.name}</td>
-                    </tr>
-                    <tr>
-                        <td style="color:#8F8F8F;font-size: 12px; line-height: 30px;">Powered by <a href="https://www.odoo.com" target="_blank" style="color:#875A7B">Odoo</a>.</td>
-                    </tr>
-                    <tr>
-                        <td height="30"> </td>
-                    </tr>
-                </table>
-            </td>
-        </tr>
-    </table>
-</div>
-        </field>
-        <field name="lang">${user.lang}</field>
-        <field name="auto_delete" eval="True" />
-    </record>
-</odoo>
diff --git a/addons/digest/data/digest_tips_data.xml b/addons/digest/data/digest_tips_data.xml
index ae7ed018ed84..25242ef2bfc4 100644
--- a/addons/digest/data/digest_tips_data.xml
+++ b/addons/digest/data/digest_tips_data.xml
@@ -2,7 +2,7 @@
 <odoo>
     <data noupdate="1">
         <record id="digest_digest_default" model="digest.digest">
-            <field name="name">Awesome Stats with Odoo</field>
+            <field name="name">Your Odoo Periodic Report</field>
             <field name="periodicity">daily</field>
             <field name="user_ids" eval="[(4, ref('base.user_admin'))]"/>
             <field name="next_run_date" eval="(DateTime.now() + timedelta(days=1)).strftime('%Y-%m-%d')"/>
diff --git a/addons/digest/models/digest.py b/addons/digest/models/digest.py
index 3c39f49e244a..c6e51a4fb9a3 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):
diff --git a/addons/digest/views/digest_views.xml b/addons/digest/views/digest_views.xml
index a84df14a2d33..9df32772bee2 100644
--- a/addons/digest/views/digest_views.xml
+++ b/addons/digest/views/digest_views.xml
@@ -46,10 +46,8 @@
                     <group>
                         <group>
                             <field name="periodicity" widget="radio" options="{'horizontal': true}"/>
-                            <field name="template_id" groups="base.group_no_one"/>
                             <field name="next_run_date" groups="base.group_system"/>
                             <field name="company_id" options="{'no_create': True}" invisible="1"/>
-                            <!-- <field name="digest_history_ids" invisible="1"/> -->
                         </group>
                     </group>
                     <notebook>
diff --git a/addons/hr_recruitment/models/digest.py b/addons/hr_recruitment/models/digest.py
index c24cc80e5d59..c8d17019beac 100644
--- a/addons/hr_recruitment/models/digest.py
+++ b/addons/hr_recruitment/models/digest.py
@@ -23,7 +23,7 @@ class Digest(models.Model):
             ])
             record.kpi_hr_recruitment_new_colleagues_value = new_colleagues
 
-    def compute_kpis_actions(self, company, user):
-        res = super(Digest, self).compute_kpis_actions(company, user)
+    def _compute_kpis_actions(self, company, user):
+        res = super(Digest, self)._compute_kpis_actions(company, user)
         res['kpi_hr_recruitment_new_colleagues'] = 'hr.open_view_employee_list_my&menu_id=%s' % self.env.ref('hr.menu_hr_root').id
         return res
diff --git a/addons/im_livechat/models/digest.py b/addons/im_livechat/models/digest.py
index 6c0fefd7ae44..5b23ab65b978 100644
--- a/addons/im_livechat/models/digest.py
+++ b/addons/im_livechat/models/digest.py
@@ -42,8 +42,8 @@ class Digest(models.Model):
                 ('partner_id', '=', self.env.user.partner_id.id)], ['partner_id', 'time_to_answer'], ['partner_id'])
             record.kpi_livechat_response_value = "%.2f" % sum([response['time_to_answer'] for response in response_time]) or 0
 
-    def compute_kpis_actions(self, company, user):
-        res = super(Digest, self).compute_kpis_actions(company, user)
+    def _compute_kpis_actions(self, company, user):
+        res = super(Digest, self)._compute_kpis_actions(company, user)
         res['kpi_livechat_rating'] = 'im_livechat.rating_rating_action_livechat_report'
         res['kpi_livechat_conversations'] = 'im_livechat.im_livechat_report_operator_action'
         res['kpi_livechat_response'] = 'im_livechat.im_livechat_report_channel_time_to_answer_action'
diff --git a/addons/point_of_sale/models/digest.py b/addons/point_of_sale/models/digest.py
index ea115f9b1949..21a65cc4fea9 100644
--- a/addons/point_of_sale/models/digest.py
+++ b/addons/point_of_sale/models/digest.py
@@ -23,7 +23,7 @@ class Digest(models.Model):
                 ('company_id', '=', company.id)
             ]).mapped('amount_total'))
 
-    def compute_kpis_actions(self, company, user):
-        res = super(Digest, self).compute_kpis_actions(company, user)
+    def _compute_kpis_actions(self, company, user):
+        res = super(Digest, self)._compute_kpis_actions(company, user)
         res['kpi_pos_total'] = 'point_of_sale.action_pos_sale_graph&menu_id=%s' % self.env.ref('point_of_sale.menu_point_root').id
         return res
diff --git a/addons/project/models/digest.py b/addons/project/models/digest.py
index 9034e6471253..b584ca956d6b 100644
--- a/addons/project/models/digest.py
+++ b/addons/project/models/digest.py
@@ -23,7 +23,7 @@ class Digest(models.Model):
                 ('company_id', '=', company.id)
             ])
 
-    def compute_kpis_actions(self, company, user):
-        res = super(Digest, self).compute_kpis_actions(company, user)
+    def _compute_kpis_actions(self, company, user):
+        res = super(Digest, self)._compute_kpis_actions(company, user)
         res['kpi_project_task_opened'] = 'project.open_view_project_all&menu_id=%s' % self.env.ref('project.menu_main_pm').id
         return res
diff --git a/addons/sale_management/models/digest.py b/addons/sale_management/models/digest.py
index c2bea94c9807..8828ad98a773 100644
--- a/addons/sale_management/models/digest.py
+++ b/addons/sale_management/models/digest.py
@@ -23,7 +23,7 @@ class Digest(models.Model):
                 ('company_id', '=', company.id)], ['price_total'], ['price_total'])
             record.kpi_all_sale_total_value = sum([channel_sale['price_total'] for channel_sale in all_channels_sales])
 
-    def compute_kpis_actions(self, company, user):
-        res = super(Digest, self).compute_kpis_actions(company, user)
+    def _compute_kpis_actions(self, company, user):
+        res = super(Digest, self)._compute_kpis_actions(company, user)
         res['kpi_all_sale_total'] = 'sale.report_all_channels_sales_action&menu_id=%s' % self.env.ref('sale.sale_menu_root').id
         return res
diff --git a/addons/website_sale/models/digest.py b/addons/website_sale/models/digest.py
index 276a5b546187..4d03289c978b 100644
--- a/addons/website_sale/models/digest.py
+++ b/addons/website_sale/models/digest.py
@@ -25,7 +25,7 @@ class Digest(models.Model):
             ])
             record.kpi_website_sale_total_value = sum(confirmed_website_sales.mapped('amount_total'))
 
-    def compute_kpis_actions(self, company, user):
-        res = super(Digest, self).compute_kpis_actions(company, user)
+    def _compute_kpis_actions(self, company, user):
+        res = super(Digest, self)._compute_kpis_actions(company, user)
         res['kpi_website_sale_total'] = 'website.backend_dashboard&menu_id=%s' % self.env.ref('website.menu_website_configuration').id
         return res
-- 
GitLab