[FIX] point_of_sale: restored removed report

The sale of the day report was removed during new api migration as it was
thought, it could be replaced by pivot view.
Due to the lack of some options and the problems to be printed, create similar
report.

This commit adds a report in the PoS interface to be printed on ESC/POS printers
and add a JS wizard to restore it in the backend with date selection.

The wizard is made in a new web widget because adding a wizard requires changes
not compatible with stable version.
Creating a proper wizard should be done in v10

opw-681833
This commit is contained in:
Martin Trigaux
2016-09-08 16:37:01 +02:00
parent ff4549ec6a
commit ba070781b8
11 changed files with 430 additions and 1 deletions
+1
View File
@@ -59,6 +59,7 @@ Main Features
'views/report_statement.xml',
'views/report_usersproduct.xml',
'views/report_userlabel.xml',
'views/report_saledetails.xml',
'views/point_of_sale.xml',
'views/point_of_sale_dashboard.xml',
],
+7
View File
@@ -18,3 +18,10 @@ class PosController(http.Controller):
return werkzeug.utils.redirect('/web#action=point_of_sale.action_client_pos_menu')
pos_sessions.login()
return request.render('point_of_sale.index')
@http.route('/pos/sale_details_report', type='http', auth='user')
def print_sale_details(self, date_start=False, date_stop=False, **kw):
r = request.env['report.point_of_sale.report_saledetails']
pdf = request.env['report'].with_context(date_start=date_start, date_stop=date_stop).get_pdf(r, 'point_of_sale.report_saledetails')
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
return request.make_response(pdf, headers=pdfhttpheaders)
+103 -1
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
from datetime import timedelta
from functools import partial
import psycopg2
@@ -8,7 +9,7 @@ import psycopg2
from odoo import api, fields, models, tools, _
from odoo.tools import float_is_zero
from odoo.exceptions import UserError
from odoo.http import request
import odoo.addons.decimal_precision as dp
_logger = logging.getLogger(__name__)
@@ -733,3 +734,104 @@ class PosOrderLine(models.Model):
def _get_tax_ids_after_fiscal_position(self):
for line in self:
line.tax_ids_after_fiscal_position = line.order_id.fiscal_position_id.map_tax(line.tax_ids)
class ReportSaleDetails(models.AbstractModel):
_name = 'report.point_of_sale.report_saledetails'
@api.model
def get_sale_details(self, date_start=False, date_stop=False, company=False):
""" Serialise the orders of the day information
params: date_start, date_stop string representing the day of order
"""
if not company:
company = self.env.user.company_id
today = fields.Datetime.from_string(fields.Date.context_today(self))
if date_start:
date_start = fields.Datetime.from_string(date_start)
else:
# start by default today 00:00:00
date_start = today
if date_stop:
# set time to 23:59:59
date_stop = fields.Datetime.from_string(date_stop) + timedelta(days=1, seconds=-1)
else:
# stop by default today 23:59:59
date_stop = today + timedelta(days=1, seconds=-1)
# avoid a date_stop smaller than date_start
date_stop = max(date_stop, date_start)
date_start = fields.Datetime.to_string(date_start)
date_stop = fields.Datetime.to_string(date_stop)
orders = self.env['pos.order'].search([('date_order', '>=', date_start), ('date_order', '<=', date_stop),
('state', 'in', ['paid','invoiced','done']),
('company_id', '=', company.id)])
user_currency = self.env.user.company_id.currency_id
total = 0.0
products_sold = {}
taxes = {}
for order in orders:
if user_currency != order.pricelist_id.currency_id:
total += order.pricelist_id.currency_id.compute(order.amount_total, user_currency)
else:
total += order.amount_total
currency = order.session_id.currency_id
for line in order.lines:
key = (line.product_id, line.price_unit, line.discount)
products_sold.setdefault(key, 0.0)
products_sold[key] += line.qty
if line.tax_ids_after_fiscal_position:
line_taxes = line.tax_ids_after_fiscal_position.compute_all(line.price_unit * (1-(line.discount or 0.0)/100.0), currency, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)
for tax in line_taxes['taxes']:
taxes.setdefault(tax['id'], {'name': tax['name'], 'total':0.0})
taxes[tax['id']]['total'] += tax['amount']
st_line_ids = self.env["account.bank.statement.line"].search([('pos_statement_id', 'in', orders.ids)]).ids
if st_line_ids:
self.env.cr.execute("""
SELECT aj.name, sum(amount) total
FROM account_bank_statement_line AS absl,
account_bank_statement AS abs,
account_journal AS aj
WHERE absl.statement_id = abs.id
AND abs.journal_id = aj.id
AND absl.id IN %s
GROUP BY aj.name
""", (tuple(st_line_ids),))
payments = self.env.cr.dictfetchall()
else:
payments = []
return {
'total_paid': user_currency.round(total),
'payments': payments,
'company_name': self.env.user.company_id.name,
'taxes': taxes.values(),
'products': [{
'product_id': product.id,
'product_name': product.name[:20],
'code': product.default_code,
'quantity': qty,
'price_unit': price_unit,
'discount': discount,
'uom': product.uom_id.name
} for (product, price_unit, discount), qty in products_sold.items()]
}
@api.multi
def render_html(self, data=None):
company = request.env.user.company_id
date_start = self.env.context.get('date_start', False)
date_stop = self.env.context.get('date_stop', False)
data = dict(data or {}, date_start=date_start, date_stop=date_stop)
data.update(self.get_sale_details(date_start, date_stop, company))
return self.env['report'].render('point_of_sale.report_saledetails', data)
@@ -406,6 +406,23 @@ var ProxyStatusWidget = StatusWidget.extend({
},
});
/* --------- The Sale Details --------- */
// Generates a report to print the sales of the
// day on a ticket
var SaleDetailsButton = PosBaseWidget.extend({
template: 'SaleDetailsButton',
start: function(){
var self = this;
this.$el.click(function(){
self.pos.proxy.print_sale_details();
});
},
});
/*--------------------------------------*\
| THE CHROME |
\*======================================*/
@@ -662,6 +679,11 @@ var Chrome = PosBaseWidget.extend({
'name': 'order_selector',
'widget': OrderSelectorWidget,
'replace': '.placeholder-OrderSelectorWidget',
},{
'name': 'sale_details',
'widget': SaleDetailsButton,
'append': '.pos-rightheader',
'condition': function(){ return this.pos.config.use_proxy; },
},{
'name': 'proxy_status',
'widget': ProxyStatusWidget,
@@ -769,6 +791,7 @@ return {
HeaderButtonWidget: HeaderButtonWidget,
OrderSelectorWidget: OrderSelectorWidget,
ProxyStatusWidget: ProxyStatusWidget,
SaleDetailsButton: SaleDetailsButton,
StatusWidget: StatusWidget,
SynchNotificationWidget: SynchNotificationWidget,
UsernameWidget: UsernameWidget,
@@ -2,8 +2,10 @@ odoo.define('point_of_sale.devices', function (require) {
"use strict";
var core = require('web.core');
var Model = require('web.DataModel');
var Session = require('web.Session');
var QWeb = core.qweb;
var _t = core._t;
// the JobQueue schedules a sequence of 'jobs'. each job is
@@ -434,6 +436,23 @@ var ProxyDevice = core.Class.extend(core.mixins.PropertiesMixin,{
send_printing_job();
},
print_sale_details: function() {
var self = this;
new Model('report.point_of_sale.report_saledetails').call('get_sale_details').then(function(result){
var env = {
company: self.pos.company,
pos: self.pos,
products: result.products,
payments: result.payments,
taxes: result.taxes,
total_paid: result.total_paid,
date: (new Date()).toLocaleString(),
};
var report = QWeb.render('SaleDetailsReport', env);
self.print_receipt(report);
})
},
// asks the proxy to log some information, as with the debug.log you can provide several arguments.
log: function(){
return this.message('log',{'arguments': _.toArray(arguments)});
@@ -0,0 +1,63 @@
odoo.define('point_of_sale.report', function (require) {
"use strict";
var chat_manager = require('mail.chat_manager');
var composer = require('mail.composer');
var ChatThread = require('mail.ChatThread');
var utils = require('mail.utils');
var config = require('web.config');
var core = require('web.core');
var data = require('web.data');
var Dialog = require('web.Dialog');
var framework = require('web.framework');
var Model = require('web.Model');
var pyeval = require('web.pyeval');
var SearchView = require('web.SearchView');
var ActionManager = require('web.ActionManager');
var ControlPanelMixin = require('web.ControlPanelMixin');
var datepicker = require('web.datepicker');
var Widget = require('web.Widget');
var QWeb = core.qweb;
var _t = core._t;
// TODO replace in v10 with proper wizard
var PosDetailsWidget = Widget.extend(ControlPanelMixin, {
template: 'WizardSaleDetailsReport',
events: {
"click .js_generate": "renderReport",
},
renderElement: function() {
this._super();
var current_date = new moment();
this.dp_start = new datepicker.DateWidget(this);
this.dp_start.insertBefore(this.$el.find('.js_date_start'));
this.dp_start.set_value(current_date.format('YYYY-MM-DD'));
this.dp_stop = new datepicker.DateWidget(this);
this.dp_stop.insertAfter(this.$el.find('.js_date_stop'));
this.dp_stop.set_value(current_date.format('YYYY-MM-DD'));
},
renderReport: function() {
var start = this.dp_start.get_value();
var stop = this.dp_stop.get_value();
var url = '/pos/sale_details_report?date_start='+start+'&date_stop='+stop;
window.open(url, '_blank');
},
});
core.action_registry.add('report_pos_details', PosDetailsWidget);
return PosDetailsWidget;
});
+116
View File
@@ -84,6 +84,14 @@
</div>
</t>
<t t-name="SaleDetailsButton">
<div class="oe_status">
<div class="js_connected oe_icon">
<i class='fa fa-fw fa-print'></i>
</div>
</div>
</t>
<t t-name="HeaderButtonWidget">
<div class="header-button">
<t t-esc="widget.label" />
@@ -829,6 +837,114 @@
</receipt>
</t>
<t t-name="SaleDetailsReport">
<receipt align='center' width='40' value-thousands-separator='' >
<t t-if='pos.company_logo_base64'>
<img t-att-src='pos.company_logo_base64' />
<br/>
</t>
<t t-if='!pos.company_logo_base64'>
<h1><t t-esc='company.name' /></h1>
<br/>
</t>
<br /><br />
<!-- Orderlines -->
<div class='orderlines' line-ratio='0.6'>
<t t-foreach='products' t-as='line'>
<line>
<left><t t-esc='line.product_name' /></left>
<right>
<value value-decimals='2' value-autoint='on'>
<t t-esc='line.quantity' />
</value>
<t t-if='line.uom !== "Unit(s)"'>
<t t-esc='line.uom' />
</t>
</right>
<right>
<value><t t-esc='line.price_unit' /></value>
</right>
</line>
<t t-if='line.discount !== 0'>
<line indent='1'><left>Discount: <t t-esc='line.discount' />%</left></line>
</t>
</t>
</div>
<br/>
<line><left>------------------------</left></line>
<br/>
<line>
<left>Payments:</left>
</line>
<line t-foreach='payments' t-as='payment'>
<left><t t-esc='payment.name' /></left>
<right>
<t t-esc='payment.total'/>
</right>
</line>
<br/>
<line><left>------------------------</left></line>
<br/>
<line>
<left>Taxes:</left>
</line>
<line t-foreach='taxes' t-as='taxe'>
<left><t t-esc='taxe.name' /></left>
<right>
<t t-esc='taxe.total'/>
</right>
</line>
<br/>
<line><left>------------------------</left></line>
<br/>
<line>
<left>Total:</left>
<right>
<t t-esc='total_paid'/>
</right>
</line>
<br/>
<div font='b'>
<div><t t-esc='date' /></div>
</div>
</receipt>
</t>
<t t-name="WizardSaleDetailsReport">
<div class="o_form_view"><div class="oe_form_sheetbg o_form_sheet_bg"><div class="oe_form_sheet oe_form_sheet_width o_form_sheet">
<div class="pos_details_dates">
<h3>Select Dates for the report</h3>
<table class="table-condensed">
<tr>
<td><strong>Start date</strong></td>
<td><div class="js_date_start"></div></td>
</tr>
<tr>
<td><strong>Stop date</strong></td>
<td><div class="js_date_stop"></div></td>
</tr>
</table>
<p>
<button class="js_generate">Generate report</button>
</p>
</div>
</div></div></div>
</t>
<t t-name="PopupWidget">
<div class="modal-dialog">
<div class="popup popup-alert">
@@ -17,5 +17,11 @@
<field name="report_name">point_of_sale.report_invoice</field>
</record>
<record id="sale_details_report" model="ir.actions.report.xml">
<field name="name">Sale Details</field>
<field name="model">report.point_of_sale.details</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">point_of_sale.report_saledetails</field>
</record>
</odoo>
@@ -3,6 +3,8 @@
<template id="assets_backend" name="hr assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<link rel="stylesheet" href="/point_of_sale/static/src/less/pos_dashboard.less"/>
<script type="text/javascript" src="/point_of_sale/static/src/js/report.js"></script>
</xpath>
</template>
</odoo>
@@ -68,5 +68,11 @@
<field name="context">{'group_by_no_leaf':1,'group_by':[]}</field>
</record>
<record id="action_report_pos_details" model="ir.actions.client">
<field name="name">Sale Details</field>
<field name="tag">report_pos_details</field>
</record>
<menuitem id="menu_report_pos_order_all" name="Orders" action="action_report_pos_order_all" parent="menu_point_rep" sequence="3"/>
<menuitem id="menu_report_order_details" name="Sale Details" action="action_report_pos_details" parent="menu_point_rep" sequence="4"/>
</odoo>
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<template id="report_saledetails">
<t t-call="report.html_container">
<t t-call="report.internal_layout">
<div class="page">
<div class="text-center">
<h2>Sale Details</h2>
<strong><t t-esc="date_start"/> - <t t-esc="date_stop"/></strong>
</div>
<!-- Orderlines -->
<h3>Products</h3>
<table class="table table-condensed">
<thead><tr>
<th>Product</th>
<th>Quantity</th>
<th>Price Unit</th>
</tr></thead>
<tbody>
<tr t-foreach='products' t-as='line'>
<td><t t-esc="line['product_name']" /></td>
<td>
<t t-esc="line['quantity']" />
<t t-if='line["uom"] != "Unit(s)"'>
<t t-esc='line["uom"]' />
</t>
</td>
<td>
<t t-esc='line["price_unit"]' />
<t t-if='line["discount"] != 0'>
Disc: <t t-esc='line["discount"]' />%
</t>
</td>
</tr>
</tbody>
</table>
<br/>
<h3>Payments</h3>
<table class="table table-condensed">
<thead><tr>
<th>Name</th>
<th>Total</th>
</tr></thead>
<tbody>
<tr t-foreach='payments' t-as='payment'>
<td><t t-esc="payment['name']" /></td>
<td><t t-esc="payment['total']" /></td>
</tr>
</tbody>
</table>
<br/>
<h3>Taxes</h3>
<table class="table table-condensed">
<thead><tr>
<th>Name</th>
<th>Total</th>
</tr></thead>
<tbody>
<tr t-foreach='taxes' t-as='tax'>
<td><t t-esc="tax['name']" /></td>
<td><t t-esc="tax['total']" /></td>
</tr>
</tbody>
</table>
<br/>
<br/>
<strong>Total: <t t-esc='total_paid'/></strong>
</div>
</t>
</t>
</template>
</data>
</openerp>