[IMP] point_of_sale: ESC/POS receipt printing

bzr revid: fva@openerp.com-20131220163057-9nu1u7hti0m8wo3h
This commit is contained in:
Frédéric van der Essen 2013-12-20 17:30:57 +01:00
parent 2fec901cb5
commit 293001c826
8 changed files with 232 additions and 2 deletions

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import controllers
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'ESC/POS Hardware Driver',
'version': '1.0',
'category': 'Hardware Drivers',
'sequence': 6,
'summary': 'Hardware Driver for ESC/POS Printers and Cashdrawers',
'description': """
ESC/POS Hardware Driver
=======================
This module allows openerp to print with ESC/POS compatible printers and
to open ESC/POS controlled cashdrawers in the point of sale and other modules
that would need such functionality.
""",
'author': 'OpenERP SA',
'depends': [],
'test': [
],
'installable': True,
'auto_install': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,3 @@
import main
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
import logging
import simplejson
import os
import openerp
import time
import random
import openerp.addons.hw_proxy.controllers.main as hw_proxy
import subprocess
import usb.core
import escpos
import escpos.printer
from openerp import http
from openerp.http import request
from openerp.addons.web.controllers.main import manifest_list, module_boot, html_template
_logger = logging.getLogger(__name__)
class EscposDriver(hw_proxy.Proxy):
supported_printers = [
{ 'vendor' : 0x04b8, 'product' : 0x0e03, 'name' : 'Epson TM-T20' }
]
@http.route('/hw_proxy/open_cashbox', type='json', auth='admin')
def open_cashbox(self):
print 'ESC/POS: OPEN CASHBOX'
@http.route('/hw_proxy/print_receipt', type='json', auth='admin')
def print_receipt(self, receipt):
print 'ESC/POS: PRINT RECEIPT ' + str(receipt)
printers = self.connected_usb_devices(self.supported_printers)
def check(string):
return string != True and bool(string) and string.strip()
def price(amount):
return "{0:.2f}".format(amount)
def printline(left, right='', width=40, ratio=0.5, indent=0):
lwidth = int(width * ratio)
rwidth = width - lwidth
lwidth = lwidth - indent
left = left[:lwidth]
if len(left) != lwidth:
left = left + ' ' * (lwidth - len(left))
right = right[-rwidth:]
if len(right) != rwidth:
right = ' ' * (rwidth - len(right)) + right
return ' ' * indent + left + right + '\n'
if len(printers) > 0:
printer = printers[0]
eprint = escpos.printer.Usb(printer['vendor'], printer['product'])
#eprint.image(os.path.join(os.path.dirname(__file__),'logo_grayscale.png'))
# Receipt Header
eprint.set(align='center',type='b',height=2,width=2)
eprint.text(receipt['shop']['name'] + '\n')
eprint.set(align='center',type='b')
if check(receipt['company']['name']):
eprint.text(receipt['company']['name'] + '\n')
if check(receipt['company']['contact_address']):
eprint.text(receipt['company']['contact address'] + '\n')
if check(receipt['company']['phone']):
eprint.text('Tel:' + receipt['company']['phone'] + '\n')
if check(receipt['company']['vat']):
eprint.text('VAT:' + receipt['company']['vat'] + '\n')
if check(receipt['company']['email']):
eprint.text(receipt['company']['email'] + '\n')
if check(receipt['company']['website']):
eprint.text(receipt['company']['website'] + '\n')
if check(receipt['cashier']):
eprint.text('-'*32+'\n')
eprint.text('Served by '+receipt['cashier']+'\n')
# Orderlines
eprint.text('\n\n')
eprint.set(align='center')
for line in receipt['orderlines']:
pricestr = price(line['price_display'])
if line['discount'] == 0 and line['unit_name'] == 'Unit(s)' and line['quantity'] == 1:
eprint.text(printline(line['product_name'],pricestr,ratio=0.6))
else:
eprint.text(printline(line['product_name'],ratio=0.6))
if line['discount'] != 0:
eprint.text(printline('Discount: '+str(line['discount'])+'%', ratio=0.6, indent=2))
if line['unit_name'] == 'Unit(s)':
eprint.text( printline( str(line['quantity']) + ' x ' + price(line['price']), pricestr, ratio=0.6, indent=2))
else:
eprint.text( printline( str(line['quantity']) + line['unit_name'] + ' x ' + price(line['price']), pricestr, ratio=0.6, indent=2))
# Subtotal if the taxes are not included
taxincluded = True
if price(receipt['subtotal']) != price(receipt['total_with_tax']):
eprint.text(printline('','-------'));
eprint.text(printline('Subtotal',price(receipt['subtotal']),width=40, ratio=0.6))
eprint.text(printline('Taxes',price(receipt['total_tax']),width=40, ratio=0.6))
taxincluded = False
# Total
eprint.text(printline('','-------'));
eprint.set(align='center',height=2)
eprint.text(printline(' TOTAL',price(receipt['total_with_tax']),width=40, ratio=0.6))
eprint.text('\n\n');
# Paymentlines
eprint.set(align='center')
for line in receipt['paymentlines']:
eprint.text(printline(line['journal'], price(line['amount']), ratio=0.6))
eprint.text('\n');
eprint.set(align='center',height=2)
eprint.text(printline(' CHANGE',price(receipt['change']),width=40, ratio=0.6))
eprint.set(align='center')
eprint.text('\n');
# Extra Payment info
if receipt['total_discount'] != 0:
eprint.text(printline('Discounts',price(receipt['total_discount']),width=40, ratio=0.6))
if taxincluded:
eprint.text(printline('Taxes',price(receipt['total_tax']),width=40, ratio=0.6))
# Footer
eprint.text(receipt['name']+'\n')
eprint.text( str(receipt['date']['date']).zfill(2)
+'/'+ str(receipt['date']['month']+1).zfill(2)
+'/'+ str(receipt['date']['year']).zfill(4)
+' '+ str(receipt['date']['hour']).zfill(2)
+':'+ str(receipt['date']['minute']).zfill(2) )
eprint.cut()
return

