It was very confusing for the user to distinct account.payment and payment.transaction. From now on, the transactions are technical objects and, in the backend, we only refer to it in log messages (Front end will be adapted in the same fashion later on). They are hidden in debug mode in accounting\configuration\payments as their purpose is now purely technical/log This commit also aims to reduce the gap between the accounting app and the transactions: account.payment objects are created/validated upon completion of transaction. To ease the capture/voiding of pending transactions, the related buttons are now displayed directly on the SO/invoice instead of the transactions. Was task: https://www.odoo.com/web#id=35857&view_type=form&model=project.task&action=333&active_id=967&menu_id=4720 Was PR #24043 [FIX] add domain based on journal to payment tokens Was opw: https://www.odoo.com/web?debug#id=1828206&view_type=form&model=project.task&menu_id=5200
147 lines
6.9 KiB
Python
147 lines
6.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import babel
|
|
|
|
from datetime import datetime, timedelta, time
|
|
|
|
from odoo import fields, http, _
|
|
from odoo.addons.website.controllers.backend import WebsiteBackend
|
|
from odoo.http import request
|
|
|
|
|
|
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_abandoned_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_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)
|
|
|
|
# Product-based computation
|
|
report_product_lines = request.env['sale.report'].read_group(
|
|
domain=[
|
|
('team_id.team_type', '=', 'website'),
|
|
('state', 'in', ['sale', 'done']),
|
|
('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_tmpl_id = request.env['product.template'].browse(product_line['product_tmpl_id'][0])
|
|
sales_values['best_sellers'].append({
|
|
'id': product_tmpl_id.id,
|
|
'name': product_tmpl_id.name,
|
|
'qty': product_line['product_uom_qty'],
|
|
'sales': product_line['price_subtotal'],
|
|
})
|
|
|
|
# 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']
|
|
|
|
report_price_lines = request.env['sale.report'].read_group(
|
|
domain=[
|
|
('team_id.team_type', '=', 'website'),
|
|
('state', 'in', ['sale', 'done']),
|
|
('date', '>=', date_from),
|
|
('date', '<=', date_to)],
|
|
fields=['team_id', 'price_subtotal'],
|
|
groupby=['team_id'],
|
|
)
|
|
sales_values['summary'].update(
|
|
order_to_invoice_count=request.env['sale.order'].search_count(sale_order_domain + [
|
|
('state', 'in', ['sale', 'done']),
|
|
('order_line', '!=', False),
|
|
('partner_id', '!=', request.env.ref('base.public_partner').id),
|
|
('invoice_status', '=', 'to invoice'),
|
|
]),
|
|
order_carts_abandoned_count=request.env['sale.order'].search_count(sale_order_domain + [
|
|
('is_abandoned_cart', '=', True),
|
|
('cart_recovery_email_sent', '=', False)
|
|
]),
|
|
payment_to_capture_count=request.env['payment.transaction'].search_count([
|
|
('state', '=', 'authorized'),
|
|
# that part perform a search on sale.order in order to comply with access rights as tx do not have any
|
|
('sale_order_ids', 'in', request.env['sale.order'].search(sale_order_domain + [('state', '!=', 'cancel')]).ids),
|
|
]),
|
|
total_sold=sum(price_line['price_subtotal'] for price_line in report_price_lines)
|
|
)
|
|
|
|
# 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']),
|
|
('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),
|
|
'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
|
|
|
|
def _compute_sale_graph(self, date_from, date_to, sales_domain, previous=False):
|
|
|
|
days_between = (date_to - date_from).days
|
|
date_list = [(date_from + timedelta(days=x)) for x in range(0, days_between + 1)]
|
|
|
|
daily_sales = request.env['sale.report'].read_group(
|
|
domain=sales_domain,
|
|
fields=['confirmation_date', 'price_subtotal'],
|
|
groupby='confirmation_date:day')
|
|
|
|
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)),
|
|
# 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') or 'en_US'), 0)
|
|
} for d in date_list]
|
|
|
|
return sales_graph
|