From 3b833c73a77041efa7b09b38863a8d3cb217bd11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Phil=C3=A9mon=20van=20Helden?= Date: Mon, 20 Sep 2021 08:02:23 +0000 Subject: [PATCH] [IMP] mass_mailing, *: improve mailing stat buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *mass_mailing_crm, mass_mailing_sale Currently, the Mailing stat buttons have a few problems. The business reporting smart buttons send the user to a list of linked records, which doesn't provide much value (e.g. what is the user supposed to do with a list of 12345 leads?). They also show wrong values depending on access rights. The Mailing KPI smart buttons lead to a blank screen if there's no data to show. In this commit, we add/adapt all action helpers to help users understand what is going on. For the business reporting smart buttons we instead send users to relevant reporting actions, so they can make sense of the data we showed before. The values of the business reporting smart buttons are now calculated as a super user. We also change some domains from the whole UTM Sale Domain to only using Source ID, which simplifies computation while staying accurate. task-2604515 closes odoo/odoo#76798 Signed-off-by: Warnon Aurélien (awa) --- .../report/account_invoice_report_view.xml | 5 +- addons/crm/views/crm_lead_views.xml | 5 +- addons/mass_mailing/models/mailing.py | 37 +++++- .../models/mailing_mailing.py | 32 ++++-- .../views/mailing_mailing_views.xml | 3 +- .../models/mailing_mailing.py | 105 ++++++++++-------- .../views/mailing_mailing_views.xml | 4 +- addons/sale/report/sale_report_views.xml | 5 +- 8 files changed, 127 insertions(+), 69 deletions(-) diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml index c2788f27c49..11ea2253821 100644 --- a/addons/account/report/account_invoice_report_view.xml +++ b/addons/account/report/account_invoice_report_view.xml @@ -117,7 +117,10 @@ - + + diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml index c17c07d0c58..e0cece23871 100644 --- a/addons/crm/views/crm_lead_views.xml +++ b/addons/crm/views/crm_lead_views.xml @@ -998,7 +998,10 @@ - + + diff --git a/addons/mass_mailing/models/mailing.py b/addons/mass_mailing/models/mailing.py index 0956945843a..12460592403 100644 --- a/addons/mass_mailing/models/mailing.py +++ b/addons/mass_mailing/models/mailing.py @@ -11,6 +11,7 @@ import threading import werkzeug.urls from ast import literal_eval from dateutil.relativedelta import relativedelta +from markupsafe import Markup from werkzeug.urls import url_join from odoo import api, fields, models, tools, _ @@ -544,12 +545,19 @@ class MassMailing(models.Model): def action_view_clicked(self): model_name = self.env['ir.model']._get('link.tracker').display_name + recipient = self.env['ir.model']._get(self.mailing_model_real).display_name + helper_header = _("No %s clicked your mailing yet!", recipient) + helper_message = _("Link Trackers will measure how many times each link is clicked as well as " + "the proportion of %s who clicked at least once in your mailing.", recipient) return { 'name': model_name, 'type': 'ir.actions.act_window', 'view_mode': 'tree', 'res_model': 'link.tracker', 'domain': [('mass_mailing_id.id', '=', self.id)], + 'help': Markup('

%s

%s

