From 3b4b65849bea3ab342ad919eb4e1027538852359 Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Tue, 17 Jan 2017 12:15:13 +0530 Subject: [PATCH 1/9] [FIX] website: update google analytics documentation link --- addons/website/static/src/xml/website.backend.xml | 2 +- addons/website/views/res_config_views.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/website/static/src/xml/website.backend.xml b/addons/website/static/src/xml/website.backend.xml index ad7c4715378..413c3d35631 100644 --- a/addons/website/static/src/xml/website.backend.xml +++ b/addons/website/static/src/xml/website.backend.xml @@ -42,7 +42,7 @@
Your Google Analytic Client ID: -
Go to our User Documentation to get more information. +
Go to our User Documentation to get more information.
diff --git a/addons/website/views/res_config_views.xml b/addons/website/views/res_config_views.xml index 0683496ec02..31d5f4e3ba7 100644 --- a/addons/website/views/res_config_views.xml +++ b/addons/website/views/res_config_views.xml @@ -98,7 +98,7 @@
- Tutorial From 47b532638e82db78a7b9a4d4fb437a1fa29903e6 Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Tue, 28 Feb 2017 11:55:28 +0100 Subject: [PATCH 2/9] [IMP] website_*: improve groups on website menu items This commit adds required groups on website application menu items like blog and slides. It also move Website Admin menu definition from res config views to website views, because it is where it should always have been defined. This commit also update some sequences to reorder Apps. --- addons/website/views/res_config_views.xml | 10 ---------- addons/website/views/website_views.xml | 19 +++++++++++++++++++ .../website_blog/views/website_blog_views.xml | 6 +++++- addons/website_crm/__manifest__.py | 1 + addons/website_event/__manifest__.py | 2 +- addons/website_hr_recruitment/__manifest__.py | 1 + .../view/website_slides_backend.xml | 6 +++++- 7 files changed, 32 insertions(+), 13 deletions(-) diff --git a/addons/website/views/res_config_views.xml b/addons/website/views/res_config_views.xml index 31d5f4e3ba7..5b05e220b1c 100644 --- a/addons/website/views/res_config_views.xml +++ b/addons/website/views/res_config_views.xml @@ -371,16 +371,6 @@ You can also edit the fields of the Extra Step page in eCommerce checkout."> inline - - Website Dashboard - backend_dashboard - - - - - - diff --git a/addons/website/views/website_views.xml b/addons/website/views/website_views.xml index c52476cc4ef..9fbe8790a72 100644 --- a/addons/website/views/website_views.xml +++ b/addons/website/views/website_views.xml @@ -1,6 +1,12 @@ + + @@ -112,5 +118,18 @@ + + + Dashboard + backend_dashboard + + + + diff --git a/addons/website_blog/views/website_blog_views.xml b/addons/website_blog/views/website_blog_views.xml index c8192c33d44..46e80f1d0e1 100644 --- a/addons/website_blog/views/website_blog_views.xml +++ b/addons/website_blog/views/website_blog_views.xml @@ -1,6 +1,10 @@ - + diff --git a/addons/website_crm/__manifest__.py b/addons/website_crm/__manifest__.py index 53d16e58db9..2e16675dc95 100644 --- a/addons/website_crm/__manifest__.py +++ b/addons/website_crm/__manifest__.py @@ -1,6 +1,7 @@ { 'name': 'Contact Form', 'category': 'Website', + 'sequence': 54, 'website': 'https://www.odoo.com/page/website-builder', 'summary': 'Create Leads From Contact Form', 'version': '2.0', diff --git a/addons/website_event/__manifest__.py b/addons/website_event/__manifest__.py index 6811d586e16..cd5d5ead131 100644 --- a/addons/website_event/__manifest__.py +++ b/addons/website_event/__manifest__.py @@ -3,7 +3,7 @@ { 'name': 'Online Events', 'category': 'Marketing', - 'sequence': 135, + 'sequence': 166, 'summary': 'Publish Events and Manage Online Registrations on your Website', 'website': 'https://www.odoo.com/page/website-builder', 'description': """ diff --git a/addons/website_hr_recruitment/__manifest__.py b/addons/website_hr_recruitment/__manifest__.py index 0d3097d73b7..0e661b173e2 100644 --- a/addons/website_hr_recruitment/__manifest__.py +++ b/addons/website_hr_recruitment/__manifest__.py @@ -4,6 +4,7 @@ { 'name': 'Online Jobs', 'category': 'Website', + 'sequence': 142, 'version': '1.0', 'summary': 'Job Descriptions And Application Forms', 'description': """ diff --git a/addons/website_slides/view/website_slides_backend.xml b/addons/website_slides/view/website_slides_backend.xml index 85fdab556b0..1c0e5f75f36 100644 --- a/addons/website_slides/view/website_slides_backend.xml +++ b/addons/website_slides/view/website_slides_backend.xml @@ -1,7 +1,11 @@ - + From 79597a2b84c7ffab18f2c7b4d9015eac97e45707 Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Fri, 3 Mar 2017 16:30:37 +0100 Subject: [PATCH 3/9] [REF] website: merge Website and Website Admin menu items This commit merge the two website menu items in order to ease user flow inside Odoo. When clicking on the Website item a server action is called that decides to redirect the user either to the dashboard if the user is an admin or a website publisher, either to the website for other people. Overriding this method allows to define a specific behavior. Future commit will also redirect sales people to the website dashboard. Dashboard itself is customized to add a header used to display a website button. It will be used in future commits to add other new stuff to the dashboard. --- addons/website/__manifest__.py | 4 +- addons/website/data/web_planner_data.xml | 2 +- addons/website/data/website_data.xml | 7 -- addons/website/models/website.py | 6 ++ .../website/static/description/icon_admin.png | Bin 21652 -> 0 bytes .../static/src/js/website.backendDashboard.js | 18 +++++ .../src/less/website.backendDashboard.less | 68 ++++++++++++++++++ .../static/src/xml/website.backend.xml | 14 ++++ addons/website/views/res_config_views.xml | 6 +- addons/website/views/website_views.xml | 16 +++-- 10 files changed, 124 insertions(+), 17 deletions(-) delete mode 100644 addons/website/static/description/icon_admin.png diff --git a/addons/website/__manifest__.py b/addons/website/__manifest__.py index aaba5cea675..5e62bf95162 100644 --- a/addons/website/__manifest__.py +++ b/addons/website/__manifest__.py @@ -17,18 +17,16 @@ Odoo Website CMS 'installable': True, 'data': [ 'data/website_data.xml', - 'data/web_planner_data.xml', 'security/website_security.xml', 'security/ir.model.access.csv', - 'views/website_templates.xml', 'views/website_navbar_templates.xml', 'views/snippets.xml', 'views/website_views.xml', 'views/res_config_views.xml', 'views/ir_actions_views.xml', - 'wizard/base_language_install_views.xml', + 'data/web_planner_data.xml', ], 'demo': [ 'data/website_demo.xml', diff --git a/addons/website/data/web_planner_data.xml b/addons/website/data/web_planner_data.xml index 0485cd3aac4..0cb1687dda8 100644 --- a/addons/website/data/web_planner_data.xml +++ b/addons/website/data/web_planner_data.xml @@ -709,7 +709,7 @@ Website Planner - + planner_website self - - Website - - website,static/description/icon.png - - - open diff --git a/addons/website/models/website.py b/addons/website/models/website.py index db918401eed..95a229cda71 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -601,6 +601,12 @@ class Website(models.Model): return urlparse.urljoin(cdn_url, uri) return uri + @api.model + def action_dashboard_redirect(self): + if self.env.user.has_group('base.group_system') or self.env.user.has_group('website.group_website_designer'): + return self.env.ref('website.backend_dashboard').read()[0] + return self.env.ref('website.action_website').read()[0] + class Menu(models.Model): diff --git a/addons/website/static/description/icon_admin.png b/addons/website/static/description/icon_admin.png deleted file mode 100644 index c3c37a0d6451d1c29ede195d59b01046df104bb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21652 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 LTe(=#BH(`krWT!x diff --git a/addons/website/static/src/js/website.backendDashboard.js b/addons/website/static/src/js/website.backendDashboard.js index 5caad853fcf..23e4c74a817 100644 --- a/addons/website/static/src/js/website.backendDashboard.js +++ b/addons/website/static/src/js/website.backendDashboard.js @@ -19,6 +19,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, { template: "website.WebsiteDashboardMain", events: { 'click .js_link_analytics_settings': 'on_link_analytics_settings', + 'click .o_dashboard_action': 'on_dashboard_action_clicked', }, init: function(parent, context) { @@ -217,6 +218,23 @@ var Dashboard = Widget.extend(ControlPanelMixin, { this.update_cp(); }, + on_dashboard_action_clicked: function (ev) { + ev.preventDefault(); + var $action = $(ev.currentTarget); + var additional_context = {}; + if (this.date_range === 'week') { + additional_context = {'search_default_week': true} + } else if (this.date_range === 'month') { + additional_context = {'search_default_month': true} + } else if (this.date_range === 'year') { + additional_context = {'search_default_year': true} + } + this.do_action($action.attr('name'), { + additional_context: additional_context, + on_reverse_breadcrumb: this.on_reverse_breadcrumb + }); + }, + update_cp: function() { var self = this; if (!this.$searchview) { diff --git a/addons/website/static/src/less/website.backendDashboard.less b/addons/website/static/src/less/website.backendDashboard.less index 8c2c4737a3a..1e162f6c43b 100644 --- a/addons/website/static/src/less/website.backendDashboard.less +++ b/addons/website/static/src/less/website.backendDashboard.less @@ -128,6 +128,74 @@ } } + .o_dashboard_common { + .o_box { + display: flex; + flex-flow: row wrap; + justify-content: flex-start; + > .o_box_item { + @media (max-width: @screen-sm-min) { + flex: 1 1 200px; + display: block !important; + } + @media (min-width: @screen-sm-min) { + flex: 0 0 20%; + } + } + } + .o_inner_box { + padding-top: 10px; + text-align: center; + border: 1px solid @odoo-view-background-color; + height: 80px; + overflow: hidden; + text-overflow: ellipsis; + &.o_main { + background-color: @odoo-brand-optional; + &:hover { + background-color: darken(@odoo-brand-optional, 10%); + } + a { + color: white; + } + } + &.o_primary { + background-color: @odoo-brand-primary; + color: white; + &:hover { + background-color: darken(@odoo-brand-primary, 10%); + } + } + &.o_apps { + position: relative; + background-color: @odoo-brand-optional; + color: white; + .o_apps_hidden { + display: none; + &.o_apps_hide_panel { + position: absolute; + top: 0px; + right: 0px; + background: @brand-danger; + padding: 6px; + } + } + &:hover { + background-color: #45818E; + .o_apps_show { + display: none; + } + .o_apps_hidden { + display: block; + } + } + } + .o_highlight { + font-size: 27px; + } + } + } + .o_dashboard_visits { .o_demo_background { diff --git a/addons/website/static/src/xml/website.backend.xml b/addons/website/static/src/xml/website.backend.xml index 413c3d35631..992ffc5d821 100644 --- a/addons/website/static/src/xml/website.backend.xml +++ b/addons/website/static/src/xml/website.backend.xml @@ -8,6 +8,20 @@
+ +
+
+
+ + + diff --git a/addons/website/views/res_config_views.xml b/addons/website/views/res_config_views.xml index 5b05e220b1c..f923fa167a5 100644 --- a/addons/website/views/res_config_views.xml +++ b/addons/website/views/res_config_views.xml @@ -373,6 +373,10 @@ You can also edit the fields of the Extra Step page in eCommerce checkout."> - +
diff --git a/addons/website/views/website_views.xml b/addons/website/views/website_views.xml index 9fbe8790a72..1b9b4c87fa5 100644 --- a/addons/website/views/website_views.xml +++ b/addons/website/views/website_views.xml @@ -2,11 +2,11 @@ - + groups="base.group_user" + web_icon="website,static/description/icon.png"/> @@ -124,11 +124,17 @@ backend_dashboard + + Website: Dashboard + + code + action = model.action_dashboard_redirect() + + From 4d1cef1b1f3c283d54d8e8bd5d58f0da5b109a23 Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Fri, 3 Mar 2017 16:35:11 +0100 Subject: [PATCH 4/9] [IMP] website: improve dashboard header to have a website-related dashboard This header displays icons that proposes to install other website related applications. --- addons/website/controllers/backend.py | 31 +++++++--- .../static/src/js/website.backendDashboard.js | 57 ++++++++++++++----- .../src/less/website.backendDashboard.less | 20 +++---- .../static/src/xml/website.backend.xml | 53 ++++++++++++++--- 4 files changed, 120 insertions(+), 41 deletions(-) diff --git a/addons/website/controllers/backend.py b/addons/website/controllers/backend.py index 6897da70fc3..3c9323c6823 100644 --- a/addons/website/controllers/backend.py +++ b/addons/website/controllers/backend.py @@ -9,16 +9,29 @@ class WebsiteBackend(http.Controller): @http.route('/website/fetch_dashboard_data', type="json", auth='user') def fetch_dashboard_data(self, date_from, date_to): - - params = request.env['ir.config_parameter'] - ga_client_id = params.sudo().get_param('google_management_client_id', default='') - - return { - 'groups': {'system': request.env['res.users'].has_group('base.group_system')}, + has_group_system = request.env['res.users'].has_group('base.group_system') + has_group_designer = request.env['res.users'].has_group('website.group_website_designer') + if has_group_system: + apps_data = dict((app['name'], app) for app in request.env['ir.module.module'].sudo().search_read( + ['|', ('name', 'ilike', 'website'), ('application', '=', True)], + ['id', 'sequence', 'name', 'shortdesc', 'state'], + order='sequence ASC')) + else: + apps_data = {} + dashboard_data = { + 'groups': { + 'system': has_group_system, + 'website_designer': has_group_designer + }, 'currency': request.env.user.company_id.currency_id.id, 'dashboards': { - 'visits': { - 'ga_client_id': ga_client_id, - } + 'apps_data': apps_data, + 'visits': {}, } } + if has_group_designer: + ga_dashboard = request.env['ir.values'].sudo().get_default('website.config.settings', 'has_google_analytics_dashboard') + if ga_dashboard: + ga_client_id = request.env['ir.config_parameter'].sudo().get_param('google_management_client_id', default='') + dashboard_data['dashboards']['visits']['ga_client_id'] = ga_client_id + return dashboard_data diff --git a/addons/website/static/src/js/website.backendDashboard.js b/addons/website/static/src/js/website.backendDashboard.js index 23e4c74a817..3b5ad204ad7 100644 --- a/addons/website/static/src/js/website.backendDashboard.js +++ b/addons/website/static/src/js/website.backendDashboard.js @@ -6,12 +6,14 @@ var ajax = require('web.ajax'); var ControlPanelMixin = require('web.ControlPanelMixin'); var core = require('web.core'); var Dialog = require('web.Dialog'); +var formats = require('web.formats'); var Model = require('web.Model'); var session = require('web.session'); -var utils = require('web.utils'); var web_client = require('web.web_client'); var Widget = require('web.Widget'); +var local_storage = require('web.local_storage'); + var _t = core._t; var QWeb = core.qweb; @@ -19,7 +21,9 @@ var Dashboard = Widget.extend(ControlPanelMixin, { template: "website.WebsiteDashboardMain", events: { 'click .js_link_analytics_settings': 'on_link_analytics_settings', - 'click .o_dashboard_action': 'on_dashboard_action_clicked', + 'click .o_dashboard_action': 'on_dashboard_action', + 'click .o_dashboard_action_form': 'on_dashboard_action_form', + 'click .o_dashboard_hide_panel': 'on_dashboard_hide_panel', }, init: function(parent, context) { @@ -28,6 +32,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, { this.date_range = 'week'; // possible values : 'week', 'month', year' this.date_from = moment().subtract(1, 'week'); this.date_to = moment(); + this.hidden_apps = JSON.parse(local_storage.getItem('website_dashboard_hidden_app_ids') || '[]'); this.dashboards_templates = ['website.dashboard_visits']; this.graphs = []; @@ -107,7 +112,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, { render_dashboards: function() { var self = this; _.each(this.dashboards_templates, function(template) { - self.$('.o_website_dashboard').append(QWeb.render(template, {widget: self})); + self.$('.o_website_dashboard_content').append(QWeb.render(template, {widget: self})); }); }, @@ -140,7 +145,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, { .append("svg"); svg - .attr("height", '20em') + .attr("height", '24em') .datum(chart_values) .call(chart); @@ -206,7 +211,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, { var self = this; $.when(this.fetch_data()).then(function() { - self.$('.o_website_dashboard').empty(); + self.$('.o_website_dashboard_content').empty(); self.render_dashboards(); self.render_graphs(); }); @@ -218,7 +223,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, { this.update_cp(); }, - on_dashboard_action_clicked: function (ev) { + on_dashboard_action: function (ev) { ev.preventDefault(); var $action = $(ev.currentTarget); var additional_context = {}; @@ -235,6 +240,32 @@ var Dashboard = Widget.extend(ControlPanelMixin, { }); }, + on_dashboard_action_form: function (ev) { + ev.preventDefault(); + var $action = $(ev.currentTarget); + this.do_action({ + name: $action.attr('name'), + res_model: $action.data('res_model'), + res_id: $action.data('res_id'), + views: [[false, 'form']], + type: 'ir.actions.act_window', + }, { + on_reverse_breadcrumb: this.on_reverse_breadcrumb + }); + }, + + on_dashboard_hide_panel: function (ev) { + ev.preventDefault(); + ev.stopPropagation(); + var $action = $(ev.currentTarget); + // update hidden module list + this.hidden_apps = JSON.parse(local_storage.getItem('website_dashboard_hidden_app_ids') || '[]'); + this.hidden_apps.push(JSON.parse($action.data('module_id'))); + local_storage.setItem('website_dashboard_hidden_app_ids', JSON.stringify(this.hidden_apps)); + // remove box + $action.closest(".o_box_item").remove(); + }, + update_cp: function() { var self = this; if (!this.$searchview) { @@ -535,24 +566,24 @@ var Dashboard = Widget.extend(ControlPanelMixin, { return i % keep_one_of === 0; }); }, - format_number: function(value, symbol) { - value = utils.human_number(value); - if (symbol === 'currency') { + format_number: function(value, type, digits, symbol) { + if (type === 'currency') { return this.render_monetary_field(value, this.currency_id); } else { - return value + (symbol || ''); + return formats.format_value(value || 0, {type: type, digits: digits}) + ' ' + symbol; } }, render_monetary_field: function(value, currency_id) { var currency = session.get_currency(currency_id); + var formatted_value = formats.format_value(value || 0, {type: "float", digits: currency && currency.digits}); if (currency) { if (currency.position === "after") { - value += currency.symbol; + formatted_value += currency.symbol; } else { - value = currency.symbol + value; + formatted_value = currency.symbol + formatted_value; } } - return value; + return formatted_value; }, }); diff --git a/addons/website/static/src/less/website.backendDashboard.less b/addons/website/static/src/less/website.backendDashboard.less index 1e162f6c43b..83116d07a94 100644 --- a/addons/website/static/src/less/website.backendDashboard.less +++ b/addons/website/static/src/less/website.backendDashboard.less @@ -27,9 +27,9 @@ h4 { margin: 0 0 0 8px; } - h2 { color: @odoo-brand-primary; } + h2, h4 { color: @odoo-brand-primary; } - .col-md-6 { + .col-md-5, .col-md-7 { padding: 15px; .js_field_selection { @@ -80,12 +80,6 @@ .translate(45; 30) !important; } - g.nv-y.nv-axis, g.nv-x.nv-axis, g.nv-y.nv-axis .nv-axisMaxMin { - text { - opacity: .3 !important; - } - } - g.nv-x.nv-axis .tick > line { opacity: 0 !important; } @@ -122,7 +116,7 @@ background-color: @odoo-brand-optional !important; } tr:last-child .legend-color-guide > div { - background-color: darken(@odoo-brand-optional, 10%) !important; + background-color: darken(@odoo-brand-lightsecondary, 10%) !important; } } } @@ -139,7 +133,7 @@ display: block !important; } @media (min-width: @screen-sm-min) { - flex: 0 0 20%; + flex: 0 0 16.6%; } } } @@ -172,7 +166,7 @@ color: white; .o_apps_hidden { display: none; - &.o_apps_hide_panel { + &.o_dashboard_hide_panel { position: absolute; top: 0px; right: 0px; @@ -198,6 +192,10 @@ .o_dashboard_visits { + h2 { + padding: 15px; + } + .o_demo_background { margin-top: 16px; diff --git a/addons/website/static/src/xml/website.backend.xml b/addons/website/static/src/xml/website.backend.xml index 992ffc5d821..a30267af9a8 100644 --- a/addons/website/static/src/xml/website.backend.xml +++ b/addons/website/static/src/xml/website.backend.xml @@ -9,6 +9,7 @@
+
@@ -22,17 +23,54 @@ Go to Website
+ + + + + + + + + + + + +
+
+
+ +
+
+
+ Add this feature +
+ +
+
+
+
-
+

Visits

@@ -40,8 +78,7 @@

There is no data currently available.

- -
You do not have sufficient access rights to configure the Google Analytics account.
+
@@ -60,10 +97,10 @@
-
- - - +
+ + +
From 27357ac566ce9e62e11b0c1c7e631974519eb178 Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Wed, 1 Mar 2017 11:55:37 +0100 Subject: [PATCH 5/9] [REM] website_crm: remove addition in website dashboard Managing CRM should be done in CRM. Diziz da rule (tudududu ... du dudududu). FGI rulez teh word. --- addons/website_crm/__init__.py | 1 - addons/website_crm/controllers/__init__.py | 4 - addons/website_crm/controllers/main.py | 91 ------------------- .../static/src/js/website_crm_backend.js | 64 ------------- .../static/src/xml/website_crm_dashboard.xml | 44 --------- .../views/website_crm_templates.xml | 6 -- 6 files changed, 210 deletions(-) delete mode 100644 addons/website_crm/controllers/__init__.py delete mode 100644 addons/website_crm/controllers/main.py delete mode 100644 addons/website_crm/static/src/js/website_crm_backend.js delete mode 100644 addons/website_crm/static/src/xml/website_crm_dashboard.xml diff --git a/addons/website_crm/__init__.py b/addons/website_crm/__init__.py index ac0011716f6..81fc81d29c9 100644 --- a/addons/website_crm/__init__.py +++ b/addons/website_crm/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import controllers import models diff --git a/addons/website_crm/controllers/__init__.py b/addons/website_crm/controllers/__init__.py deleted file mode 100644 index f48716d3a69..00000000000 --- a/addons/website_crm/controllers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -import main diff --git a/addons/website_crm/controllers/main.py b/addons/website_crm/controllers/main.py deleted file mode 100644 index 0782c760a62..00000000000 --- a/addons/website_crm/controllers/main.py +++ /dev/null @@ -1,91 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -import babel -from datetime import datetime, timedelta - -from odoo import http -from odoo.http import request - -from odoo.addons.website.controllers.backend import WebsiteBackend -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT - - -class WebsiteCrmBackend(WebsiteBackend): - - @http.route() - def fetch_dashboard_data(self, date_from, date_to): - - results = super(WebsiteCrmBackend, self).fetch_dashboard_data(date_from, date_to) - results['groups']['sale_salesman'] = request.env['res.users'].has_group('sales_team.group_sale_salesman') - if not results['groups']['sale_salesman']: - return results - - date_from = datetime.strptime(date_from, DEFAULT_SERVER_DATE_FORMAT) - date_to = datetime.strptime(date_to, DEFAULT_SERVER_DATE_FORMAT) - - lead_domain = [] - - website_utm = request.env['ir.model.data'].xmlid_to_res_id('utm.utm_medium_website') - if website_utm: - lead_domain += [('medium_id', '=', website_utm)] - - lead_ids = request.env['crm.lead'].search( - lead_domain + [ - ('create_date', '>=', date_from.strftime(DEFAULT_SERVER_DATE_FORMAT)), - ('create_date', '<=', date_to.strftime(DEFAULT_SERVER_DATE_FORMAT))], - ) - - leads = [{ - 'create_date': datetime.strptime(lead.create_date, DEFAULT_SERVER_DATETIME_FORMAT).strftime(DEFAULT_SERVER_DATE_FORMAT), - 'campaign_id': lead.campaign_id.name, - 'medium_id': lead.medium_id.name, - 'source_id': lead.source_id.name, - } for lead in lead_ids] - - leads_graph = self._compute_lead_graph(date_from, date_to, lead_domain) - previous_leads_graph = self._compute_lead_graph(date_from - timedelta(days=(date_to - date_from).days), date_from, lead_domain, previous=True) - - results['dashboards']['leads'] = { - 'graph': [ - { - 'values': leads_graph, - 'key': 'Leads', - }, - { - 'values': previous_leads_graph, - 'key': 'Previous Leads', - }, - ], - 'leads': leads, - 'lead_fields': { - 'create_date': 'Create Date', - 'campaign_id': 'Campaign', - 'medium_id': 'Medium', - 'source_id': 'Source', - }, - } - - return results - - def _compute_lead_graph(self, date_from, date_to, lead_domain, previous=False): - - days_between = (date_to - date_from).days - date_list = [(date_from + timedelta(days=x)) for x in range(0, days_between + 1)] - - daily_leads = request.env['crm.lead'].read_group( - domain=lead_domain + [ - ('create_date', '>=', date_from.strftime(DEFAULT_SERVER_DATE_FORMAT)), - ('create_date', '<=', date_to.strftime(DEFAULT_SERVER_DATE_FORMAT))], - fields=['create_date'], - groupby='create_date:day') - - daily_leads_dict = {l['create_date:day']: l['create_date_count'] for l in daily_leads} - - leads_graph = [{ - '0': d.strftime(DEFAULT_SERVER_DATE_FORMAT) if not previous else (d + timedelta(days=days_between)).strftime(DEFAULT_SERVER_DATE_FORMAT), - # Respect read_group format in models.py - '1': daily_leads_dict.get(babel.dates.format_date(d, format='dd MMM yyyy', locale=request.env.context.get('lang', 'en_US')), 0) - } for d in date_list] - - return leads_graph diff --git a/addons/website_crm/static/src/js/website_crm_backend.js b/addons/website_crm/static/src/js/website_crm_backend.js deleted file mode 100644 index 4a8bee4875c..00000000000 --- a/addons/website_crm/static/src/js/website_crm_backend.js +++ /dev/null @@ -1,64 +0,0 @@ -odoo.define('website_crm.backend', function (require) { -"use strict"; - -var core = require('web.core'); -var WebsiteBackend = require('website.backendDashboard'); - -var QWeb = core.qweb; - -WebsiteBackend.include({ - - events: _.defaults({ - 'change .js_field_selection': 'on_field_selection', - }, WebsiteBackend.prototype.events), - - init: function(parent, context) { - this._super(parent, context); - - this.dashboards_templates.push('website_crm.dashboard_leads'); - this.graphs.push({'name': 'leads', 'group': 'sale_salesman'}); - - this.leads_table = []; - this.lead_fields = []; - this.lead_field = ''; - }, - - compute_percentages: function() { - if (!this.dashboards_data.leads.leads || !this.dashboards_data.leads.leads.length) { - return; - } - var leads = this.dashboards_data.leads.leads; - var lead_field_keys = _.pluck(leads, this.lead_field); - var leads_counter = _.groupBy(lead_field_keys); - this.leads_table = $.map(leads_counter, function(e, key) { - return { - 'value': e.length, - 'key': key, - 'perc': Math.round(e.length*100/leads.length) - }; - }); - this.leads_table.sort(function(a, b) { return b.value - a.value; }); - }, - - fetch_data: function() { - var self = this; - return this._super().then(function() { - if (self.dashboards_data.leads) { - var leads = self.dashboards_data.leads.leads; - if (leads && leads.length) { - self.lead_fields = self.dashboards_data.leads.lead_fields; - self.lead_field = Object.keys(self.lead_fields)[0]; - self.compute_percentages(); - } - } - }); - }, - - on_field_selection: function(ev) { - this.lead_field = ev.currentTarget.value; - this.compute_percentages(); - this.$('.js_leads_table').replaceWith(QWeb.render("website_crm.LeadsTable", {'widget': this})); - }, -}); - -}); diff --git a/addons/website_crm/static/src/xml/website_crm_dashboard.xml b/addons/website_crm/static/src/xml/website_crm_dashboard.xml deleted file mode 100644 index 277ac0f4f17..00000000000 --- a/addons/website_crm/static/src/xml/website_crm_dashboard.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - -
-
-
-

Leads Acquisition /

-
-
- -
-

Leads Analysis

-
- There is no recent lead. -
- -
- - - -
-
-
-
- - - - - - - - - - - - - - -
Leads% Leads
%
-
- diff --git a/addons/website_crm/views/website_crm_templates.xml b/addons/website_crm/views/website_crm_templates.xml index 5c7c2807bab..f3c657ed3a3 100644 --- a/addons/website_crm/views/website_crm_templates.xml +++ b/addons/website_crm/views/website_crm_templates.xml @@ -89,10 +89,4 @@ - - From b9d831c66ef15b7531ea6d761330f4abfb403519 Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Wed, 1 Mar 2017 12:34:38 +0100 Subject: [PATCH 6/9] [MOV] website_sale: move and rename files according to guidelines and clean imports There should be no functional change implied by this commit. Only some code move and file renaming to match guidelines. --- addons/website_sale/__manifest__.py | 9 ++- addons/website_sale/controllers/backend.py | 15 ++--- addons/website_sale/views/account_views.xml | 24 ++++++++ addons/website_sale/views/payment.xml | 15 ----- addons/website_sale/views/payment_views.xml | 13 +++++ .../views/product_attribute_views.xml | 33 ----------- .../views/{views.xml => product_views.xml} | 49 ++++++++++++++++ addons/website_sale/views/sale_order.xml | 15 ----- .../{backend.xml => sale_order_views.xml} | 57 +++++-------------- 9 files changed, 109 insertions(+), 121 deletions(-) create mode 100644 addons/website_sale/views/account_views.xml delete mode 100644 addons/website_sale/views/payment.xml create mode 100644 addons/website_sale/views/payment_views.xml delete mode 100644 addons/website_sale/views/product_attribute_views.xml rename addons/website_sale/views/{views.xml => product_views.xml} (79%) delete mode 100644 addons/website_sale/views/sale_order.xml rename addons/website_sale/views/{backend.xml => sale_order_views.xml} (65%) diff --git a/addons/website_sale/__manifest__.py b/addons/website_sale/__manifest__.py index a02e4627ce1..7c4ed12fad5 100644 --- a/addons/website_sale/__manifest__.py +++ b/addons/website_sale/__manifest__.py @@ -16,13 +16,12 @@ Odoo E-Commerce 'security/website_sale.xml', 'data/data.xml', 'data/web_planner_data.xml', - 'views/views.xml', - 'views/backend.xml', + 'views/product_views.xml', + 'views/account_views.xml', + 'views/sale_order_views.xml', 'views/crm_team_views.xml', 'views/templates.xml', - 'views/payment.xml', - 'views/product_attribute_views.xml', - 'views/sale_order.xml', + 'views/payment_views.xml', 'views/snippets.xml', 'views/report_shop_saleorder.xml', 'views/res_config_view.xml', diff --git a/addons/website_sale/controllers/backend.py b/addons/website_sale/controllers/backend.py index 53bd7054e97..a4427df6de7 100644 --- a/addons/website_sale/controllers/backend.py +++ b/addons/website_sale/controllers/backend.py @@ -1,22 +1,19 @@ # -*- coding: utf-8 -*- -from odoo import http -from odoo.http import request -from dateutil.relativedelta import relativedelta -from datetime import datetime, date, timedelta -from math import floor -import time -import operator + import babel +from datetime import datetime, timedelta + +from odoo import http from odoo.addons.website.controllers.backend import WebsiteBackend -from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT +from odoo.http import request +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT class WebsiteSaleBackend(WebsiteBackend): @http.route() def fetch_dashboard_data(self, date_from, date_to): - results = super(WebsiteSaleBackend, self).fetch_dashboard_data(date_from, date_to) results['groups']['sale_salesman'] = request.env['res.users'].has_group('sales_team.group_sale_salesman') if not results['groups']['sale_salesman']: diff --git a/addons/website_sale/views/account_views.xml b/addons/website_sale/views/account_views.xml new file mode 100644 index 00000000000..53954449ae7 --- /dev/null +++ b/addons/website_sale/views/account_views.xml @@ -0,0 +1,24 @@ + + + + Invoices + account.invoice + form + tree,form + [('team_id.website_ids', '!=', False)] + + {'type':'out_invoice'} + + + + + Pricelists + ir.actions.act_window + product.pricelist + form + tree,form + [('website_id', '!=', False)] + + {"default_base":'list_price'} + + \ No newline at end of file diff --git a/addons/website_sale/views/payment.xml b/addons/website_sale/views/payment.xml deleted file mode 100644 index 994ed13adad..00000000000 --- a/addons/website_sale/views/payment.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - payment.transaction.form.website_sale - payment.transaction - - - - - - - - - diff --git a/addons/website_sale/views/payment_views.xml b/addons/website_sale/views/payment_views.xml new file mode 100644 index 00000000000..781c1634e49 --- /dev/null +++ b/addons/website_sale/views/payment_views.xml @@ -0,0 +1,13 @@ + + + + payment.transaction.form.website_sale + payment.transaction + + + + + + + + diff --git a/addons/website_sale/views/product_attribute_views.xml b/addons/website_sale/views/product_attribute_views.xml deleted file mode 100644 index c76d94aaab9..00000000000 --- a/addons/website_sale/views/product_attribute_views.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - product.attribute.line.form.inherit - product.attribute.line - - - - - - - - - - - product.attribute.form.inherit - product.attribute - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/addons/website_sale/views/views.xml b/addons/website_sale/views/product_views.xml similarity index 79% rename from addons/website_sale/views/views.xml rename to addons/website_sale/views/product_views.xml index eab192f3ce6..ac6fe261357 100644 --- a/addons/website_sale/views/views.xml +++ b/addons/website_sale/views/product_views.xml @@ -1,5 +1,54 @@ + + product.template.search.published + product.template + + + + + + + + + + + Products + product.template + form + kanban,tree,form + {'search_default_published': 1, 'default_website_published': True} + + + + product.attribute.line.form.inherit + product.attribute.line + + + + + + + + + + + product.attribute.form.inherit + product.attribute + + + + + + + + + + + + + + product.pricelist.website.form product.pricelist diff --git a/addons/website_sale/views/sale_order.xml b/addons/website_sale/views/sale_order.xml deleted file mode 100644 index 0040ac33fe3..00000000000 --- a/addons/website_sale/views/sale_order.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - sale.order.form.acquirer - sale.order - - - - - - - - - diff --git a/addons/website_sale/views/backend.xml b/addons/website_sale/views/sale_order_views.xml similarity index 65% rename from addons/website_sale/views/backend.xml rename to addons/website_sale/views/sale_order_views.xml index d970213c2a7..2412367a7e7 100644 --- a/addons/website_sale/views/backend.xml +++ b/addons/website_sale/views/sale_order_views.xml @@ -1,26 +1,5 @@ - - - product.template.search.published - product.template - - - - - - - - - - - Products - product.template - form - kanban,tree,form - {'search_default_published': 1, 'default_website_published': True} - - sale.order.ecommerce.search.view sale.order @@ -32,6 +11,18 @@ + + sale.order.form.acquirer + sale.order + + + + + + + + + eCommerce Orders sale.order @@ -62,28 +53,6 @@ You don't have any cancelled order from the website. - - Invoices - account.invoice - form - tree,form - [('team_id.website_ids', '!=', False)] - - {'type':'out_invoice'} - - - - - Pricelists - ir.actions.act_window - product.pricelist - form - tree,form - [('website_id', '!=', False)] - - {"default_base":'list_price'} - - @@ -95,7 +64,7 @@ - + From 4dd5a54110aa54517b79cf03560324c2b128faa5 Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Wed, 1 Mar 2017 14:21:31 +0100 Subject: [PATCH 7/9] [IMP] website_sale: add ecommerce-related sale report views and actions Because we miss them. --- addons/website_sale/__manifest__.py | 1 + .../website_sale/views/sale_report_views.xml | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 addons/website_sale/views/sale_report_views.xml diff --git a/addons/website_sale/__manifest__.py b/addons/website_sale/__manifest__.py index 7c4ed12fad5..b31db20df7a 100644 --- a/addons/website_sale/__manifest__.py +++ b/addons/website_sale/__manifest__.py @@ -18,6 +18,7 @@ Odoo E-Commerce 'data/web_planner_data.xml', 'views/product_views.xml', 'views/account_views.xml', + 'views/sale_report_views.xml', 'views/sale_order_views.xml', 'views/crm_team_views.xml', 'views/templates.xml', diff --git a/addons/website_sale/views/sale_report_views.xml b/addons/website_sale/views/sale_report_views.xml new file mode 100644 index 00000000000..b9c39c34926 --- /dev/null +++ b/addons/website_sale/views/sale_report_views.xml @@ -0,0 +1,98 @@ + + + + sale.report.search + sale.report + + + + + + + + + + + sale.report.view.pivot.website + sale.report + + + + + + + + + + + sale.report.view.graph.website + sale.report + + + + + + + + + + Sales + sale.report + pivot,graph + [('state','in',('sale', 'done')), ('team_id.team_type', '=', 'website')] + + You don't have any order from the website. + + + + + pivot + + + + + + + graph + + + + + + Sales + sale.report + pivot,graph + [('team_id.team_type', '=', 'website')] + + You don't have any order from the website. + + + + + pivot + + + + + + + graph + + + + \ No newline at end of file From f6fc7c23192663c9c72c1593b7c5827fa8369ea1 Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Wed, 1 Mar 2017 16:33:07 +0100 Subject: [PATCH 8/9] [REF] website_sale: improve sales part of website dashboard This commit refactors the sales part of website dashboard. It introduces to main features * header of dashboard now contains buttons related to ecommerce orders and payments. It allows to quickly jump to the related business views; * graph and summary is updated to display more precise data about orders, carts, abandoned carts, orders to invoices and payment status. --- addons/website_sale/controllers/backend.py | 140 ++++++++++++------ addons/website_sale/models/__init__.py | 1 + addons/website_sale/models/website.py | 14 ++ .../img/website_sale_dashboard_sales_demo.png | Bin 0 -> 56319 bytes .../static/src/js/website_sale_backend.js | 2 +- .../src/less/website_sale_dashboard.less | 61 ++++++++ .../static/src/xml/website_sale_dashboard.xml | 139 +++++++++++++---- addons/website_sale/views/payment_views.xml | 15 +- .../website_sale/views/sale_order_views.xml | 107 ++++++++++--- addons/website_sale/views/templates.xml | 1 + 10 files changed, 385 insertions(+), 95 deletions(-) create mode 100644 addons/website_sale/models/website.py create mode 100644 addons/website_sale/static/src/img/website_sale_dashboard_sales_demo.png create mode 100644 addons/website_sale/static/src/less/website_sale_dashboard.less diff --git a/addons/website_sale/controllers/backend.py b/addons/website_sale/controllers/backend.py index a4427df6de7..80cfc690d0e 100644 --- a/addons/website_sale/controllers/backend.py +++ b/addons/website_sale/controllers/backend.py @@ -2,12 +2,11 @@ import babel -from datetime import datetime, timedelta +from datetime import datetime, timedelta, time -from odoo import http +from odoo import fields, http, _ from odoo.addons.website.controllers.backend import WebsiteBackend from odoo.http import request -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT class WebsiteSaleBackend(WebsiteBackend): @@ -15,55 +14,108 @@ class WebsiteSaleBackend(WebsiteBackend): @http.route() def fetch_dashboard_data(self, date_from, date_to): results = super(WebsiteSaleBackend, self).fetch_dashboard_data(date_from, date_to) + + sales_values = dict( + graph=[], + best_sellers=[], + summary=dict( + order_count=0, order_carts_count=0, order_unpaid_count=0, + order_to_invoice_count=0, order_carts_abandonned_count=0, + payment_to_capture_count=0, total_sold=0, + order_per_day_ratio=0, order_sold_ratio=0, order_convertion_pctg=0, + ) + ) + results['dashboards']['sales'] = sales_values + results['groups']['sale_salesman'] = request.env['res.users'].has_group('sales_team.group_sale_salesman') if not results['groups']['sale_salesman']: return results - date_from = datetime.strptime(date_from, DEFAULT_SERVER_DATE_FORMAT) - date_to = datetime.strptime(date_to, DEFAULT_SERVER_DATE_FORMAT) + date_date_from = fields.Date.from_string(date_from) + date_date_to = fields.Date.from_string(date_to) + date_diff_days = (date_date_to - date_date_from).days + datetime_from = datetime.combine(date_date_from, time.min) + datetime_to = datetime.combine(date_date_to, time.max) - # Best seller products - product_lines = request.env['sale.order.line'].read_group( + # Product-based computation + report_product_lines = request.env['sale.report'].read_group( domain=[ ('product_id.website_published', '=', True), - ('order_id.state', 'in', ['sent', 'sale', 'done']), - ('order_id.team_id.website_ids', '!=', False), - ('order_id.date_order', '>=', date_from.strftime(DEFAULT_SERVER_DATE_FORMAT)), - ('order_id.date_order', '<=', date_to.strftime(DEFAULT_SERVER_DATE_FORMAT))], - fields=['product_id', 'product_uom_qty', 'price_total'], + ('team_id.team_type', '=', 'website'), + ('state', 'in', ['sale', 'done']), + ('date', '>=', date_from), + ('date', '<=', date_to)], + fields=['product_id', 'product_uom_qty', 'price_subtotal'], groupby='product_id', orderby='product_uom_qty desc', limit=5) - - best_sellers = [] - for product_line in product_lines: + for product_line in report_product_lines: product_id = request.env['product.product'].browse(product_line['product_id'][0]) - best_sellers.append({ + sales_values['best_sellers'].append({ 'id': product_id.id, 'name': product_id.name, 'qty': product_line['product_uom_qty'], - 'sales': product_line['price_total'], + 'sales': product_line['price_subtotal'], }) - # Graphes computation - sales_domain = [ - ('team_id.website_ids', '!=', False), - ('state', 'in', ['sent', 'sale', 'done']), - ] - sales_graph = self._compute_sale_graph(date_from, date_to, sales_domain) - previous_sales_graph = self._compute_sale_graph(date_from - timedelta(days=(date_to - date_from).days), date_from, sales_domain, previous=True) + # Sale-based results computation + sale_order_domain = [ + ('team_id', 'in', request.env['crm.team'].search([('team_type', '=', 'website')]).ids), + ('date_order', '>=', fields.Datetime.to_string(datetime_from)), + ('date_order', '<=', fields.Datetime.to_string(datetime_to))] + so_group_data = request.env['sale.order'].read_group(sale_order_domain, fields=['state'], groupby='state') + for res in so_group_data: + if res.get('state') == 'sent': + sales_values['summary']['order_unpaid_count'] += res['state_count'] + elif res.get('state') in ['sale', 'done']: + sales_values['summary']['order_count'] += res['state_count'] + sales_values['summary']['order_carts_count'] += res['state_count'] - results['dashboards']['sales'] = { - 'graph': [ - { - 'values': sales_graph, - 'key': 'Sales', - }, - { - 'values': previous_sales_graph, - 'key': 'Previous Sales', - }, - ], - 'best_sellers': best_sellers, - } + sales_values['summary'].update( + order_to_invoice_count=request.env['sale.order'].search_count(sale_order_domain + [ + ('state', 'in', ['sale', 'done']), + ('invoice_status', '=', 'to invoice'), + ]), + order_carts_abandonned_count=request.env['sale.order'].search_count(sale_order_domain + [ + ('state', '=', 'draft'), + ('date_order', '<=', fields.Datetime.to_string(datetime.now() - timedelta(hours=1))), + ]), + payment_to_capture_count=request.env['payment.transaction'].search_count([ + ('state', '=', 'authorized'), + ('sale_order_id.state', '!=', 'cancel') + ]), + total_sold=sum(request.env['sale.report'].search([ + ('state', 'in', ['sale', 'done']), + ('team_id.team_type', '=', 'website'), + ('date', '>=', date_from), + ('date', '<=', date_to)]).mapped('price_subtotal') + ) + ) + + # Ratio computation + sales_values['summary']['order_per_day_ratio'] = round(float(sales_values['summary']['order_count']) / date_diff_days, 2) + sales_values['summary']['order_sold_ratio'] = round(float(sales_values['summary']['total_sold']) / sales_values['summary']['order_count'], 2) if sales_values['summary']['order_count'] else 0 + sales_values['summary']['order_convertion_pctg'] = 100.0 * sales_values['summary']['order_count'] / sales_values['summary']['order_carts_count'] if sales_values['summary']['order_carts_count'] else 0 + + # Graphes computation + if date_diff_days == 7: + previous_sale_label = _('Previous Week') + elif date_diff_days > 7 and date_diff_days <= 31: + previous_sale_label = _('Previous Month') + else: + previous_sale_label = _('Previous Year') + + sales_domain = [ + ('team_id.team_type', '=', 'website'), + ('state', 'in', ['sale', 'done']), + ('date', '>=', date_from), + ('date', '<=', date_to) + ] + sales_values['graph'] += [{ + 'values': self._compute_sale_graph(date_date_from, date_date_to, sales_domain), + 'key': 'Untaxed Total', + }, { + 'values': self._compute_sale_graph(date_date_from - timedelta(days=date_diff_days), date_date_from, sales_domain, previous=True), + 'key': previous_sale_label, + }] return results @@ -72,17 +124,15 @@ class WebsiteSaleBackend(WebsiteBackend): days_between = (date_to - date_from).days date_list = [(date_from + timedelta(days=x)) for x in range(0, days_between + 1)] - daily_sales = request.env['sale.order'].read_group( - domain=sales_domain + [ - ('date_order', '>=', date_from.strftime(DEFAULT_SERVER_DATE_FORMAT)), - ('date_order', '<=', date_to.strftime(DEFAULT_SERVER_DATE_FORMAT))], - fields=['date_order', 'amount_total'], - groupby='date_order:day') + daily_sales = request.env['sale.report'].read_group( + domain=sales_domain, + fields=['date', 'price_subtotal'], + groupby='date:day') - daily_sales_dict = {p['date_order:day']: p['amount_total'] for p in daily_sales} + daily_sales_dict = {p['date:day']: p['price_subtotal'] for p in daily_sales} sales_graph = [{ - '0': d.strftime(DEFAULT_SERVER_DATE_FORMAT) if not previous else (d + timedelta(days=days_between)).strftime(DEFAULT_SERVER_DATE_FORMAT), + '0': fields.Date.to_string(d) if not previous else fields.Date.to_string(d + timedelta(days=days_between)), # Respect read_group format in models.py '1': daily_sales_dict.get(babel.dates.format_date(d, format='dd MMM yyyy', locale=request.env.context.get('lang', 'en_US')), 0) } for d in date_list] diff --git a/addons/website_sale/models/__init__.py b/addons/website_sale/models/__init__.py index a40ad2379a6..2ca6f70be07 100644 --- a/addons/website_sale/models/__init__.py +++ b/addons/website_sale/models/__init__.py @@ -4,3 +4,4 @@ import product import sale_order import crm_team import res_config +import website diff --git a/addons/website_sale/models/website.py b/addons/website_sale/models/website.py new file mode 100644 index 00000000000..8cf1e6b78ae --- /dev/null +++ b/addons/website_sale/models/website.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models + + +class Website(models.Model): + _inherit = 'website' + + @api.model + def action_dashboard_redirect(self): + if self.env.user.has_group('sales_team.group_sale_salesman'): + return self.env.ref('website.backend_dashboard').read()[0] + return super(Website, self).action_dashboard_redirect() diff --git a/addons/website_sale/static/src/img/website_sale_dashboard_sales_demo.png b/addons/website_sale/static/src/img/website_sale_dashboard_sales_demo.png new file mode 100644 index 0000000000000000000000000000000000000000..4338a7223e67124dc580114c71068c431f539ed2 GIT binary patch literal 56319 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"?5|~|lIX62d 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 H2LAsKr#-D3 literal 0 HcmV?d00001 diff --git a/addons/website_sale/static/src/js/website_sale_backend.js b/addons/website_sale/static/src/js/website_sale_backend.js index 45257e3c72f..18819d7835f 100644 --- a/addons/website_sale/static/src/js/website_sale_backend.js +++ b/addons/website_sale/static/src/js/website_sale_backend.js @@ -12,7 +12,7 @@ WebsiteBackend.include({ init: function(parent, context) { this._super(parent, context); - this.dashboards_templates.push('website_sale.dashboard_sales'); + this.dashboards_templates.unshift('website_sale.dashboard_sales'); this.graphs.push({'name': 'sales', 'group': 'sale_salesman'}); }, diff --git a/addons/website_sale/static/src/less/website_sale_dashboard.less b/addons/website_sale/static/src/less/website_sale_dashboard.less new file mode 100644 index 00000000000..5a698a492ec --- /dev/null +++ b/addons/website_sale/static/src/less/website_sale_dashboard.less @@ -0,0 +1,61 @@ +.o_dashboard_sales { + h4 { margin: 3px 0 4px 0 !important; } + .o_demo_background { + margin-top: 16px; + height: 350px; + background-size: 100% !important; + background: url("/website_sale/static/src/img/website_sale_dashboard_sales_demo.png"); + position: relative; + opacity: 0.2; + } + .o_demo_message { + color: @odoo-main-color-muted; + width: 100%; + .o-position-absolute(@left: 0, @top: 12%); + .o-flex-display(); + .o-justify-content(center); + } + .o_link_enable { + @media (min-width: @screen-md-min) { + border-right: 1px solid #ccc; + } + height: initial; + padding: 5px; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + a { + color: darken(@odoo-brand-lightsecondary, 40%); + } + &:hover { + background-color: @odoo-brand-lightsecondary; + } + .o_highlight { + color: @odoo-brand-optional; + font-size: 20px; + font-weight: bold; + } + } + .o_link_disable { + @media (min-width: @screen-md-min) { + border-right: 1px solid #ccc; + } + height: initial; + padding: 5px; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + margin: 10px 0px; + color: darken(@odoo-brand-lightsecondary, 40%); + .o_highlight { + font-size: 20px; + font-weight: bold; + } + } + .o_invisible_border { + border-right: 0px; + } + .o_top_margin { + margin-top: 20px; + } +} \ No newline at end of file diff --git a/addons/website_sale/static/src/xml/website_sale_dashboard.xml b/addons/website_sale/static/src/xml/website_sale_dashboard.xml index 5ee96f22c55..dec7c0087f3 100644 --- a/addons/website_sale/static/src/xml/website_sale_dashboard.xml +++ b/addons/website_sale/static/src/xml/website_sale_dashboard.xml @@ -1,41 +1,128 @@ - + + + + + + + + -
+
-
-

Sales /

-
+
+
+ +

Sales Since Last

+
+

Sales

+
+
+
+ +
- -
- +
+
+ +

Sales Since Last

+
+

Sales

+
+
+
+

There is no recent confirmed order.

+
+
-

Best Sellers

-
- There is no recent online sale. -
- -
- - - - - - - - - - - -
ProductQty SoldAmount
+
+
+
+

AT A GLANCE

+ + + + + + + + + + + + +
+
+
+
+

Best Sellers

+ + + + + + + + + + + +
ProductQuantitySold
+
+
diff --git a/addons/website_sale/views/payment_views.xml b/addons/website_sale/views/payment_views.xml index 781c1634e49..c0b56d12f43 100644 --- a/addons/website_sale/views/payment_views.xml +++ b/addons/website_sale/views/payment_views.xml @@ -1,7 +1,7 @@ - - payment.transaction.form.website_sale + + payment.transaction.view.form.inherit.website_sale payment.transaction @@ -10,4 +10,15 @@ + + + Payments To Capture + payment.transaction + form + tree,form + [('state', '=', 'authorized'), ('sale_order_id.state', '!=', 'cancel')] + {'create': False} + + You don't have any payments to capture from the website. + diff --git a/addons/website_sale/views/sale_order_views.xml b/addons/website_sale/views/sale_order_views.xml index 2412367a7e7..ac2aa118746 100644 --- a/addons/website_sale/views/sale_order_views.xml +++ b/addons/website_sale/views/sale_order_views.xml @@ -6,7 +6,9 @@ - + + + @@ -24,51 +26,114 @@ - eCommerce Orders + Orders sale.order tree,form - [('state', 'in', ('sale', 'done'))] - {'show_sale': True, 'search_default_from_website': 1} + [] + {'show_sale': True, 'search_default_confirm_order': 1, 'search_default_from_website': 1} You don't have any confirmed order from the website. - Unpaid eCommerce Orders + Unpaid Orders sale.order tree,form [('state', '=', 'sent')] - {'show_sale': True, 'search_default_from_website': 1} + {'show_sale': True, 'search_default_from_website': 1, 'create': False} You don't have any unpaid order from the website. - Abandoned eCommerce Orders + Abandoned Carts sale.order tree,form - [('state', '=', 'cancel')] - {'show_sale': True, 'search_default_from_website': 1} + [('state', '=', 'draft'), ('order_line', '!=', False), ('date_order', '<=', (context_today() + datetime.timedelta(hours=-1)).strftime('%Y-%m-%d'))] + {'show_sale': True, 'search_default_from_website': 1, 'create': False} - You don't have any cancelled order from the website. + +

Find here all the abandoned carts, i.e. the carts generated by your website's visitors more than one hour ago and not confirmed.

+

You should send an email to revive them!

- - - + + Orders To Invoice + sale.order + tree,form + [('state', 'in', ('sale', 'done')), ('invoice_status', '=', 'to invoice')] + {'show_sale': True, 'search_default_from_website': 1} + + You don't have any order from the website. + + + + + + + + + + - - - - + + + + + + + - - - + + + + + + - + + + + + diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml index f79950916e6..f2bb05cb49e 100644 --- a/addons/website_sale/views/templates.xml +++ b/addons/website_sale/views/templates.xml @@ -4,6 +4,7 @@ From 856d891b3f0f05c1427b030ab415010dd1442a76 Mon Sep 17 00:00:00 2001 From: Rohan Patel Date: Thu, 2 Mar 2017 16:02:50 +0100 Subject: [PATCH 9/9] [IMP] website_sales: improve demo data --- addons/website_sale/data/demo.xml | 229 +++++++++++++++--------------- 1 file changed, 118 insertions(+), 111 deletions(-) diff --git a/addons/website_sale/data/demo.xml b/addons/website_sale/data/demo.xml index c0ee27a2be9..d583adbd514 100644 --- a/addons/website_sale/data/demo.xml +++ b/addons/website_sale/data/demo.xml @@ -794,20 +794,11 @@ Weight: 31 grams
- Laptop E5023 - + iPad Mini + 1 - 2950.00 - - - - - Mouse, Wireless - - 1 - - 12.50 + 599.0 @@ -820,22 +811,13 @@ Weight: 31 grams - + - Laptop E5023 - - 2 + iPad Retina Display + + 1 - 3150.00 - - - - - Mouse, Wireless - - 2 - - 12.50 + 900 @@ -848,22 +830,13 @@ Weight: 31 grams - + - Laptop E5023 - + iPad Retina Display + 1 - 3300.00 - - - - - Mouse, Wireless - - 2 - - 12.50 + 750 @@ -876,22 +849,13 @@ Weight: 31 grams - + - Laptop E5023 - - 3 + iMac + + 1 - 2750.00 - - - - - Mouse, Wireless - - 3 - - 12.50 + 1199.0 @@ -904,22 +868,13 @@ Weight: 31 grams - + - Laptop E5023 - - 2 + iPad Retina Display + + 3 - 2950.00 - - - - - Mouse, Wireless - - 2 - - 12.50 + 349.0 @@ -932,22 +887,13 @@ Weight: 31 grams - + - Laptop E5023 - - 3 + iMac + + 1 - 3250.00 - - - - - Mouse, Wireless - - 3 - - 12.50 + 1599.00 @@ -960,22 +906,13 @@ Weight: 31 grams - + - Laptop E5023 - - 2 + iMac + + 1 - 2850.00 - - - - - Mouse, Wireless - - 2 - - 12.50 + 1349.00 @@ -988,22 +925,13 @@ Weight: 31 grams - + - Laptop E5023 - - 4 + iMac + + 1 - 3050.00 - - - - - Mouse, Wireless - - 4 - - 12.50 + 1799.00 @@ -1016,7 +944,7 @@ Weight: 31 grams - + Laptop E5023 @@ -1025,7 +953,7 @@ Weight: 31 grams 2950.00 - + Mouse, Wireless @@ -1044,4 +972,83 @@ Weight: 31 grams + + + + + + + + + + + + + + + iPod + + 2 + + 16.50 + + + + + + + + + + + + + + + + Apple Wireless Keyboard + + 1 + + 47.0 + + + + + + + + + + + + + sent + + + + + iMac + + 1 + + 1799.0 + + + + + + + + form + authorized + 1799.0 + + + + + + + +