From de5366ac33ea789ea9d06ad448054a97040b2bd7 Mon Sep 17 00:00:00 2001 From: Martin Geubelle <mge@odoo.com> Date: Fri, 19 Jun 2015 13:28:09 +0200 Subject: [PATCH] [ADD] sales_team: new sales dashboard with figures A new view type has been created : sales_dashboard. It is used to display some figure in the sales_team_dashboard. Also added in this commit : 3 fields on res.users to fix sales target - target of won opportunities - target of activities done - target of invoiced sale orders --- addons/crm/__init__.py | 1 + addons/crm/crm_lead.py | 137 +++++++++- addons/crm/res_users.py | 13 + .../crm/static/src/js/sales_team_dashboard.js | 15 ++ addons/crm/views/crm.xml | 3 + addons/sale_crm/__init__.py | 1 + addons/sale_crm/crm_lead.py | 30 ++- addons/sale_crm/res_users.py | 12 + addons/sales_team/__openerp__.py | 3 + addons/sales_team/sales_team.xml | 4 +- addons/sales_team/sales_team_dashboard.xml | 16 +- .../static/src/js/sales_team_dashboard.js | 126 +++++++++ .../static/src/less/sales_team_dashboard.less | 114 +++++++++ .../static/src/xml/sales_team_dashboard.xml | 242 ++++++++++++++++++ openerp/addons/base/ir/ir_ui_view.py | 1 + 15 files changed, 711 insertions(+), 7 deletions(-) create mode 100644 addons/crm/res_users.py create mode 100644 addons/crm/static/src/js/sales_team_dashboard.js create mode 100644 addons/sale_crm/res_users.py create mode 100644 addons/sales_team/static/src/js/sales_team_dashboard.js create mode 100644 addons/sales_team/static/src/less/sales_team_dashboard.less create mode 100644 addons/sales_team/static/src/xml/sales_team_dashboard.xml diff --git a/addons/crm/__init__.py b/addons/crm/__init__.py index e9a8b62b2da3..927f765e8156 100644 --- a/addons/crm/__init__.py +++ b/addons/crm/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import res_users import crm_stage import crm_lead import sales_team diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index c23619393656..4f38d41e9337 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from datetime import datetime, timedelta +from datetime import datetime, timedelta, date +from dateutil.relativedelta import relativedelta import logging from operator import itemgetter @@ -1036,6 +1037,140 @@ Update your business card, phone book, social media,... Send an email right now break return res + def retrieve_sales_dashboard(self, cr, uid, context=None): + + res = { + 'meeting': { + 'today': 0, + 'next_7_days': 0, + }, + 'activity': { + 'today': 0, + 'overdue': 0, + 'next_7_days': 0, + }, + 'closing': { + 'today': 0, + 'overdue': 0, + 'next_7_days': 0, + }, + 'done': { + 'this_month': 0, + 'last_month': 0, + }, + 'won': { + 'this_month': 0, + 'last_month': 0, + }, + 'nb_opportunities': 0, + } + + opportunities = self.search_read( + cr, uid, + [('type', '=', 'opportunity'), ('user_id', '=', uid)], + ['date_deadline', 'next_activity_id', 'date_action', 'date_closed', 'planned_revenue'], context=context) + + for opp in opportunities: + + # Expected closing + if opp['date_deadline']: + date_deadline = datetime.strptime(opp['date_deadline'], tools.DEFAULT_SERVER_DATE_FORMAT).date() + + if date_deadline == date.today(): + res['closing']['today'] += 1 + if date_deadline >= date.today() and date_deadline <= date.today() + timedelta(days=7): + res['closing']['next_7_days'] += 1 + if date_deadline < date.today(): + res['closing']['overdue'] += 1 + + # Next activities + if opp['next_activity_id'] and opp['date_action']: + date_action = datetime.strptime(opp['date_action'], tools.DEFAULT_SERVER_DATE_FORMAT).date() + + if date_action == date.today(): + res['activity']['today'] += 1 + if date_action >= date.today() and date_action <= date.today() + timedelta(days=7): + res['activity']['next_7_days'] += 1 + if date_action < date.today(): + res['activity']['overdue'] += 1 + + # Won in Opportunities + if opp['date_closed']: + date_closed = datetime.strptime(opp['date_closed'], tools.DEFAULT_SERVER_DATETIME_FORMAT).date() + + if date_closed <= date.today() and date_closed >= date.today().replace(day=1): + if opp['planned_revenue']: + res['won']['this_month'] += opp['planned_revenue'] + elif date_closed < date.today().replace(day=1) and date_closed >= date.today().replace(day=1) - relativedelta(months=+1): + if opp['planned_revenue']: + res['won']['last_month'] += opp['planned_revenue'] + + # crm.activity is a very messy model so we need to do that in order to retrieve the actions done. + cr.execute(""" + SELECT + m.id, + m.subtype_id, + m.date, + l.user_id, + l.type + FROM + "mail_message" m + LEFT JOIN + "crm_lead" l + ON + (m.res_id = l.id) + INNER JOIN + "crm_activity" a + ON + (m.subtype_id = a.subtype_id) + WHERE + (m.model = 'crm.lead') AND (l.user_id = %s) AND (l.type = 'opportunity') + """, (uid,)) + activites_done = cr.dictfetchall() + + for act in activites_done: + if act['date']: + date_act = datetime.strptime(act['date'], tools.DEFAULT_SERVER_DATETIME_FORMAT).date() + if date_act <= date.today() and date_act >= date.today().replace(day=1): + res['done']['this_month'] += 1 + elif date_act < date.today().replace(day=1) and date_act >= date.today().replace(day=1) - relativedelta(months=+1): + res['done']['last_month'] += 1 + + # Meetings + min_date = datetime.now().strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) + max_date = (datetime.now() + timedelta(days=8)).strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) + meetings_domain = [ + ('start', '>=', min_date), + ('start', '<=', max_date) + ] + # We need to add 'mymeetings' in the context for the search to be correct. + meetings = self.pool.get('calendar.event').search_read(cr, uid, meetings_domain, ['start'], context=context.update({'mymeetings': 1}) if context else {'mymeetings': 1}) + for meeting in meetings: + if meeting['start']: + start = datetime.strptime(meeting['start'], tools.DEFAULT_SERVER_DATETIME_FORMAT).date() + + if start == date.today(): + res['meeting']['today'] += 1 + if start >= date.today() and start <= date.today() + timedelta(days=7): + res['meeting']['next_7_days'] += 1 + + res['nb_opportunities'] = len(opportunities) + + user = self.pool('res.users').browse(cr, uid, uid, context=context) + res['done']['target'] = user.target_sales_done + res['won']['target'] = user.target_sales_won + + return res + + def modify_target_sales_dashboard(self, cr, uid, target_name, target_value, context=None): + + if target_name in ['won', 'done', 'invoiced']: + # bypass rights (with superuser_id) + self.pool('res.users').write(cr, SUPERUSER_ID, [uid], {'target_sales_' + target_name: target_value}, context=context) + else: + raise UserError(_('This target does not exist.')) + + class crm_lead_tag(osv.Model): _name = "crm.lead.tag" _description = "Category of lead" diff --git a/addons/crm/res_users.py b/addons/crm/res_users.py new file mode 100644 index 000000000000..b6c11b173a12 --- /dev/null +++ b/addons/crm/res_users.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- + +from openerp.osv import osv, fields + +import openerp.addons.product.product + + +class res_users(osv.osv): + _inherit = 'res.users' + _columns = { + 'target_sales_won': fields.integer('Won in Opportunities Target'), + 'target_sales_done': fields.integer('Activities Done Target'), + } diff --git a/addons/crm/static/src/js/sales_team_dashboard.js b/addons/crm/static/src/js/sales_team_dashboard.js new file mode 100644 index 000000000000..b2bec86a7535 --- /dev/null +++ b/addons/crm/static/src/js/sales_team_dashboard.js @@ -0,0 +1,15 @@ +odoo.define('crm.sales_team_dashboard', function (require) { +"use strict"; + +var SalesTeamDashboardView = require('sales_team.dashboard'); +var Model = require('web.Model'); + +SalesTeamDashboardView.include({ + + fetch_data: function() { + return new Model('crm.lead') + .call('retrieve_sales_dashboard', []); + } +}); + +}); diff --git a/addons/crm/views/crm.xml b/addons/crm/views/crm.xml index d29f5f7b3e95..4401c82d1534 100644 --- a/addons/crm/views/crm.xml +++ b/addons/crm/views/crm.xml @@ -6,6 +6,9 @@ <link rel="stylesheet" href="/crm/static/src/css/crm.css"/> <!-- Planner assets --> <script type="text/javascript" src="/crm/static/src/js/web_planner_crm.js"></script> + + <!-- Salesteam dashboard asset --> + <script type="text/javascript" src="/crm/static/src/js/sales_team_dashboard.js"></script> </xpath> </template> </data> diff --git a/addons/sale_crm/__init__.py b/addons/sale_crm/__init__.py index 0223759a9b48..0f8f154c60ff 100644 --- a/addons/sale_crm/__init__.py +++ b/addons/sale_crm/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import res_users import sale_crm import crm_lead diff --git a/addons/sale_crm/crm_lead.py b/addons/sale_crm/crm_lead.py index 0c64496ba07a..0980eff5f782 100644 --- a/addons/sale_crm/crm_lead.py +++ b/addons/sale_crm/crm_lead.py @@ -1,5 +1,7 @@ -from openerp import models, fields, api, _ +from openerp import models, fields, api, _, tools +from datetime import datetime, date +from dateutil.relativedelta import relativedelta import openerp.addons.decimal_precision as dp class crm_lead(models.Model): @@ -21,3 +23,29 @@ class crm_lead(models.Model): sale_amount_total= fields.Float(compute='_get_sale_amount_total', string="Sum of Orders", readonly=True, digits=0) sale_number = fields.Integer(compute='_get_sale_amount_total', string="Number of Quotations", readonly=True) order_ids = fields.One2many('sale.order', 'opportunity_id', string='Orders') + + def retrieve_sales_dashboard(self, cr, uid, context=None): + res = super(crm_lead, self).retrieve_sales_dashboard(cr, uid, context=None) + + res['invoiced'] = { + 'this_month': 0, + 'last_month': 0, + } + account_invoice_domain = [ + ('state', 'in', ['open', 'paid']), + ('user_id', '=', uid), + ('date', '>=', date.today().replace(day=1) - relativedelta(months=+1)) + ] + + invoice_ids = self.pool.get('account.invoice').search_read(cr, uid, account_invoice_domain, ['date', 'amount_untaxed_signed'], context=context) + for inv in invoice_ids: + if inv['date']: + inv_date = datetime.strptime(inv['date'], tools.DEFAULT_SERVER_DATE_FORMAT).date() + if inv_date <= date.today() and inv_date >= date.today().replace(day=1): + res['invoiced']['this_month'] += inv['amount_untaxed_signed'] + elif inv_date < date.today().replace(day=1) and inv_date >= date.today().replace(day=1) - relativedelta(months=+1): + res['invoiced']['last_month'] += inv['amount_untaxed_signed'] + + res['invoiced']['target'] = self.pool('res.users').browse(cr, uid, uid, context=context).target_sales_invoiced + + return res diff --git a/addons/sale_crm/res_users.py b/addons/sale_crm/res_users.py new file mode 100644 index 000000000000..9c393085186b --- /dev/null +++ b/addons/sale_crm/res_users.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- + +from openerp.osv import osv, fields + +import openerp.addons.product.product + + +class res_users(osv.osv): + _inherit = 'res.users' + _columns = { + 'target_sales_invoiced': fields.integer('Invoiced in Sale Orders Target'), + } diff --git a/addons/sales_team/__openerp__.py b/addons/sales_team/__openerp__.py index f9da1c53e002..c9044c904114 100644 --- a/addons/sales_team/__openerp__.py +++ b/addons/sales_team/__openerp__.py @@ -17,6 +17,9 @@ Using this application you can manage Sales Team with CRM and/or Sales 'sales_team.xml', 'sales_team_dashboard.xml', ], + 'qweb': [ + "static/src/xml/sales_team_dashboard.xml", + ], 'demo': ['sales_team_demo.xml'], 'css': ['static/src/css/sales_team.css'], 'installable': True, diff --git a/addons/sales_team/sales_team.xml b/addons/sales_team/sales_team.xml index 1b3ede5d430a..81e27b85597c 100644 --- a/addons/sales_team/sales_team.xml +++ b/addons/sales_team/sales_team.xml @@ -30,7 +30,7 @@ <field name="name">Sales Teams</field> <field name="res_model">crm.team</field> <field name="view_type">form</field> - <field name="view_mode">kanban,tree,form</field> + <field name="view_mode">sales_team_dashboard</field> <field name="context">{}</field> <field name="view_id" ref="crm_team_salesteams_search"/> <field name="help" type="html"> @@ -144,7 +144,9 @@ <template id="assets_backend" name="sales_team assets" inherit_id="web.assets_backend"> <xpath expr="." position="inside"> <link rel="stylesheet" href="/sales_team/static/src/css/sales_team.css"/> + <link rel="stylesheet" href="/sales_team/static/src/less/sales_team_dashboard.less"/> <script type="text/javascript" src="/sales_team/static/src/js/sales_team.js"></script> + <script type="text/javascript" src="/sales_team/static/src/js/sales_team_dashboard.js"></script> </xpath> </template> diff --git a/addons/sales_team/sales_team_dashboard.xml b/addons/sales_team/sales_team_dashboard.xml index 36e44c8e5353..79f2bfba168e 100644 --- a/addons/sales_team/sales_team_dashboard.xml +++ b/addons/sales_team/sales_team_dashboard.xml @@ -1,10 +1,10 @@ <odoo> <!-- Case Teams Salesteams dashboard view --> <record id="crm_team_salesteams_view_kanban" model="ir.ui.view" > - <field name="name">crm.team.kanban</field> + <field name="name">crm.team.dashboard</field> <field name="model">crm.team</field> <field name="arch" type="xml"> - <kanban class="oe_background_grey o_kanban_dashboard o_salesteam_kanban" create="0"> + <sales_team_dashboard class="oe_background_grey o_kanban_dashboard o_salesteam_kanban" create="0"> <field name="name"/> <field name="user_id"/> <field name="member_ids"/> @@ -58,7 +58,15 @@ </div> </t> </templates> - </kanban> + </sales_team_dashboard> </field> </record> -</odoo> \ No newline at end of file + + <record id="action_sales_team_dashboard" model="ir.actions.client"> + <field name="name">Sales Team Dashboard</field> + <field name="res_model">crm.team</field> + <field name="tag">sales_team_dashboard</field> + </record> + + <menuitem action="action_sales_team_dashboard" id="menu_sales_team_dashboard" parent="base.menu_sales" sequence="20" groups="base.group_sale_salesman,base.group_sale_manager"/> +</odoo> diff --git a/addons/sales_team/static/src/js/sales_team_dashboard.js b/addons/sales_team/static/src/js/sales_team_dashboard.js new file mode 100644 index 000000000000..0f6fe34ad9eb --- /dev/null +++ b/addons/sales_team/static/src/js/sales_team_dashboard.js @@ -0,0 +1,126 @@ +odoo.define('sales_team.dashboard', function (require) { +"use strict"; + +var core = require('web.core'); +var KanbanView = require('web_kanban.KanbanView'); +var Model = require('web.Model'); + +var QWeb = core.qweb; + +var _t = core._t; +var _lt = core._lt; + +var SalesTeamDashboardView = KanbanView.extend({ + display_name: _lt('Dashboard'), + icon: 'fa-dashboard', + view_type: "sales_team_dashboard", + searchview_hidden: true, + events: { + 'click .o_dashboard_action': 'on_dashboard_action_clicked', + 'click .o_target_to_set': 'on_dashboard_target_clicked', + }, + + fetch_data: function() { + // Overwrite this function with useful data + return $.Deferred().resolve(); + }, + + render: function() { + var super_render = this._super; + var self = this; + + this.fetch_data().then(function(result){ + + self.show_demo = !(result && result['nb_opportunities'] > 0); + + var sales_dashboard = QWeb.render('sales_team.SalesDashboard', { + show_demo: self.show_demo, + values: result, + }); + super_render.call(self); + $(sales_dashboard).prependTo(self.$el); + }); + }, + + on_dashboard_action_clicked: function(ev){ + ev.preventDefault(); + + var self = this; + var $action = $(ev.currentTarget); + var action_name = $action.attr('name'); + var additional_context = {} + + // TODO: find a better way to add defaults to search view + if (action_name === 'calendar.action_calendar_event') { + additional_context['search_default_mymeetings'] = 1; + } + if (action_name === 'crm.crm_lead_opportunities') { + additional_context['search_default_assigned_to_me'] = 1; + } + + new Model("ir.model.data") + .call("xmlid_to_res_id", [action_name]) + .then(function(data) { + if (data){ + self.do_action(data, {additional_context: additional_context}); + } + }); + }, + + on_change_input_target: function(e) { + + var self = this; + var $input = $(e.target); + var target_name = $input.attr('name'); + var target_value = $input.val(); + + if(isNaN($input.val())) { + this.do_warn(_t("Wrong value entered!"), _t("Only Integer Value should be valid.")); + } else { + this.modify_target(target_name, target_value).then(function() { + self.render(); + }); + } + }, + + modify_target: function(target_name, value){ + return new Model('crm.lead') + .call('modify_target_sales_dashboard', [target_name, value]) + + }, + + on_dashboard_target_clicked: function(ev){ + + if (this.show_demo) { + // The user is not allowed to modify the targets in demo mode + return; + } + + var self = this; + var $target = $(ev.currentTarget); + var target_name = $target.attr('name'); + var target_value = $target.attr('value'); + + var $input = $('<input/>'); + $input.attr('name', target_name); + if (target_value) { + $input.attr('value', target_value); + } + $input.on('keyup input', function(e) { + if(e.which === $.ui.keyCode.ENTER) { + self.on_change_input_target(e); + } + }); + $input.on('blur', function(e) { + self.on_change_input_target(e); + }); + $target.replaceWith($input); + $input.focus().select(); + }, +}); + +core.view_registry.add('sales_team_dashboard', SalesTeamDashboardView); + +return SalesTeamDashboardView + +}); diff --git a/addons/sales_team/static/src/less/sales_team_dashboard.less b/addons/sales_team/static/src/less/sales_team_dashboard.less new file mode 100644 index 000000000000..9bd4f900bafb --- /dev/null +++ b/addons/sales_team/static/src/less/sales_team_dashboard.less @@ -0,0 +1,114 @@ +.o_kanban_view.o_kanban_dashboard.o_salesteam_kanban { + + padding: 0; // remove the padding of the kanban view + + .o_kanban_record { + min-width: 450px; + + @media (max-width: @grid-float-breakpoint) { + min-width: inherit; + } + } + + .o_sales_dashboard { + padding-top: 20px; + background-color: @odoo-view-background-color; + position: relative; + .o-flex(0, 0,100%); + .o-flex-display(); + + .o_welcome_message { + position: absolute; + left: 0; + right: 0; + .o-flex-display(); + justify-content: center; + + .o_welcome_image { + padding: 20px; + } + .o_welcome_content { + > a { + color: white; + display: inline-block; + } + } + } + + .o_demo { + opacity: 0.07; + } + + .o_left_panel { + .o-flex(1, 0, 0); + } + .o_right_panel { + .o-flex(1, 0, 0); + + @media (max-width: @grid-float-breakpoint) { + display: none; + } + } + + table { + -webkit-border-horizontal-spacing: 10px; + border-collapse: separate; + + > tbody { + > tr { + > td { + vertical-align: middle; + text-align: center; + border-top: 1px solid @odoo-view-background-color; + + span { + display: inline; + } + + .o_target_reached { + color: green; + } + + a:hover { + text-decoration: none; + } + + &.o_main { + background-color: @odoo-brand-optional; + &:hover { + background-color: darken(@odoo-brand-optional, 10%); + } + a { + color: white; + } + } + &.o_warning { + background-color: orange; + &:hover { + background-color: darken(orange, 10%); + } + a { + color: white; + } + } + &.o_secondary { + background-color: lightgrey; + &:hover { + background-color: darken(lightgrey, 10%); + } + a { + color: black; + } + } + &.o_highlight, .o_highlight { + font-size: 20px; + } + &.o_text { + text-align: left; + } + } + } + } + } + } +} diff --git a/addons/sales_team/static/src/xml/sales_team_dashboard.xml b/addons/sales_team/static/src/xml/sales_team_dashboard.xml new file mode 100644 index 000000000000..985f7ca04310 --- /dev/null +++ b/addons/sales_team/static/src/xml/sales_team_dashboard.xml @@ -0,0 +1,242 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<templates> + + <!-- + This template is the rubbon at the top of the salesteam dashboard that adds + some figures to it. We call this rubbon the "SalesDashboard". + --> + <t t-name="sales_team.SalesDashboard"> + + <div class="o_sales_dashboard"> + <div t-attf-class="o_left_panel #{show_demo ? 'o_demo' : ''}"> + <table border="0" class="table"> + <tr> + <td class="o_highlight o_text"> + Today + </td> + <td class="o_main" title="To Calendar"> + <a href="#" class="o_dashboard_action" name="calendar.action_calendar_event"> + <span class="o_highlight"> + <t t-if="!show_demo"> + <t t-esc="values['meeting']['today']"/> + </t> + <t t-if="show_demo"> + 0 + </t> + </span><br/> + Meetings + </a> + </td> + <td class="o_main" title="To Activities"> + <a href="#" class="o_dashboard_action" name="crm.crm_lead_action_activities"> + <span class="o_highlight"> + <t t-if="!show_demo"> + <t t-esc="values['activity']['today']"/> + </t> + <t t-if="show_demo"> + 7 + </t> + </span> <br/> + Next Actions + </a> + </td> + <td class="o_main" title="To Opportunities"> + <a href="#" class="o_dashboard_action" name="crm.crm_lead_opportunities"> + <span class="o_highlight"> + <t t-if="!show_demo"> + <t t-esc="values['closing']['today']"/> + </t> + <t t-if="show_demo"> + 4 + </t> + </span> <br/> + Expected Closing + </a> + </td> + </tr> + <tr> + <td></td> + <td></td> + <!-- Hide overdue when there is none --> + <td t-if="show_demo or (values and values['activity']['overdue'] != 0)" class="o_warning" title="To Activities"> + <a href="#" class="o_dashboard_action" name="crm.crm_lead_action_activities"> + <t t-if="!show_demo"> + <t t-esc="values['activity']['overdue']"/> overdue + </t> + <t t-if="show_demo"> + 2 + </t> + </a> + </td> + <td t-if="show_demo or (values and values['closing']['overdue'] != 0)" class="o_warning" title="To Opportunities"> + <a href="#" class="o_dashboard_action" name="crm.crm_lead_opportunities"> + <t t-if="!show_demo"> + <t t-esc="values['closing']['overdue']"/> overdue + </t> + <t t-if="show_demo"> + 3 + </t> + </a> + </td> + </tr> + <tr> + <td class="o_text">Next 7 days</td> + <td class="o_main" title="To Calendar"> + <a href="#" class="o_dashboard_action" name="calendar.action_calendar_event"> + <t t-if="!show_demo"> + <t t-esc="values['meeting']['next_7_days']"/> + </t> + <t t-if="show_demo"> + 9 + </t> + </a> + </td> + <td class="o_main" title="To Activities"> + <a href="#" class="o_dashboard_action" name="crm.crm_lead_action_activities"> + <t t-if="!show_demo"> + <t t-esc="values['activity']['next_7_days']"/> + </t> + <t t-if="show_demo"> + 28 + </t> + </a> + </td> + <td class="o_main" title="To Opportunities"> + <a href="#" class="o_dashboard_action" name="crm.crm_lead_opportunities"> + <t t-if="!show_demo"> + <t t-esc="values['closing']['next_7_days']"/> + </t> + <t t-if="show_demo"> + 17 + </t> + </a> + </td> + </tr> + </table> + </div> + + <div t-attf-class="o_right_panel #{show_demo ? 'o_demo' : ''}"> + <table class="table"> + <tr> + <td class="o_highlight o_text"> + This Month + </td> + <td class="o_secondary" title="To Activity Report"> + <a href="#" class="o_dashboard_action" name="crm.crm_activity_report_action"> + <span t-attf-class="o_highlight #{values and values['done']['target'] and values['done']['this_month'] >= values['done']['target'] ? 'o_target_reached' : ''}"> + <t t-if="!show_demo"> + <t t-esc="values['done']['this_month']"/> + </t> + <t t-if="show_demo"> + 94 + </t> + </span> <br/> + Activities Done + </a> + </td> + <td class="o_secondary" title="To Opportunity Report"> + <a href="#" class="o_dashboard_action" name="crm.crm_opportunity_report_action"> + <span t-attf-class="o_highlight #{values and values['won']['target'] and values['won']['this_month'] >= values['won']['target'] ? 'o_target_reached' : ''}"> + <t t-if="!show_demo"> + <t t-esc="values['won']['this_month']"/> + </t> + <t t-if="show_demo" class="o_target_reached"> + 78,140.03€ + </t> + </span> <br/> + Won in Opportunities + </a> + </td> + <td t-if="values['invoiced']" class="o_secondary" title="To Invoice Report"> + <a href="#" class="o_dashboard_action" name="account.action_account_invoice_report_all"> + <span t-attf-class="o_highlight #{values and values['invoiced']['target'] and values['invoiced']['this_month'] >= values['invoiced']['target'] ? 'o_target_reached' : ''}"> + <t t-if="!show_demo"> + <t t-esc="values['invoiced']['this_month']"/> + </t> + <t t-if="show_demo"> + 35,029.39€ + </t> + </span> <br/> + Invoiced + </a> + </td> + </tr> + <tr> + <td class="o_text">Target</td> + <td class="o_secondary"> + <span t-if="!show_demo" class="o_target_to_set" name='done' t-att-value="values['done']['target'] ? values['done']['target'] : undefined" title="Click to set"> + <t t-if="values['done']['target']"> + <t t-esc="values['done']['target']"/> + </t> + <t t-if="!values['done']['target']"> + Click to set + </t> + </span> + <span t-if="show_demo"> + 100 + </span> + </td> + <td class="o_secondary"> + <span t-if="!show_demo" class="o_target_to_set" name='won' t-att-value="values['won']['target'] ? values['won']['target'] : undefined" title="Click to set"> + <t t-if="values['won']['target']"> + <t t-esc="values['won']['target']"/> + </t> + <t t-if="!values['won']['target']"> + Click to set + </t> + </span> + <span t-if="show_demo"> + 80.000€ + </span> + </td> + <td t-if="show_demo or values['invoiced']" class="o_secondary"> + <span t-if="!show_demo" class="o_target_to_set" name='invoiced' t-att-value="values['invoiced']['target'] ? values['invoiced']['target'] : undefined" title="Click to set"> + <t t-if="values['invoiced']['target']"> + <t t-esc="values['invoiced']['target']"/> + </t> + <t t-if="!values['invoiced']['target']"> + Click to set + </t> + </span> + <span t-if="show_demo"> + Click to set + </span> + </td> + </tr> + <tr> + <td class="o_text">Last Month</td> + <td class="o_secondary" title="To Activity Report"> + <a href="#" class="o_dashboard_action" name="crm.crm_activity_report_action"> + <t t-esc="values['done']['last_month']"/> + </a> + </td> + <td class="o_secondary" title="To Opportunity Report"> + <a href="#" class="o_dashboard_action" name="crm.crm_opportunity_report_action"> + <t t-esc="values['won']['last_month']"/> + </a> + </td> + <td t-if="values['invoiced']" class="o_secondary" title="To Invoice Report"> + <a href="#" class="o_dashboard_action" name="account.action_account_invoice_report_all"> + <t t-esc="values['invoiced']['last_month']"/> + </a> + </td> + </tr> + </table> + </div> + + <div t-if="show_demo" class="o_welcome_message"> + <div class="o_welcome_image"> + <i class="fa fa-smile-o fa-5x"></i> + </div> + <div class="o_welcome_content"> + <h2>Hi there!</h2> + <h4>Great sales journeys start with a clean pipeline.</h4> + <h4>Have fun playing with Odoo CRM.</h4> + <a class="btn btn-primary o_dashboard_action" name="crm.crm_lead_opportunities">Your Pipeline</a> + </div> + </div> + </div> + </t> + +</templates> diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index 97b5fbe17fe6..c780e70a0885 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -205,6 +205,7 @@ class view(osv.osv): ('diagram','Diagram'), ('gantt', 'Gantt'), ('kanban', 'Kanban'), + ('sales_team_dashboard', 'Sales Team Dashboard'), ('search','Search'), ('qweb', 'QWeb')], string='View Type'), 'arch': fields.function(_arch_get, fnct_inv=_arch_set, string='View Architecture', type="text", nodrop=True), -- GitLab