') % ( + helper_header, helper_message, + ), 'context': dict(self._context, create=False) } @@ -566,28 +574,45 @@ class MassMailing(models.Model): return self._action_view_documents_filtered('delivered') def _action_view_documents_filtered(self, view_filter): - if view_filter in ('reply', 'bounce'): + model_name = self.env['ir.model']._get(self.mailing_model_real).display_name + helper_header = None + helper_message = None + if view_filter == 'reply': found_traces = self.mailing_trace_ids.filtered(lambda trace: trace.trace_status == view_filter) + helper_header = _("No %s replied to your mailing yet!", model_name) + helper_message = _("To track how many replies this mailing gets, make sure " + "its reply-to address belongs to this database.") + elif view_filter == 'bounce': + found_traces = self.mailing_trace_ids.filtered(lambda trace: trace.trace_status == view_filter) + helper_header = _("No %s address bounced yet!", model_name) + helper_message = _("Bounce happens when a mailing cannot be delivered (fake address, " + "server issues, ...). Check each record to see what went wrong.") elif view_filter == 'open': found_traces = self.mailing_trace_ids.filtered(lambda trace: trace.trace_status in ('open', 'reply')) - elif view_filter == 'click': - found_traces = self.mailing_trace_ids.filtered(lambda trace: trace.links_click_datetime) + helper_header = _("No %s opened your mailing yet!", model_name) + helper_message = _("Come back once your mailing has been sent to track who opened your mailing.") elif view_filter == 'delivered': found_traces = self.mailing_trace_ids.filtered(lambda trace: trace.trace_status in ('sent', 'open', 'reply')) + helper_header = _("No %s received your mailing yet!", model_name) + helper_message = _("Wait until your mailing has been sent to check how many recipients you managed to reach.") elif view_filter == 'sent': found_traces = self.mailing_trace_ids.filtered(lambda trace: trace.sent_datetime) else: found_traces = self.env['mailing.trace'] res_ids = found_traces.mapped('res_id') - model_name = self.env['ir.model']._get(self.mailing_model_real).display_name - return { + action = { 'name': model_name, 'type': 'ir.actions.act_window', 'view_mode': 'tree', 'res_model': self.mailing_model_real, 'domain': [('id', 'in', res_ids)], - 'context': dict(self._context, create=False) + 'context': dict(self._context, create=False), } + if helper_header and helper_message: + action['help'] = Markup('

%s

%s

') % ( + helper_header, helper_message, + ), + return action def update_opt_out(self, email, list_ids, value): if len(list_ids) > 0: diff --git a/addons/mass_mailing_crm/models/mailing_mailing.py b/addons/mass_mailing_crm/models/mailing_mailing.py index b804327242a..a2796d529f4 100644 --- a/addons/mass_mailing_crm/models/mailing_mailing.py +++ b/addons/mass_mailing_crm/models/mailing_mailing.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from markupsafe import Markup from odoo import fields, models, _, tools @@ -9,27 +10,40 @@ class MassMailing(models.Model): _inherit = 'mailing.mailing' use_leads = fields.Boolean('Use Leads', compute='_compute_use_leads') - crm_lead_count = fields.Integer('Leads/Opportunities Count', groups='sales_team.group_sale_salesman', compute='_compute_crm_lead_count') + crm_lead_count = fields.Integer('Leads/Opportunities Count', compute='_compute_crm_lead_count') def _compute_use_leads(self): self.use_leads = self.env.user.has_group('crm.group_use_lead') def _compute_crm_lead_count(self): - lead_data = self.env['crm.lead'].with_context(active_test=False).read_group( + lead_data = self.env['crm.lead'].with_context(active_test=False).sudo().read_group( [('source_id', 'in', self.source_id.ids)], - ['source_id'], ['source_id'] + ['source_id'], ['source_id'], ) mapped_data = {datum['source_id'][0]: datum['source_id_count'] for datum in lead_data} for mass_mailing in self: mass_mailing.crm_lead_count = mapped_data.get(mass_mailing.source_id.id, 0) def action_redirect_to_leads_and_opportunities(self): - view = 'crm.crm_lead_all_leads' if self.use_leads else 'crm.crm_lead_opportunities' - action = self.env.ref(view).sudo().read()[0] - action['view_mode'] = 'tree,kanban,graph,pivot,form,calendar' - action['domain'] = [('source_id', 'in', self.source_id.ids)] - action['context'] = {'active_test': False, 'create': False} - return action + text = _("Leads") if self.use_leads else _("Opportunities") + helper_header = _("No %s yet!", text) + helper_message = _("Note that Odoo cannot track replies if they are sent towards email addresses to this database.") + return { + 'context': { + 'active_test': False, + 'create': False, + 'search_default_group_by_create_date_day': True, + 'crm_lead_view_hide_month': True, + }, + 'domain': [('source_id', 'in', self.source_id.ids)], + 'help': Markup('

