c668a1ffdd79879621dcaf0407a1f0d27b90d424
[odoo/odoo.git] / addons / hw_escpos / controllers / main.py
1 # -*- coding: utf-8 -*-
2 import logging
3 import simplejson
4 import os
5 import io
6 import base64
7 import openerp
8 import time
9 import random
10 import math
11 import md5
12 import openerp.addons.hw_proxy.controllers.main as hw_proxy
13 import subprocess
14 from threading import Thread
15 from Queue import Queue, Empty
16
17 try:
18     import usb.core
19 except ImportError:
20     usb = None
21
22 try:
23     from .. import escpos
24     from ..escpos import printer
25     from ..escpos import supported_devices
26 except ImportError:
27     escpos = printer = None
28
29 from PIL import Image
30
31 from openerp import http
32 from openerp.http import request
33 from openerp.addons.web.controllers.main import manifest_list, module_boot, html_template
34 from openerp.tools.translate import _
35
36 _logger = logging.getLogger(__name__)
37
38 class EscposDriver(Thread):
39     def __init__(self):
40         Thread.__init__(self)
41         self.queue = Queue()
42         self.status = {'status':'connecting', 'messages':[]}
43
44     def connected_usb_devices(self):
45         connected = []
46         for device in supported_devices.device_list:
47             if usb.core.find(idVendor=device['vendor'], idProduct=device['product']) != None:
48                 connected.append(device)
49         return connected
50     
51     def get_escpos_printer(self):
52         try:
53             printers = self.connected_usb_devices()
54             if len(printers) > 0:
55                 self.set_status('connected','Connected to '+printers[0]['name'])
56                 return escpos.printer.Usb(printers[0]['vendor'], printers[0]['product'])
57             else:
58                 self.set_status('disconnected','Printer Not Found')
59                 return None
60         except Exception as e:
61             self.set_status('error',str(e))
62             return None
63
64     def get_status(self):
65         self.push_task('status')
66         return self.status
67
68     def open_cashbox(printer):
69         printer.cashdraw(2)
70         printer.cashdraw(5)
71
72     def set_status(self, status, message = None):
73         if status == self.status['status']:
74             if message != None and message != self.status['messages'][-1]:
75                 self.status['messages'].append(message)
76         else:
77             self.status['status'] = status
78             if message:
79                 self.status['messages'] = [message]
80             else:
81                 self.status['messages'] = []
82
83         if status == 'error' and message:
84             _logger.error('ESC/POS Error: '+message)
85         elif status == 'disconnected' and message:
86             _logger.warning('ESC/POS Device Disconnected: '+message)
87
88     def run(self):
89         self.queue = Queue()
90         while True:
91             try:
92                 timestamp, task, data = self.queue.get(True)
93
94                 printer = self.get_escpos_printer()
95
96                 if printer == None:
97                     if task != 'status':
98                         self.queue.put((timestamp,task,data))
99                     time.sleep(5)
100                     continue
101                 elif task == 'receipt': 
102                     if timestamp >= time.time() - 1 * 60 * 60:
103                         self.print_receipt_body(printer,data)
104                         printer.cut()
105                 elif task == 'cashbox':
106                     if timestamp >= time.time() * 12:
107                         self.open_cashbox(printer)
108                 elif task == 'status':
109                     pass
110
111             except Exception as e:
112                 self.set_status('error', str(e))
113                 _logger.error(e);
114
115     def push_task(self,task, data = None):
116         if not self.isAlive():
117             self.start()
118         self.queue.put((time.time(),task,data))
119
120     def print_receipt_body(self,eprint,receipt):
121
122         def check(string):
123             return string != True and bool(string) and string.strip()
124         
125         def price(amount):
126             return ("{0:."+str(receipt['precision']['price'])+"f}").format(amount)
127         
128         def money(amount):
129             return ("{0:."+str(receipt['precision']['money'])+"f}").format(amount)
130
131         def quantity(amount):
132             if math.floor(amount) != amount:
133                 return ("{0:."+str(receipt['precision']['quantity'])+"f}").format(amount)
134             else:
135                 return str(amount)
136
137
138         def printline(left, right='', width=40, ratio=0.5, indent=0):
139             lwidth = int(width * ratio) 
140             rwidth = width - lwidth 
141             lwidth = lwidth - indent
142             
143             left = left[:lwidth]
144             if len(left) != lwidth:
145                 left = left + ' ' * (lwidth - len(left))
146
147             right = right[-rwidth:]
148             if len(right) != rwidth:
149                 right = ' ' * (rwidth - len(right)) + right
150
151             return ' ' * indent + left + right + '\n'
152         
153         def print_taxes():
154             taxes = receipt['tax_details']
155             for tax in taxes:
156                 eprint.text(printline(tax['tax']['name'],price(tax['amount']), width=40,ratio=0.6))
157
158         # Receipt Header
159         if receipt['company']['logo']:
160             eprint.print_base64_image(receipt['company']['logo'])
161             eprint.text('\n')
162         else:
163             eprint.set(align='center',type='b',height=2,width=2)
164             eprint.text(receipt['company']['name'] + '\n')
165
166         eprint.set(align='center',type='b')
167         if check(receipt['shop']['name']):
168             eprint.text(receipt['shop']['name'] + '\n')
169         if check(receipt['company']['contact_address']):
170             eprint.text(receipt['company']['contact_address'] + '\n')
171         if check(receipt['company']['phone']):
172             eprint.text('Tel:' + receipt['company']['phone'] + '\n')
173         if check(receipt['company']['vat']):
174             eprint.text('VAT:' + receipt['company']['vat'] + '\n')
175         if check(receipt['company']['email']):
176             eprint.text(receipt['company']['email'] + '\n')
177         if check(receipt['company']['website']):
178             eprint.text(receipt['company']['website'] + '\n')
179         if check(receipt['header']):
180             eprint.text(receipt['header']+'\n')
181         if check(receipt['cashier']):
182             eprint.text('-'*32+'\n')
183             eprint.text('Served by '+receipt['cashier']+'\n')
184
185         # Orderlines
186         eprint.text('\n\n')
187         eprint.set(align='center')
188         for line in receipt['orderlines']:
189             pricestr = price(line['price_display'])
190             if line['discount'] == 0 and line['unit_name'] == 'Unit(s)' and line['quantity'] == 1:
191                 eprint.text(printline(line['product_name'],pricestr,ratio=0.6))
192             else:
193                 eprint.text(printline(line['product_name'],ratio=0.6))
194                 if line['discount'] != 0:
195                     eprint.text(printline('Discount: '+str(line['discount'])+'%', ratio=0.6, indent=2))
196                 if line['unit_name'] == 'Unit(s)':
197                     eprint.text( printline( quantity(line['quantity']) + ' x ' + price(line['price']), pricestr, ratio=0.6, indent=2))
198                 else:
199                     eprint.text( printline( quantity(line['quantity']) + line['unit_name'] + ' x ' + price(line['price']), pricestr, ratio=0.6, indent=2))
200
201         # Subtotal if the taxes are not included
202         taxincluded = True
203         if money(receipt['subtotal']) != money(receipt['total_with_tax']):
204             eprint.text(printline('','-------'));
205             eprint.text(printline(_('Subtotal'),money(receipt['subtotal']),width=40, ratio=0.6))
206             print_taxes()
207             #eprint.text(printline(_('Taxes'),money(receipt['total_tax']),width=40, ratio=0.6))
208             taxincluded = False
209
210
211         # Total
212         eprint.text(printline('','-------'));
213         eprint.set(align='center',height=2)
214         eprint.text(printline(_('         TOTAL'),money(receipt['total_with_tax']),width=40, ratio=0.6))
215         eprint.text('\n\n');
216         
217         # Paymentlines
218         eprint.set(align='center')
219         for line in receipt['paymentlines']:
220             eprint.text(printline(line['journal'], money(line['amount']), ratio=0.6))
221
222         eprint.text('\n');
223         eprint.set(align='center',height=2)
224         eprint.text(printline(_('        CHANGE'),money(receipt['change']),width=40, ratio=0.6))
225         eprint.set(align='center')
226         eprint.text('\n');
227
228         # Extra Payment info
229         if receipt['total_discount'] != 0:
230             eprint.text(printline(_('Discounts'),money(receipt['total_discount']),width=40, ratio=0.6))
231         if taxincluded:
232             print_taxes()
233             #eprint.text(printline(_('Taxes'),money(receipt['total_tax']),width=40, ratio=0.6))
234
235         # Footer
236         if check(receipt['footer']):
237             eprint.text('\n'+receipt['footer']+'\n\n')
238         eprint.text(receipt['name']+'\n')
239         eprint.text(      str(receipt['date']['date']).zfill(2)
240                     +'/'+ str(receipt['date']['month']+1).zfill(2)
241                     +'/'+ str(receipt['date']['year']).zfill(4)
242                     +' '+ str(receipt['date']['hour']).zfill(2)
243                     +':'+ str(receipt['date']['minute']).zfill(2) )
244
245 driver = EscposDriver()
246
247 hw_proxy.drivers['escpos'] = driver
248         
249 class EscposProxy(hw_proxy.Proxy):
250     
251     @http.route('/hw_proxy/open_cashbox', type='json', auth='none', cors='*')
252     def open_cashbox(self):
253         _logger.info('ESC/POS: OPEN CASHBOX') 
254         driver.push_task('cashbox')
255         
256     @http.route('/hw_proxy/print_receipt', type='json', auth='none', cors='*')
257     def print_receipt(self, receipt):
258         _logger.info('ESC/POS: PRINT RECEIPT') 
259         driver.push_task('receipt',receipt)
260