View File

@ -5,6 +5,10 @@ import os
import openerp import openerp
import time import time
import random import random
import subprocess
import usb.core
import escpos
import escpos.printer
from openerp import http from openerp import http
from openerp.http import request from openerp.http import request
@ -12,12 +16,19 @@ from openerp.addons.web.controllers.main import manifest_list, module_boot, html
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
class ProxyController(http.Controller): class Proxy(http.Controller):
def __init__(self): def __init__(self):
self.scale = 'closed' self.scale = 'closed'
self.scale_weight = 0.0 self.scale_weight = 0.0
pass pass
def connected_usb_devices(self,devices):
connected = []
for device in devices:
if usb.core.find(idVendor=device['vendor'], idProduct=device['product']) != None:
connected.append(device)
return connected
@http.route('/hw_proxy/test_connection', type='json', auth='admin') @http.route('/hw_proxy/test_connection', type='json', auth='admin')
def test_connection(self): def test_connection(self):
_logger.info('Received Connection Test from the Point of Sale'); _logger.info('Received Connection Test from the Point of Sale');

View File

@ -331,6 +331,7 @@ function openerp_pos_devices(instance,module){ //module is instance.point_of_sal
* } * }
*/ */
print_receipt: function(receipt){ print_receipt: function(receipt){
console.log('PRINT RECEIPT:', receipt);
return this.message('print_receipt',{receipt: receipt}); return this.message('print_receipt',{receipt: receipt});
}, },

View File

@ -845,7 +845,8 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal
date: date.getDate(), // day of the month date: date.getDate(), // day of the month
day: date.getDay(), // day of the week day: date.getDay(), // day of the week
hour: date.getHours(), hour: date.getHours(),
minute: date.getMinutes() minute: date.getMinutes() ,
isostring: date.toISOString(),
}, },
company:{ company:{
email: company.email, email: company.email,