%s

%s

') % ( + helper_header, helper_message, + ), + 'name': _("Leads Analysis"), + 'res_model': 'crm.lead', + 'type': 'ir.actions.act_window', + 'view_mode': 'graph,pivot,tree,form', + } def _prepare_statistics_email_values(self): self.ensure_one() diff --git a/addons/mass_mailing_crm/views/mailing_mailing_views.xml b/addons/mass_mailing_crm/views/mailing_mailing_views.xml index dc75114a7cb..c57c6837559 100644 --- a/addons/mass_mailing_crm/views/mailing_mailing_views.xml +++ b/addons/mass_mailing_crm/views/mailing_mailing_views.xml @@ -10,7 +10,6 @@ type="object" icon="fa-star" class="oe_stat_button" - groups="sales_team.group_sale_salesman" attrs="{'invisible': [('state', '=', 'draft')]}">
@@ -22,4 +21,4 @@ - \ No newline at end of file + diff --git a/addons/mass_mailing_sale/models/mailing_mailing.py b/addons/mass_mailing_sale/models/mailing_mailing.py index ad4f2f1fc8d..40e6091a98a 100644 --- a/addons/mass_mailing_sale/models/mailing_mailing.py +++ b/addons/mass_mailing_sale/models/mailing_mailing.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. - +from markupsafe import Markup from odoo import api, fields, models, _, tools from odoo.osv import expression @@ -10,64 +10,77 @@ class MassMailing(models.Model): _name = 'mailing.mailing' _inherit = 'mailing.mailing' - sale_quotation_count = fields.Integer('Quotation Count', groups='sales_team.group_sale_salesman', compute='_compute_sale_quotation_count') - sale_invoiced_amount = fields.Integer('Invoiced Amount', groups='sales_team.group_sale_salesman', compute='_compute_sale_invoiced_amount') + sale_quotation_count = fields.Integer('Quotation Count', compute='_compute_sale_quotation_count') + sale_invoiced_amount = fields.Integer('Invoiced Amount', compute='_compute_sale_invoiced_amount') @api.depends('mailing_domain') def _compute_sale_quotation_count(self): - has_so_access = self.env['sale.order'].check_access_rights('read', raise_exception=False) - if not has_so_access: - self.sale_quotation_count = 0 - return + quotation_data = self.env['sale.order'].sudo().read_group( + [('source_id', 'in', self.source_id.ids)], + ['source_id'], ['source_id'], + ) + mapped_data = {datum['source_id'][0]: datum['source_id_count'] for datum in quotation_data} for mass_mailing in self: - mass_mailing.sale_quotation_count = self.env['sale.order'].search_count(mass_mailing._get_sale_utm_domain()) + mass_mailing.sale_quotation_count = mapped_data.get(mass_mailing.source_id.id, 0) @api.depends('mailing_domain') def _compute_sale_invoiced_amount(self): - if not self.user_has_groups('sales_team.group_sale_salesman') or not self.user_has_groups('account.group_account_invoice'): - self.sale_invoiced_amount = 0 - return + domain = expression.AND([ + [('source_id', 'in', self.source_id.ids)], + [('state', 'not in', ['draft', 'cancel'])] + ]) + moves_data = self.env['account.move'].sudo().read_group( + domain, ['source_id', 'amount_untaxed_signed'], ['source_id'], + ) + mapped_data = {datum['source_id'][0]: datum['amount_untaxed_signed'] for datum in moves_data} for mass_mailing in self: - domain = expression.AND([ - mass_mailing._get_sale_utm_domain(), - [('state', 'not in', ['draft', 'cancel'])] - ]) - moves = self.env['account.move'].search_read(domain, ['amount_untaxed_signed']) - mass_mailing.sale_invoiced_amount = sum(i['amount_untaxed_signed'] for i in moves) + mass_mailing.sale_invoiced_amount = mapped_data.get(mass_mailing.source_id.id, 0) def action_redirect_to_quotations(self): - action = self.env["ir.actions.actions"]._for_xml_id("sale.action_quotations_with_onboarding") - action['domain'] = self._get_sale_utm_domain() - action['context'] = {'create': False} - return action + helper_header = _("No Quotations yet!") + helper_message = _("Quotations will appear here once your customers add " + "products to their Carts or when your sales reps assign this mailing.") + return { + 'context': { + 'create': False, + 'search_default_group_by_date_day': True, + 'sale_report_view_hide_date': True, + }, + 'domain': [('source_id', '=', self.source_id.id)], + 'help': Markup('

%s

%s

') % ( + helper_header, helper_message, + ), + 'name': _("Sales Analysis"), + 'res_model': 'sale.report', + 'type': 'ir.actions.act_window', + 'view_mode': 'graph,pivot,tree,form', + } def action_redirect_to_invoiced(self): - action = self.env["ir.actions.actions"]._for_xml_id("account.action_move_out_invoice_type") - moves = self.env['account.move'].search(self._get_sale_utm_domain()) - action['context'] = { - 'create': False, - 'edit': False, - 'view_no_maturity': True + domain = expression.AND([ + [('source_id', '=', self.source_id.id)], + [('state', 'not in', ['draft', 'cancel'])] + ]) + moves = self.env['account.move'].search(domain) + helper_header = _("No Revenues yet!") + helper_message = _("Revenues will appear here once orders are turned into invoices.") + return { + 'context': { + 'create': False, + 'edit': False, + 'view_no_maturity': True, + 'search_default_group_by_invoice_date_week': True, + 'invoice_report_view_hide_invoice_date': True, + }, + 'domain': [('move_id', 'in', moves.ids)], + 'help': Markup('

%s

%s

') % ( + helper_header, helper_message, + ), + 'name': _("Invoices Analysis"), + 'res_model': 'account.invoice.report', + 'type': 'ir.actions.act_window', + 'view_mode': 'graph,pivot,tree,form', } - action['domain'] = [ - ('id', 'in', moves.ids), - ('move_type', 'in', ('out_invoice', 'out_refund', 'in_invoice', 'in_refund', 'out_receipt', 'in_receipt')), - ('state', 'not in', ['draft', 'cancel']) - ] - action['context'] = {'create': False} - return action - - def _get_sale_utm_domain(self): - res = [] - if self.campaign_id: - res.append(('campaign_id', '=', self.campaign_id.id)) - if self.source_id: - res.append(('source_id', '=', self.source_id.id)) - if self.medium_id: - res.append(('medium_id', '=', self.medium_id.id)) - if not res: - res.append((0, '=', 1)) - return res def _prepare_statistics_email_values(self): self.ensure_one() diff --git a/addons/mass_mailing_sale/views/mailing_mailing_views.xml b/addons/mass_mailing_sale/views/mailing_mailing_views.xml index 76308b22c39..219c5a24fd4 100644 --- a/addons/mass_mailing_sale/views/mailing_mailing_views.xml +++ b/addons/mass_mailing_sale/views/mailing_mailing_views.xml @@ -10,7 +10,6 @@ type="object" icon="fa-pencil-square-o" class="oe_stat_button" - groups="sales_team.group_sale_salesman" attrs="{'invisible': [('state', '=', 'draft')]}"> @@ -18,11 +17,10 @@ type="object" icon="fa-dollar" class="oe_stat_button" - groups="sales_team.group_sale_salesman" attrs="{'invisible': [('state', '=', 'draft')]}" > - \ No newline at end of file + diff --git a/addons/sale/report/sale_report_views.xml b/addons/sale/report/sale_report_views.xml index 6121877d918..33bfcdfe7b4 100644 --- a/addons/sale/report/sale_report_views.xml +++ b/addons/sale/report/sale_report_views.xml @@ -76,7 +76,10 @@ - + +