[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:
Yannick Tivisse
2017-12-05 13:17:41 +01:00
13 changed files with 225 additions and 216 deletions
+1 -1
View File
@@ -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.")
+1 -1
View File
@@ -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"/>
-8
View File
@@ -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 &lt; vignette_max &amp;&amp; widget.hidden_apps.indexOf(app_data['id']) == -1 &amp;&amp; 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 &amp;&amp; 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 &amp;&amp; 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 &amp;&amp; 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 &amp;&amp; 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>
+12 -13
View File
@@ -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)),
+39 -11
View File
@@ -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">
+20 -2
View File
@@ -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', '&lt;=', (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>