[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.
This commit is contained in:
committed by
Thibault Delavallée
parent
4dd5a54110
commit
f6fc7c2319
@@ -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]
|
||||
|
||||
@@ -4,3 +4,4 @@ import product
|
||||
import sale_order
|
||||
import crm_team
|
||||
import res_config
|
||||
import website
|
||||
|
||||
@@ -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()
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 55 KiB |
@@ -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'});
|
||||
},
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +1,128 @@
|
||||
<?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.">
|
||||
<a href="#" class="o_dashboard_action" 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.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>
|
||||
<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 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_abandonned_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_abandonned_orders_ecommerce">
|
||||
<div class="o_highlight"><t t-esc="widget.dashboards_data.sales.summary.order_carts_abandonned_count"/></div>
|
||||
Abandoned Carts
|
||||
</a>
|
||||
<t t-set="vignette_nbr" t-value="vignette_nbr + 1"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="website_sale.dashboard_sales">
|
||||
<div t-if="widget.dashboards_data.sales" class="row o_dashboard_sales">
|
||||
<div t-if="widget.groups.sale_salesman" class="row o_dashboard_sales">
|
||||
<div class="col-xs-12 o_box">
|
||||
<div class="col-md-6 col-xs-12">
|
||||
<h2>Sales / <t t-esc="widget.date_range"></t></h2>
|
||||
<div id="o_graph_sales" class="o_graph" data-type="sales"/>
|
||||
<div t-if="widget.dashboards_data.sales.summary.order_count">
|
||||
<div class="col-md-7 col-xs-12">
|
||||
<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-5 col-xs-12">
|
||||
<t t-call="website_sale.products_table"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-xs-12">
|
||||
<t t-call="website_sale.products_table"/>
|
||||
<div t-if="! widget.dashboards_data.sales.summary.order_count">
|
||||
<div class="col-md-12 col-xs-12">
|
||||
<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>
|
||||
<div class="o_demo_message">
|
||||
<h3>There is no recent confirmed order.</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website_sale.products_table">
|
||||
<h4>Best Sellers</h4>
|
||||
<div t-if="!widget.dashboards_data.sales.best_sellers || !widget.dashboards_data.sales.best_sellers.length">
|
||||
<i>There is no recent online sale.</i>
|
||||
</div>
|
||||
|
||||
<div t-if="widget.dashboards_data.sales.best_sellers && widget.dashboards_data.sales.best_sellers.length" class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>Qty Sold</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
<tr class="o_product_template" t-foreach="widget.dashboards_data.sales.best_sellers" t-as="product" t-att-data-product-id="product.id">
|
||||
<td><t t-esc="product.name"/></td>
|
||||
<td><t t-esc="product.qty"/></td>
|
||||
<td><t t-esc="widget.format_number(product.sales, 'currency')"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-xs-12">
|
||||
<div class="row">
|
||||
<h4>AT A GLANCE</h4>
|
||||
<a href="#" class="o_dashboard_action" name="website_sale.sale_report_action_dashboard">
|
||||
<div class="col-sm-4 o_link_enable" title="Orders">
|
||||
<div class="o_highlight">
|
||||
<t t-esc="widget.dashboards_data.sales.summary.order_count"/>
|
||||
</div>
|
||||
Orders
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" class="o_dashboard_action" name="website_sale.sale_report_action_dashboard">
|
||||
<div class="col-sm-4 o_link_enable" title="Untaxed Total Sold">
|
||||
<div class="o_highlight">
|
||||
<t t-esc="widget.render_monetary_field(widget.dashboards_data.sales.summary.total_sold, widget.data.currency)"/>
|
||||
</div>
|
||||
Sold
|
||||
</div>
|
||||
</a>
|
||||
<a href="#" class="o_dashboard_action" name="website_sale.sale_report_action_carts">
|
||||
<div class="col-sm-4 o_link_enable o_invisible_border" title="Carts">
|
||||
<div class="o_highlight"><t t-esc="widget.dashboards_data.sales.summary.order_carts_count"/></div>
|
||||
Carts
|
||||
</div>
|
||||
</a>
|
||||
<div class="col-sm-4 o_link_disable" title="Orders/Day">
|
||||
<div class="o_highlight"><t t-esc="widget.dashboards_data.sales.summary.order_per_day_ratio"/></div>
|
||||
Orders/Day
|
||||
</div>
|
||||
<div class="col-sm-4 o_link_disable" title="Average Order">
|
||||
<div class="o_highlight"><t t-esc="widget.render_monetary_field(widget.dashboards_data.sales.summary.order_sold_ratio, widget.data.currency)"/></div>
|
||||
Average Order
|
||||
</div>
|
||||
<div class="col-sm-4 o_link_disable o_invisible_border" title="Conversion">
|
||||
<div class="o_highlight"><t t-esc="widget.format_number(widget.dashboards_data.sales.summary.order_convertion_pctg, 'float', [3, 2], '%')"/></div>
|
||||
Conversion
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12 col-xs-12 o_top_margin">
|
||||
<div class="row">
|
||||
<h4>Best Sellers</h4>
|
||||
<table class="table table-responsive table-hover">
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>Quantity</th>
|
||||
<th>Sold</th>
|
||||
</tr>
|
||||
<tr class="o_product_template" t-foreach="widget.dashboards_data.sales.best_sellers" t-as="product" t-att-data-product-id="product.id">
|
||||
<td><t t-esc="product.name"/></td>
|
||||
<td><t t-esc="product.qty"/></td>
|
||||
<td><t t-esc="widget.render_monetary_field(product.sales, widget.data.currency)"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="transaction_form_website_sale" model="ir.ui.view">
|
||||
<field name="name">payment.transaction.form.website_sale</field>
|
||||
<record id="payment_transaction_view_form_inherit_website_sale" model="ir.ui.view">
|
||||
<field name="name">payment.transaction.view.form.inherit.website_sale</field>
|
||||
<field name="model">payment.transaction</field>
|
||||
<field name="inherit_id" ref="payment.transaction_form"/>
|
||||
<field name="arch" type="xml">
|
||||
@@ -10,4 +10,15 @@
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="payment_transaction_action_payments_to_capture" model="ir.actions.act_window">
|
||||
<field name="name">Payments To Capture</field>
|
||||
<field name="res_model">payment.transaction</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('state', '=', 'authorized'), ('sale_order_id.state', '!=', 'cancel')]</field>
|
||||
<field name="context">{'create': False}</field>
|
||||
<field name="view_id" ref="payment.transaction"/>
|
||||
<field name="help">You don't have any payments to capture from the website.</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
<field name="inherit_id" ref="sale.view_sales_order_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<search>
|
||||
<filter string="From Website" name="from_website" domain="[('team_id.website_ids', '!=', False)]"/>
|
||||
<filter string="Confirmed Orders" name="order_confirmed" domain="[('state', 'in', ('sale', 'done'))]"/>
|
||||
<separator/>
|
||||
<filter string="From Website" name="from_website" domain="[('team_id.team_type', '=', 'website')]"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
@@ -24,51 +26,114 @@
|
||||
</record>
|
||||
|
||||
<record id="action_orders_ecommerce" model="ir.actions.act_window">
|
||||
<field name="name">eCommerce Orders</field>
|
||||
<field name="name">Orders</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('state', 'in', ('sale', 'done'))]</field>
|
||||
<field name="context">{'show_sale': True, 'search_default_from_website': 1}</field>
|
||||
<field name="domain">[]</field>
|
||||
<field name="context">{'show_sale': True, 'search_default_confirm_order': 1, 'search_default_from_website': 1}</field>
|
||||
<field name="search_view_id" ref="view_sales_order_filter_ecommerce"/>
|
||||
<field name="help">You don't have any confirmed order from the website.</field>
|
||||
</record>
|
||||
|
||||
<record id="action_unpaid_orders_ecommerce" model="ir.actions.act_window">
|
||||
<field name="name">Unpaid eCommerce Orders</field>
|
||||
<field name="name">Unpaid Orders</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('state', '=', 'sent')]</field>
|
||||
<field name="context">{'show_sale': True, 'search_default_from_website': 1}</field>
|
||||
<field name="context">{'show_sale': True, 'search_default_from_website': 1, 'create': False}</field>
|
||||
<field name="search_view_id" ref="view_sales_order_filter_ecommerce"/>
|
||||
<field name="help">You don't have any unpaid order from the website.</field>
|
||||
</record>
|
||||
|
||||
<record id="action_abandonned_orders_ecommerce" model="ir.actions.act_window">
|
||||
<field name="name">Abandoned eCommerce Orders</field>
|
||||
<field name="name">Abandoned Carts</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('state', '=', 'cancel')]</field>
|
||||
<field name="context">{'show_sale': True, 'search_default_from_website': 1}</field>
|
||||
<field name="domain">[('state', '=', 'draft'), ('order_line', '!=', False), ('date_order', '<=', (context_today() + datetime.timedelta(hours=-1)).strftime('%Y-%m-%d'))]</field>
|
||||
<field name="context">{'show_sale': True, 'search_default_from_website': 1, 'create': False}</field>
|
||||
<field name="search_view_id" ref="view_sales_order_filter_ecommerce"/>
|
||||
<field name="help">You don't have any cancelled order from the website.</field>
|
||||
<field name="help" type="xml">
|
||||
<p>Find here all the abandoned carts, i.e. the carts generated by your website's visitors more than one hour ago and not confirmed.</p>
|
||||
<p>You should send an email to revive them!</p></field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_orders" name="Orders" parent="website.menu_website_configuration" sequence="2"/>
|
||||
<menuitem id="menu_catalog" name="Catalog" parent="website.menu_website_configuration" sequence="3"/>
|
||||
<menuitem id="menu_ecommerce_settings" parent="website.menu_website_global_configuration" name="eCommerce"/>
|
||||
<record id="sale_order_action_to_invoice" model="ir.actions.act_window">
|
||||
<field name="name">Orders To Invoice</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('state', 'in', ('sale', 'done')), ('invoice_status', '=', 'to invoice')]</field>
|
||||
<field name="context">{'show_sale': True, 'search_default_from_website': 1}</field>
|
||||
<field name="search_view_id" ref="view_sales_order_filter_ecommerce"/>
|
||||
<field name="help">You don't have any order from the website.</field>
|
||||
</record>
|
||||
|
||||
<!-- Main website sale menu items -->
|
||||
<menuitem id="menu_orders" name="Orders"
|
||||
parent="website.menu_website_configuration" sequence="2"
|
||||
groups="sales_team.group_sale_salesman"/>
|
||||
<menuitem id="menu_catalog" name="Catalog"
|
||||
parent="website.menu_website_configuration" sequence="3"
|
||||
groups="sales_team.group_sale_salesman"/>
|
||||
<menuitem id="menu_reporting" name="Reporting"
|
||||
parent="website.menu_website_configuration" sequence="99"
|
||||
groups="sales_team.group_sale_manager"/>
|
||||
<menuitem id="website.menu_website_global_configuration" name="Configuration"
|
||||
parent="website.menu_website_configuration" sequence="100"
|
||||
groups="base.group_system,sales_team.group_sale_manager"/>
|
||||
|
||||
<menuitem id="menu_ecommerce_settings" name="eCommerce"
|
||||
parent="website.menu_website_global_configuration"/>
|
||||
<menuitem id="menu_product_settings" name="Products"
|
||||
parent="website.menu_website_global_configuration"/>
|
||||
|
||||
<!-- Orders sub-menus -->
|
||||
<menuitem id="menu_orders_orders" name="Orders" parent="menu_orders" action="action_orders_ecommerce" sequence="1"/>
|
||||
<menuitem id="menu_orders_unpaid_orders" name="Unpaid Orders" parent="menu_orders" action="action_unpaid_orders_ecommerce" sequence="2"/>
|
||||
<menuitem id="menu_orders_abandonned_orders" name="Abandoned Orders" parent="menu_orders" action="action_abandonned_orders_ecommerce" sequence="3"/>
|
||||
<menuitem id="menu_orders_invoices" name="Invoices" parent="menu_orders" action="action_invoices_ecommerce" sequence="4"/>
|
||||
<menuitem id="menu_orders_orders" name="Orders"
|
||||
action="action_orders_ecommerce"
|
||||
parent="menu_orders" sequence="1"/>
|
||||
<menuitem id="menu_orders_unpaid_orders" name="Unpaid Orders"
|
||||
action="action_unpaid_orders_ecommerce"
|
||||
parent="menu_orders" sequence="2"/>
|
||||
<menuitem id="menu_orders_abandonned_orders" name="Abandoned Carts"
|
||||
action="action_abandonned_orders_ecommerce"
|
||||
parent="menu_orders" sequence="3"/>
|
||||
<menuitem id="menu_orders_customers" name="Customers"
|
||||
action="base.action_partner_customer_form"
|
||||
parent="menu_orders" sequence="4"/>
|
||||
|
||||
|
||||
<!-- <menuitem id="menu_orders_invoices" name="Invoices" parent="menu_orders" action="action_invoices_ecommerce" sequence="4"/> -->
|
||||
|
||||
<!-- Catalog sub-menus -->
|
||||
<menuitem id="menu_catalog_products" name="Products" parent="menu_catalog" action="product_template_action_website" sequence="1"/>
|
||||
<menuitem id="menu_catalog_pricelists" name="Pricelists" parent="menu_catalog" action="product.product_pricelist_action2" groups="product.group_sale_pricelist" sequence="3"/>
|
||||
<menuitem id="menu_catalog_categories" parent="menu_ecommerce_settings" action="product_public_category_action" sequence="2"/>
|
||||
<menuitem id="menu_catalog_products" name="Products"
|
||||
action="product_template_action_website"
|
||||
parent="menu_catalog" sequence="1"/>
|
||||
<menuitem id="product_catalog_variants" name="Product Variants"
|
||||
action="product.product_normal_action"
|
||||
parent="menu_catalog" groups="product.group_product_variant" sequence="2"/>
|
||||
<menuitem id="menu_catalog_pricelists" name="Pricelists"
|
||||
action="product.product_pricelist_action2"
|
||||
parent="menu_catalog" groups="product.group_sale_pricelist" sequence="3"/>
|
||||
|
||||
<!-- Reporting sub-menus -->
|
||||
<menuitem id="menu_report_sales" name="Sales"
|
||||
action="sale_report_action_dashboard"
|
||||
parent="menu_reporting" sequence="1"/>
|
||||
|
||||
<!-- Configuration sub-menus -->
|
||||
<menuitem id="menu_ecommerce_payment_acquirers" parent="menu_ecommerce_settings" name="Payment Acquirers" action="payment.action_payment_acquirer"/>
|
||||
<menuitem id="menu_ecommerce_payment_acquirers"
|
||||
action="payment.action_payment_acquirer"
|
||||
parent="menu_ecommerce_settings" name="Payment Methods" />
|
||||
<menuitem id="menu_product_attribute_action"
|
||||
action="product.attribute_action"
|
||||
parent="menu_product_settings" groups="product.group_product_variant" sequence="1"/>
|
||||
<menuitem id="menu_catalog_variants_action"
|
||||
action="product.variants_action"
|
||||
parent="menu_product_settings" groups="product.group_product_variant" sequence="2"/>
|
||||
<menuitem id="menu_catalog_categories" name="Website Categories"
|
||||
action="product_public_category_action"
|
||||
parent="menu_product_settings" sequence="3"/>
|
||||
<menuitem id="menu_internal_product_categories" name="Internal Categories"
|
||||
action="product.product_category_action_form"
|
||||
parent="menu_product_settings" sequence="4"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<template id="assets_backend" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale_backend.js"></script>
|
||||
<link rel="stylesheet" href="/website_sale/static/src/less/website_sale_dashboard.less"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user