[MERGE] website(_sale): Improve 'todos' in website's dashboard with minor usability
Purpose
=======
Todo buttons are too confusing because it's mixing app suggestions & business todos, etc.
It looks empty when ecommerce is not installed
Specification
=============
[IMP] website(_sale): Improve UX for the website dashboard
There are some usability issues in the website dashboard
1/ Move button 'Go to Website' in the header
2/ Hide the action button bar if there's nothing in it
3/ Align the labels in the graph's legend
4/ Make the legend labels and display more obvious
5/ Modify the graph colors to be more readable
6/ Remove margins around todo buttons
[IMP] website_sale: Fetch confirmed orders grouped by product templates
Sales graph should be based on confirmation_date of sales orders, not the date_order.
Date should be aggregated by product templates, not variants
[IMP] website: Reload website dashboard when coming back from breadcrumb
When clicking on a dashboard element (eg: an invoice) and modifying it doesn't reload the dashboard, which could be confusing.
[IMP] website_sale: Display correct records on dashboard buttons
Display the wanted sales orders when clicking on 'Abandoned Carts' or 'Unpaid Orders'
[IMP] website: Remove the suggested app on the website dashboard
[IMP] sale: Rename 'Order Date' into 'Confirmation Date' in SO tree view
[IMP] sale: Don't copy confirmation date on duplication
This commit is contained in:
@@ -130,7 +130,7 @@ class SaleOrder(models.Model):
|
||||
help="Manually set the expiration date of your quotation (offer), or it will set the date automatically based on the template if online quotation is installed.")
|
||||
is_expired = fields.Boolean(compute='_compute_is_expired', string="Is expired")
|
||||
create_date = fields.Datetime(string='Creation Date', readonly=True, index=True, help="Date on which sales order is created.")
|
||||
confirmation_date = fields.Datetime(string='Confirmation Date', readonly=True, index=True, help="Date on which the sales order is confirmed.", oldname="date_confirm")
|
||||
confirmation_date = fields.Datetime(string='Confirmation Date', readonly=True, index=True, help="Date on which the sales order is confirmed.", oldname="date_confirm", copy=False)
|
||||
user_id = fields.Many2one('res.users', string='Salesperson', index=True, track_visibility='onchange', default=lambda self: self.env.user)
|
||||
partner_id = fields.Many2one('res.partner', string='Customer', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, required=True, change_default=True, index=True, track_visibility='always')
|
||||
partner_invoice_id = fields.Many2one('res.partner', string='Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)], 'sale': [('readonly', False)]}, help="Invoice address for current sales order.")
|
||||
|
||||
@@ -148,7 +148,7 @@
|
||||
<tree string="Sales Orders" decoration-bf="message_needaction==True" decoration-muted="state=='cancel'">
|
||||
<field name="message_needaction" invisible="1"/>
|
||||
<field name="name" string="Order Number"/>
|
||||
<field name="confirmation_date" string="Order Date"/>
|
||||
<field name="confirmation_date" string="Confirmation Date"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="user_id"/>
|
||||
<field name="amount_total" sum="Total Tax Included" widget="monetary"/>
|
||||
|
||||
@@ -11,13 +11,6 @@ class WebsiteBackend(http.Controller):
|
||||
def fetch_dashboard_data(self, date_from, date_to):
|
||||
has_group_system = request.env.user.has_group('base.group_system')
|
||||
has_group_designer = request.env.user.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,
|
||||
@@ -25,7 +18,6 @@ class WebsiteBackend(http.Controller):
|
||||
},
|
||||
'currency': request.env.user.company_id.currency_id.id,
|
||||
'dashboards': {
|
||||
'apps_data': apps_data,
|
||||
'visits': {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ var session = require('web.session');
|
||||
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;
|
||||
|
||||
@@ -29,7 +27,6 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
'click .js_link_analytics_settings': 'on_link_analytics_settings',
|
||||
'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) {
|
||||
@@ -38,9 +35,8 @@ 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.dashboards_templates = ['website.dashboard_header', 'website.dashboard_content'];
|
||||
this.graphs = [];
|
||||
},
|
||||
|
||||
@@ -55,7 +51,6 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
var self = this;
|
||||
return this._super().then(function() {
|
||||
self.update_cp();
|
||||
self.render_dashboards();
|
||||
self.render_graphs();
|
||||
self.$el.parent().addClass('oe_background_grey');
|
||||
});
|
||||
@@ -130,7 +125,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
render_dashboards: function() {
|
||||
var self = this;
|
||||
_.each(this.dashboards_templates, function(template) {
|
||||
self.$('.o_website_dashboard_content').append(QWeb.render(template, {widget: self}));
|
||||
self.$('.o_website_dashboard').append(QWeb.render(template, {widget: self}));
|
||||
});
|
||||
},
|
||||
|
||||
@@ -156,6 +151,14 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
.tickValues(_.map(tick_values, function(d) { return self.getDate(d); }))
|
||||
.rotateLabels(-45);
|
||||
|
||||
chart.interactiveLayer.tooltip.contentGenerator(function(data) {
|
||||
return QWeb.render('website.SalesChartTooltip', {
|
||||
format: field_utils.format,
|
||||
chartData: data,
|
||||
dateRange: self.date_range
|
||||
});
|
||||
});
|
||||
|
||||
chart.yAxis
|
||||
.tickFormat(d3.format('.02f'));
|
||||
|
||||
@@ -176,7 +179,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
var self = this;
|
||||
_.each(this.graphs, function(e) {
|
||||
if (self.groups[e.group]) {
|
||||
self.render_graph('#o_graph_' + e.name, self.dashboards_data[e.name].graph);
|
||||
self.render_graph('.o_graph_' + e.name, self.dashboards_data[e.name].graph);
|
||||
}
|
||||
});
|
||||
this.render_graph_analytics(this.dashboards_data.visits.ga_client_id);
|
||||
@@ -234,7 +237,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
|
||||
var self = this;
|
||||
$.when(this.fetch_data()).then(function() {
|
||||
self.$('.o_website_dashboard_content').empty();
|
||||
self.$('.o_website_dashboard').empty();
|
||||
self.render_dashboards();
|
||||
self.render_graphs();
|
||||
});
|
||||
@@ -242,8 +245,14 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
},
|
||||
|
||||
on_reverse_breadcrumb: function() {
|
||||
var self = this;
|
||||
web_client.do_push_state({});
|
||||
this.update_cp();
|
||||
this.fetch_data().then(function() {
|
||||
self.$('.o_website_dashboard').empty();
|
||||
self.render_dashboards();
|
||||
self.render_graphs();
|
||||
});
|
||||
},
|
||||
|
||||
on_dashboard_action: function (ev) {
|
||||
@@ -277,18 +286,6 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
});
|
||||
},
|
||||
|
||||
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) {
|
||||
@@ -304,6 +301,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
this.update_control_panel({
|
||||
cp_content: {
|
||||
$searchview: this.$searchview,
|
||||
$buttons: QWeb.render("website.GoToButtons"),
|
||||
},
|
||||
breadcrumbs: this.getParent().get_breadcrumbs(),
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
color: @odoo-main-color-muted;
|
||||
background-color: @odoo-view-background-color;
|
||||
background-size: cover;
|
||||
margin-bottom: @odoo-horizontal-padding;
|
||||
margin-top: @odoo-horizontal-padding;
|
||||
position: static;
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.3);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
h4 { margin: 0 0 0 8px; }
|
||||
|
||||
.col-md-6, .col-md-12 {
|
||||
.col-md-7, .col-xs-12 {
|
||||
padding: 15px;
|
||||
|
||||
.js_field_selection {
|
||||
@@ -36,7 +36,11 @@
|
||||
|
||||
table {
|
||||
|
||||
tr:nth-child(even) {
|
||||
tr:first-child {
|
||||
background: white;
|
||||
}
|
||||
|
||||
tr:nth-child(even):not(:hover) {
|
||||
background: #f5f6f7;
|
||||
}
|
||||
|
||||
@@ -53,6 +57,9 @@
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.o_tooltip_key {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------------------------*/
|
||||
@@ -75,23 +82,26 @@
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
[class*="nv-series-"] {
|
||||
stroke-width: 3 !important;
|
||||
fill: @odoo-main-color-muted !important;
|
||||
}
|
||||
|
||||
.nv-groups .nv-point, g circle {
|
||||
fill: @odoo-brand-optional;
|
||||
}
|
||||
|
||||
.nv-groups .nv-point {
|
||||
fill-opacity: 1 !important;
|
||||
stroke-opacity: 1 !important;
|
||||
stroke: @odoo-brand-optional;
|
||||
}
|
||||
|
||||
.nv-groups .nv-point.hover, .nv-group.nv-noninteractive .nv-point.hover {
|
||||
stroke: darken(@odoo-brand-optional, 10%);
|
||||
.nv-groups {
|
||||
.nv-series-0 {
|
||||
stroke-width: 3 !important;
|
||||
.nv-point {
|
||||
stroke: @odoo-brand-optional;
|
||||
stroke-opacity: 1 !important;
|
||||
&.hover {
|
||||
stroke: darken(@odoo-brand-optional, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
.nv-series-1 {
|
||||
.nv-point {
|
||||
stroke: @odoo-main-color-muted !important;
|
||||
stroke-opacity: 1 !important;
|
||||
&.hover {
|
||||
stroke: darken(@odoo-main-color-muted, 10%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +128,7 @@
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: flex-start;
|
||||
> .o_box_item {
|
||||
> .o_inner_box {
|
||||
@media (max-width: @screen-sm-min) {
|
||||
flex: 1 1 200px;
|
||||
display: block !important;
|
||||
@@ -136,15 +146,12 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
&.o_main {
|
||||
background-color: @odoo-brand-optional;
|
||||
cursor: pointer;
|
||||
color: white;
|
||||
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;
|
||||
@@ -152,36 +159,21 @@
|
||||
background-color: darken(@odoo-brand-primary, 10%);
|
||||
}
|
||||
}
|
||||
&.o_apps {
|
||||
position: relative;
|
||||
background-color: @odoo-brand-optional;
|
||||
color: white;
|
||||
.o_apps_hidden {
|
||||
display: none;
|
||||
&.o_dashboard_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_graph_sales {
|
||||
.o_legend0 {
|
||||
background-color: @odoo-brand-optional;
|
||||
}
|
||||
.o_legend1 {
|
||||
background-color: @odoo-main-color-muted;
|
||||
}
|
||||
}
|
||||
|
||||
.o_dashboard_visits {
|
||||
|
||||
h2 {
|
||||
|
||||
@@ -9,76 +9,32 @@
|
||||
<div class="o_dashboards">
|
||||
<div class="container-fluid o_website_dashboard">
|
||||
<t t-call="website.dashboard_header"/>
|
||||
<div class="o_website_dashboard_content"/>
|
||||
<t t-call="website.dashboard_content"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website.dashboard_header">
|
||||
<div class="row o_dashboard_common">
|
||||
<div class="o_box">
|
||||
<a href="#" class="o_box_item o_dashboard_action" name="website.action_website" title="Go to Website">
|
||||
<div class="o_inner_box o_primary">
|
||||
<i class="fa fa-globe fa-3x"></i><br/>
|
||||
Go to Website
|
||||
</div>
|
||||
</a>
|
||||
<t t-if="widget.groups.system">
|
||||
<t t-set="apps" t-value="{
|
||||
'website_crm': {'icon': 'fa-file-text-o', 'help': _('Add a contact form to your Contact Us page.')},
|
||||
'website_sale': {'icon': 'fa-shopping-cart', 'help': _('Sell online to reach new customers.')},
|
||||
'website_blog': {'icon': 'fa-rss-square', 'help': _('Build up a community with an efficient content strategy.')},
|
||||
'website_hr_recruitment': {'icon': 'fa-suitcase', 'help': _('Promote your job announces to attract new talents.')},
|
||||
'website_event': {'icon': 'fa-ticket', 'help': _('Promote your events, manage attendance and sell tickets.')},
|
||||
'im_livechat': {'icon': 'fa-comments-o', 'help': _('Chat with your visitors in real time.')},
|
||||
}"/>
|
||||
</t>
|
||||
<t t-if="! widget.groups.system"><t t-set="apps" t-value="{}"/></t>
|
||||
<t t-set="vignette_max" t-value="6"/>
|
||||
<t t-set="vignette_nbr" t-value="1"/>
|
||||
<t t-foreach="apps" t-as="app">
|
||||
<t t-set="app_data" t-value="widget.dashboards_data.apps_data[app]"/>
|
||||
<t t-set="app_dashboard" t-value="apps[app]"/>
|
||||
<t t-if="vignette_nbr < vignette_max && widget.hidden_apps.indexOf(app_data['id']) == -1 && app_data['state'] == 'uninstalled'">
|
||||
<t t-set="vignette_nbr" t-value="vignette_nbr + 1"/>
|
||||
<a href="#" class="o_box_item o_dashboard_action_form"
|
||||
name="Apps"
|
||||
data-res_model="ir.module.module"
|
||||
t-att-data-res_id="app_data['id']"
|
||||
t-att-title="app_dashboard['help']">
|
||||
<div class="o_inner_box o_apps">
|
||||
<div class="o_apps_show">
|
||||
<i t-attf-class="fa #{app_dashboard['icon']} fa-3x"></i><br/>
|
||||
<t t-esc="app_data['shortdesc']"/>
|
||||
</div>
|
||||
<div class="o_apps_hidden">
|
||||
<i class="fa fa-plus-circle fa-3x"></i><br/>
|
||||
Add this feature
|
||||
</div>
|
||||
<i class="o_apps_hidden o_dashboard_hide_panel fa fa-close" t-att-data-module_id="app_data['id']" title="Remove this suggestion from dashboard"/>
|
||||
</div>
|
||||
</a>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row o_dashboard_common"/>
|
||||
</t>
|
||||
|
||||
<t t-name="website.dashboard_visits">
|
||||
<div class="row o_dashboard_visits" t-if="widget.groups.website_designer">
|
||||
<div class="col-xs-12 o_box">
|
||||
<h2>Visits</h2>
|
||||
<div t-if="widget.dashboards_data.visits && widget.dashboards_data.visits.ga_client_id">
|
||||
<div class="row js_analytics_components"/>
|
||||
<a href="#" class="js_link_analytics_settings">Edit my Analytics Client ID</a>
|
||||
</div>
|
||||
<div t-if="!(widget.dashboards_data.visits && widget.dashboards_data.visits.ga_client_id)" class="col-md-12">
|
||||
<div class="o_demo_background">
|
||||
<div class="o_layer">
|
||||
</div>
|
||||
<div class="o_buttons text-center">
|
||||
<h3>There is no data currently available.</h3>
|
||||
<button class="btn btn-sm btn-primary js_link_analytics_settings center-block mb8">Link my Analytics Account</button>
|
||||
<t t-name="website.dashboard_content">
|
||||
<div class="o_website_dashboard_content">
|
||||
<div class="row o_dashboard_visits" t-if="widget.groups.website_designer">
|
||||
<div class="col-xs-12 o_box">
|
||||
<h2>Visits</h2>
|
||||
<div t-if="widget.dashboards_data.visits && widget.dashboards_data.visits.ga_client_id">
|
||||
<div class="row js_analytics_components"/>
|
||||
<a href="#" class="js_link_analytics_settings">Edit my Analytics Client ID</a>
|
||||
</div>
|
||||
<div t-if="!(widget.dashboards_data.visits && widget.dashboards_data.visits.ga_client_id)" class="col-md-12">
|
||||
<div class="o_demo_background">
|
||||
<div class="o_layer">
|
||||
</div>
|
||||
<div class="o_buttons text-center">
|
||||
<h3>There is no data currently available.</h3>
|
||||
<button class="btn btn-sm btn-primary js_link_analytics_settings center-block mb8">Link my Analytics Account</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,4 +67,31 @@
|
||||
<button class="btn btn-sm btn-default js_date_range" data-date="year">Last Year</button>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website.SalesChartTooltip">
|
||||
<table>
|
||||
<thead>
|
||||
<tr><td colspan="3"><strong><t t-esc="chartData.series[0].key"/></strong></td></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="legend-color-guide"><div class="o_legend0"/></td>
|
||||
<td class="key o_tooltip_key"><t t-esc="format.date(moment(chartData.value))"/></td>
|
||||
<td class="value"><t t-esc="format.float(chartData.series[0].data[1])"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="legend-color-guide"><div class="o_legend1"/></td>
|
||||
<td class="key o_tooltip_key"><t t-esc="format.date(moment(chartData.value).subtract(1, dateRange))"/></td>
|
||||
<td class="value"><t t-esc="format.float(chartData.series[1].data[1])"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
|
||||
<t t-name="website.GoToButtons">
|
||||
<a href="/" class="btn btn-primary" title="Go to Website">
|
||||
Go to Website
|
||||
</a>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -40,18 +40,17 @@ class WebsiteSaleBackend(WebsiteBackend):
|
||||
# Product-based computation
|
||||
report_product_lines = request.env['sale.report'].read_group(
|
||||
domain=[
|
||||
('product_id.website_published', '=', True),
|
||||
('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)
|
||||
('confirmation_date', '>=', date_from),
|
||||
('confirmation_date', '<=', fields.Datetime.now())],
|
||||
fields=['product_tmpl_id', 'product_uom_qty', 'price_subtotal'],
|
||||
groupby='product_tmpl_id', orderby='product_uom_qty desc', limit=5)
|
||||
for product_line in report_product_lines:
|
||||
product_id = request.env['product.product'].browse(product_line['product_id'][0])
|
||||
product_tmpl_id = request.env['product.template'].browse(product_line['product_tmpl_id'][0])
|
||||
sales_values['best_sellers'].append({
|
||||
'id': product_id.id,
|
||||
'name': product_id.name,
|
||||
'id': product_tmpl_id.id,
|
||||
'name': product_tmpl_id.name,
|
||||
'qty': product_line['product_uom_qty'],
|
||||
'sales': product_line['price_subtotal'],
|
||||
})
|
||||
@@ -113,8 +112,8 @@ class WebsiteSaleBackend(WebsiteBackend):
|
||||
sales_domain = [
|
||||
('team_id.team_type', '=', 'website'),
|
||||
('state', 'in', ['sale', 'done']),
|
||||
('date', '>=', date_from),
|
||||
('date', '<=', date_to)
|
||||
('confirmation_date', '>=', date_from),
|
||||
('confirmation_date', '<=', fields.Datetime.now())
|
||||
]
|
||||
sales_values['graph'] += [{
|
||||
'values': self._compute_sale_graph(date_date_from, date_date_to, sales_domain),
|
||||
@@ -133,10 +132,10 @@ class WebsiteSaleBackend(WebsiteBackend):
|
||||
|
||||
daily_sales = request.env['sale.report'].read_group(
|
||||
domain=sales_domain,
|
||||
fields=['date', 'price_subtotal'],
|
||||
groupby='date:day')
|
||||
fields=['confirmation_date', 'price_subtotal'],
|
||||
groupby='confirmation_date:day')
|
||||
|
||||
daily_sales_dict = {p['date:day']: p['price_subtotal'] for p in daily_sales}
|
||||
daily_sales_dict = {p['confirmation_date:day']: p['price_subtotal'] for p in daily_sales}
|
||||
|
||||
sales_graph = [{
|
||||
'0': fields.Date.to_string(d) if not previous else fields.Date.to_string(d + timedelta(days=days_between)),
|
||||
|
||||
@@ -785,6 +785,8 @@ Weight: 31 grams</field>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="team_id" ref="sales_team.salesteam_website_sales"/>
|
||||
<field name="date_order" eval="(datetime.now()-relativedelta(days=7)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="confirmation_date" eval="(datetime.now()-relativedelta(days=7)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="state">sale</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_order_line_1" model="sale.order.line">
|
||||
@@ -804,6 +806,8 @@ Weight: 31 grams</field>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="team_id" ref="sales_team.salesteam_website_sales"/>
|
||||
<field name="date_order" eval="(datetime.now()-relativedelta(days=6)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="confirmation_date" eval="(datetime.now()-relativedelta(days=6)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="state">sale</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_order_line_2" model="sale.order.line">
|
||||
@@ -823,6 +827,8 @@ Weight: 31 grams</field>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="team_id" ref="sales_team.salesteam_website_sales"/>
|
||||
<field name="date_order" eval="(datetime.now()-relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="confirmation_date" eval="(datetime.now()-relativedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="state">sale</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_order_line_3" model="sale.order.line">
|
||||
@@ -842,6 +848,8 @@ Weight: 31 grams</field>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="team_id" ref="sales_team.salesteam_website_sales"/>
|
||||
<field name="date_order" eval="(datetime.now()-relativedelta(days=4)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="confirmation_date" eval="(datetime.now()-relativedelta(days=4)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="state">sale</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_order_line_4" model="sale.order.line">
|
||||
@@ -861,6 +869,8 @@ Weight: 31 grams</field>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="team_id" ref="sales_team.salesteam_website_sales"/>
|
||||
<field name="date_order" eval="(datetime.now()-relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="confirmation_date" eval="(datetime.now()-relativedelta(days=3)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="state">sale</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_order_line_5" model="sale.order.line">
|
||||
@@ -880,6 +890,8 @@ Weight: 31 grams</field>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="team_id" ref="sales_team.salesteam_website_sales"/>
|
||||
<field name="date_order" eval="(datetime.now()-relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="confirmation_date" eval="(datetime.now()-relativedelta(days=2)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="state">sale</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_order_line_6" model="sale.order.line">
|
||||
@@ -899,6 +911,8 @@ Weight: 31 grams</field>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="team_id" ref="sales_team.salesteam_website_sales"/>
|
||||
<field name="date_order" eval="(datetime.now()-relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="confirmation_date" eval="(datetime.now()-relativedelta(days=1)).strftime('%Y-%m-%d %H:%M:%S')"/>
|
||||
<field name="state">sale</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_order_line_7" model="sale.order.line">
|
||||
@@ -918,6 +932,8 @@ Weight: 31 grams</field>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="team_id" ref="sales_team.salesteam_website_sales"/>
|
||||
<field name="date_order" eval="datetime.now()"/>
|
||||
<field name="confirmation_date" eval="datetime.now()"/>
|
||||
<field name="state">sale</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_order_line_8" model="sale.order.line">
|
||||
@@ -957,17 +973,6 @@ Weight: 31 grams</field>
|
||||
<field name="price_unit">12.50</field>
|
||||
</record>
|
||||
|
||||
<!-- Confirm the sales orders -->
|
||||
|
||||
<function model="sale.order" name="action_confirm" eval="[[ref('website_sale_order_1')]]"/>
|
||||
<function model="sale.order" name="action_confirm" eval="[[ref('website_sale_order_2')]]"/>
|
||||
<function model="sale.order" name="action_confirm" eval="[[ref('website_sale_order_3')]]"/>
|
||||
<function model="sale.order" name="action_confirm" eval="[[ref('website_sale_order_4')]]"/>
|
||||
<function model="sale.order" name="action_confirm" eval="[[ref('website_sale_order_5')]]"/>
|
||||
<function model="sale.order" name="action_confirm" eval="[[ref('website_sale_order_6')]]"/>
|
||||
<function model="sale.order" name="action_confirm" eval="[[ref('website_sale_order_7')]]"/>
|
||||
<function model="sale.order" name="action_confirm" eval="[[ref('website_sale_order_8')]]"/>
|
||||
|
||||
<!-- Active Carts -->
|
||||
<record id="website_sale_order_10" model="sale.order">
|
||||
<field name="partner_id" ref="base.res_partner_3"/>
|
||||
@@ -1046,4 +1051,27 @@ Weight: 31 grams</field>
|
||||
<field name="payment_acquirer_id" ref="payment.payment_acquirer_transfer"/>
|
||||
<field name="payment_tx_id" ref="website_payment_transaction_2"/>
|
||||
</record>
|
||||
|
||||
<!-- Order to Invoice -->
|
||||
<record id="website_sale_order_14" model="sale.order">
|
||||
<field name="partner_id" ref="base.res_partner_3"/>
|
||||
<field name="partner_invoice_id" ref="base.res_partner_address_25"/>
|
||||
<field name="partner_shipping_id" ref="base.res_partner_address_25"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="pricelist_id" ref="product.list0"/>
|
||||
<field name="team_id" ref="sales_team.salesteam_website_sales"/>
|
||||
</record>
|
||||
|
||||
<record id="website_sale_order_line_15" model="sale.order.line">
|
||||
<field name="order_id" ref="website_sale_order_14"/>
|
||||
<field name="name">Computer Case</field>
|
||||
<field name="product_id" ref="product.product_product_16"/>
|
||||
<field name="product_uom_qty">1</field>
|
||||
<field name="product_uom" ref="product.product_uom_unit"/>
|
||||
<field name="price_unit">25.0</field>
|
||||
</record>
|
||||
|
||||
<!-- action_confirm for confirmation date -->
|
||||
<function model="sale.order" name="action_confirm" eval="[[ref('website_sale_order_14')]]"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -11,18 +11,17 @@ WebsiteBackend.include({
|
||||
init: function (parent, context) {
|
||||
this._super(parent, context);
|
||||
|
||||
this.dashboards_templates.unshift('website_sale.dashboard_sales');
|
||||
this.graphs.push({'name': 'sales', 'group': 'sale_salesman'});
|
||||
},
|
||||
|
||||
on_product_template: function (ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var product_id = $(ev.currentTarget).data('productId');
|
||||
var product_tmpl_id = $(ev.currentTarget).data('productId');
|
||||
this.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'product.product',
|
||||
res_id: product_id,
|
||||
res_model: 'product.template',
|
||||
res_id: product_tmpl_id,
|
||||
views: [[false, 'form']],
|
||||
target: 'current',
|
||||
}, {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
.o_dashboard_sales {
|
||||
|
||||
h2 {
|
||||
padding: 15px;
|
||||
}
|
||||
h4 { margin: 3px 0 4px 0 !important; }
|
||||
.o_demo_background {
|
||||
margin-top: 16px;
|
||||
|
||||
@@ -1,61 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates>
|
||||
<t t-extend="website.dashboard_header">
|
||||
<t t-jquery="t[t-foreach='apps']" t-operation="before">
|
||||
<div t-if="widget.dashboards_data.sales.summary.order_unpaid_count" class="o_inner_box o_main o_box_item" title="Confirm orders when you get paid." t-name='unpaid_orders_box'>
|
||||
<a href="#" class="o_dashboard_action" name="website_sale.action_unpaid_orders_ecommerce">
|
||||
<t t-jquery="div.o_dashboard_common" t-operation="append">
|
||||
<div class="o_box" t-if="widget.dashboards_data.sales.summary.order_unpaid_count || widget.dashboards_data.sales.summary.order_to_invoice_count || widget.dashboards_data.sales.summary.payment_to_capture_count || widget.dashboards_data.sales.summary.order_carts_abandoned_count">
|
||||
<div t-if="widget.dashboards_data.sales.summary.order_unpaid_count" class="o_inner_box o_dashboard_action" title="Confirm orders when you get paid." name="website_sale.action_unpaid_orders_ecommerce">
|
||||
<div class="o_highlight"><t t-esc="widget.dashboards_data.sales.summary.order_unpaid_count"/></div>
|
||||
Unpaid Orders
|
||||
</a>
|
||||
<t t-set="vignette_nbr" t-value="vignette_nbr + 1"/>
|
||||
</div>
|
||||
<div t-if="widget.dashboards_data.sales.summary.payment_to_capture_count" class="o_inner_box o_main o_box_item" title="Capture order payments when the delivery is completed.">
|
||||
<a href="#" class="o_dashboard_action" name="website_sale.payment_transaction_action_payments_to_capture">
|
||||
</div>
|
||||
<div t-if="widget.dashboards_data.sales.summary.order_to_invoice_count" class="o_inner_box o_dashboard_action" title="Generate an invoice from orders ready for invoicing." name="website_sale.sale_order_action_to_invoice">
|
||||
<div class="o_highlight"><t t-esc="widget.dashboards_data.sales.summary.order_to_invoice_count"/></div>
|
||||
Orders to Invoice
|
||||
</div>
|
||||
<div t-if="widget.dashboards_data.sales.summary.payment_to_capture_count" class="o_inner_box o_dashboard_action" title="Capture order payments when the delivery is completed." name="website_sale.payment_transaction_action_payments_to_capture">
|
||||
<div class="o_highlight"><t t-esc="widget.dashboards_data.sales.summary.payment_to_capture_count"/></div>
|
||||
Payments to Capture
|
||||
</a>
|
||||
<t t-set="vignette_nbr" t-value="vignette_nbr + 1"/>
|
||||
</div>
|
||||
<div t-if="widget.dashboards_data.sales.summary.order_carts_abandoned_count" class="o_inner_box o_main o_box_item" title="Send a recovery email to visitors who haven't completed their order.">
|
||||
<a href="#" class="o_dashboard_action" name="website_sale.action_abandoned_orders_ecommerce">
|
||||
</div>
|
||||
<div t-if="widget.dashboards_data.sales.summary.order_carts_abandoned_count" class="o_inner_box o_dashboard_action" title="Send a recovery email to visitors who haven't completed their order." name="website_sale.action_abandoned_orders_ecommerce">
|
||||
<div class="o_highlight"><t t-esc="widget.dashboards_data.sales.summary.order_carts_abandoned_count"/></div>
|
||||
Abandoned Carts to Recover
|
||||
</a>
|
||||
<t t-set="vignette_nbr" t-value="vignette_nbr + 1"/>
|
||||
Abandoned Carts
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="website_sale.dashboard_sales">
|
||||
<div t-if="widget.groups.sale_salesman" class="row o_dashboard_sales">
|
||||
<div class="col-xs-12 o_box">
|
||||
<div t-if="widget.dashboards_data.sales.summary.order_count">
|
||||
<div class="col-md-6 col-xs-12">
|
||||
<t t-extend="website.dashboard_content">
|
||||
<t t-jquery="div.o_website_dashboard_content" t-operation="prepend">
|
||||
<div t-if="widget.groups.sale_salesman" class="row o_dashboard_sales">
|
||||
<div class="col-xs-12 o_box">
|
||||
<t t-if="widget.dashboards_data.sales.summary.order_count">
|
||||
<t t-if="widget.date_range">
|
||||
<h2>Sales Since Last <t t-esc="widget.date_range"></t></h2>
|
||||
</t>
|
||||
<t t-else=""><h2>Sales</h2></t>
|
||||
<div id="o_graph_sales" class="o_graph" data-type="sales"/>
|
||||
</div>
|
||||
<div class="col-md-6 col-xs-12">
|
||||
<t t-call="website_sale.products_table"/>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="! widget.dashboards_data.sales.summary.order_count">
|
||||
<div class="col-md-12 col-xs-12">
|
||||
<div class="col-md-7 col-xs-12">
|
||||
<div class="o_graph_sales" data-type="sales"/>
|
||||
</div>
|
||||
<div class="col-md-5 col-xs-12">
|
||||
<t t-call="website_sale.products_table"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="! widget.dashboards_data.sales.summary.order_count">
|
||||
<t t-if="widget.date_range">
|
||||
<h2>Sales Since Last <t t-esc="widget.date_range"></t></h2>
|
||||
</t>
|
||||
<t t-else=""><h2>Sales</h2></t>
|
||||
<div class="o_demo_background">
|
||||
<div class="col-md-12 col-xs-12">
|
||||
<div class="o_demo_background">
|
||||
</div>
|
||||
<div class="o_demo_message">
|
||||
<h3>There is no recent confirmed order.</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_demo_message">
|
||||
<h3>There is no recent confirmed order.</h3>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="website_sale.products_table">
|
||||
|
||||
@@ -93,6 +93,24 @@
|
||||
<field name="binding_model_id" ref="sale.model_sale_order"/>
|
||||
</record>
|
||||
|
||||
<record id="action_view_unpaid_quotation_tree" model="ir.actions.act_window">
|
||||
<field name="name">Unpaid Orders</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[('state', '=', 'sent'), ('team_id.team_type', '=', 'website')]</field>
|
||||
<field name="context" eval="{'show_sale': True, 'create': False}"/>
|
||||
<field name="view_id" ref="sale.view_quotation_tree"/>
|
||||
</record>
|
||||
|
||||
<record id="action_view_abandoned_tree" model="ir.actions.act_window">
|
||||
<field name="name">Abandoned Carts</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="domain">[('state', '=', 'draft'), ('order_line', '!=', False), ('partner_id', '!=', context.get('public_partner_id')), ('team_id.team_type', '=', 'website'), ('date_order', '<=', (datetime.datetime.utcnow() - datetime.timedelta(hours=1)).strftime('%Y-%m-%d %H:%M:%S'))]</field>
|
||||
<field name="context" eval="{'show_sale': True, 'create': False, 'public_partner_id': ref('base.public_partner')}"/>
|
||||
<field name="view_id" ref="sale.view_quotation_tree"/>
|
||||
</record>
|
||||
|
||||
<!-- Main website sale menu items -->
|
||||
<menuitem id="menu_orders" name="Orders"
|
||||
parent="website.menu_website_configuration" sequence="2"
|
||||
@@ -117,10 +135,10 @@
|
||||
action="action_orders_ecommerce"
|
||||
parent="menu_orders" sequence="1"/>
|
||||
<menuitem id="menu_orders_unpaid_orders" name="Unpaid Orders"
|
||||
action="action_unpaid_orders_ecommerce"
|
||||
action="action_view_unpaid_quotation_tree"
|
||||
parent="menu_orders" sequence="2"/>
|
||||
<menuitem id="menu_orders_abandoned_orders" name="Abandoned Carts"
|
||||
action="action_abandoned_orders_ecommerce"
|
||||
action="action_view_abandoned_tree"
|
||||
parent="menu_orders" sequence="3"/>
|
||||
<menuitem id="menu_orders_customers" name="Customers"
|
||||
action="base.action_partner_customer_form"
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
<templates>
|
||||
<t t-extend="website.dashboard_header">
|
||||
<t t-jquery=".o_dashboard_common a.o_dashboard_action .o_primary" t-operation="after">
|
||||
<div t-if="widget.dashboards_data.sales.summary.order_to_invoice_count" class="o_inner_box o_main o_box_item" title="Generate an invoice from orders ready for invoicing.">
|
||||
<a href="#" class="o_dashboard_action" name="website_sale.sale_order_action_to_invoice">
|
||||
<div class="o_highlight"><t t-esc="widget.dashboards_data.sales.summary.order_to_invoice_count"/></div>
|
||||
Orders to Invoice
|
||||
</a>
|
||||
<t t-set="vignette_nbr" t-value="vignette_nbr + 1"/>
|
||||
<div t-if="widget.dashboards_data.sales.summary.order_to_invoice_count" class="o_inner_box o_dashboard_action" title="Generate an invoice from orders ready for invoicing." name="website_sale.sale_order_action_to_invoice">
|
||||
<div class="o_highlight"><t t-esc="widget.dashboards_data.sales.summary.order_to_invoice_count"/></div>
|
||||
Orders to Invoice
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
Reference in New Issue
Block a user