1 # -*- coding: utf-8 -*-
12 import openerp.addons.hw_proxy.controllers.main as hw_proxy
14 from threading import Thread
15 from Queue import Queue, Empty
24 from ..escpos import printer
25 from ..escpos import supported_devices
27 escpos = printer = None
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 _
36 _logger = logging.getLogger(__name__)
38 class EscposDriver(Thread):
42 self.status = {'status':'connecting', 'messages':[]}
44 def connected_usb_devices(self):
46 for device in supported_devices.device_list:
47 if usb.core.find(idVendor=device['vendor'], idProduct=device['product']) != None:
48 connected.append(device)
51 def get_escpos_printer(self):
53 printers = self.connected_usb_devices()
55 self.set_status('connected','Connected to '+printers[0]['name'])
56 return escpos.printer.Usb(printers[0]['vendor'], printers[0]['product'])
58 self.set_status('disconnected','Printer Not Found')
60 except Exception as e:
61 self.set_status('error',str(e))
65 self.push_task('status')
68 def open_cashbox(printer):
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)
77 self.status['status'] = status
79 self.status['messages'] = [message]
81 self.status['messages'] = []
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)
92 timestamp, task, data = self.queue.get(True)
94 printer = self.get_escpos_printer()
98 self.queue.put((timestamp,task,data))
101 elif task == 'receipt':
102 if timestamp >= time.time() - 1 * 60 * 60:
103 self.print_receipt_body(printer,data)
105 elif task == 'cashbox':
106 if timestamp >= time.time() * 12:
107 self.open_cashbox(printer)
108 elif task == 'status':
111 except Exception as e:
112 self.set_status('error', str(e))
115 def push_task(self,task, data = None):
116 if not self.isAlive():
118 self.queue.put((time.time(),task,data))
120 def print_receipt_body(self,eprint,receipt):
123 return string != True and bool(string) and string.strip()
126 return ("{0:."+str(receipt['precision']['price'])+"f}").format(amount)
129 return ("{0:."+str(receipt['precision']['money'])+"f}").format(amount)
131 def quantity(amount):
132 if math.floor(amount) != amount:
133 return ("{0:."+str(receipt['precision']['quantity'])+"f}").format(amount)
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
144 if len(left) != lwidth:
145 left = left + ' ' * (lwidth - len(left))
147 right = right[-rwidth:]
148 if len(right) != rwidth:
149 right = ' ' * (rwidth - len(right)) + right
151 return ' ' * indent + left + right + '\n'
154 taxes = receipt['tax_details']
156 eprint.text(printline(tax['tax']['name'],price(tax['amount']), width=40,ratio=0.6))
159 if receipt['company']['logo']:
160 eprint.print_base64_image(receipt['company']['logo'])
163 eprint.set(align='center',type='b',height=2,width=2)
164 eprint.text(receipt['company']['name'] + '\n')
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')
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))
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))
199 eprint.text( printline( quantity(line['quantity']) + line['unit_name'] + ' x ' + price(line['price']), pricestr, ratio=0.6, indent=2))
201 # Subtotal if the taxes are not included
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))
207 #eprint.text(printline(_('Taxes'),money(receipt['total_tax']),width=40, ratio=0.6))
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))
218 eprint.set(align='center')
219 for line in receipt['paymentlines']:
220 eprint.text(printline(line['journal'], money(line['amount']), ratio=0.6))
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')
229 if receipt['total_discount'] != 0:
230 eprint.text(printline(_('Discounts'),money(receipt['total_discount']),width=40, ratio=0.6))
233 #eprint.text(printline(_('Taxes'),money(receipt['total_tax']),width=40, ratio=0.6))
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) )
245 driver = EscposDriver()
247 hw_proxy.drivers['escpos'] = driver
249 class EscposProxy(hw_proxy.Proxy):
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')
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)