1 # -*- coding: utf-8 -*-
13 import openerp.addons.hw_proxy.controllers.main as hw_proxy
16 from threading import Thread, Lock
17 from Queue import Queue, Empty
26 from ..escpos import printer
27 from ..escpos import supported_devices
29 escpos = printer = None
33 from openerp import http
34 from openerp.http import request
35 from openerp.addons.web.controllers.main import manifest_list, module_boot, html_template
36 from openerp.tools.translate import _
38 _logger = logging.getLogger(__name__)
41 class EscposDriver(Thread):
46 self.status = {'status':'connecting', 'messages':[]}
48 def connected_usb_devices(self):
51 for device in supported_devices.device_list:
52 if usb.core.find(idVendor=device['vendor'], idProduct=device['product']) != None:
53 connected.append(device)
56 def lockedstart(self):
58 if not self.isAlive():
63 def get_escpos_printer(self):
65 printers = self.connected_usb_devices()
67 self.set_status('connected','Connected to '+printers[0]['name'])
68 return escpos.printer.Usb(printers[0]['vendor'], printers[0]['product'])
70 self.set_status('disconnected','Printer Not Found')
72 except Exception as e:
73 self.set_status('error',str(e))
77 self.push_task('status')
82 def open_cashbox(self,printer):
86 def set_status(self, status, message = None):
87 if status == self.status['status']:
88 if message != None and message != self.status['messages'][-1]:
89 self.status['messages'].append(message)
91 self.status['status'] = status
93 self.status['messages'] = [message]
95 self.status['messages'] = []
97 if status == 'error' and message:
98 _logger.error('ESC/POS Error: '+message)
99 elif status == 'disconnected' and message:
100 _logger.warning('ESC/POS Device Disconnected: '+message)
104 _logger.error('ESC/POS cannot initialize, please verify system dependencies.')
108 timestamp, task, data = self.queue.get(True)
110 printer = self.get_escpos_printer()
114 self.queue.put((timestamp,task,data))
117 elif task == 'receipt':
118 if timestamp >= time.time() - 1 * 60 * 60:
119 self.print_receipt_body(printer,data)
121 elif task == 'xml_receipt':
122 if timestamp >= time.time() - 1 * 60 * 60:
123 printer.receipt(data)
124 elif task == 'cashbox':
125 if timestamp >= time.time() - 12:
126 self.open_cashbox(printer)
127 elif task == 'printstatus':
128 self.print_status(printer)
129 elif task == 'status':
132 except Exception as e:
133 self.set_status('error', str(e))
134 errmsg = str(e) + '\n' + '-'*60+'\n' + traceback.format_exc() + '-'*60 + '\n'
135 _logger.error(errmsg);
137 def push_task(self,task, data = None):
139 self.queue.put((time.time(),task,data))
141 def print_status(self,eprint):
142 localips = ['0.0.0.0','127.0.0.1','127.0.1.1']
143 ips = [ c.split(':')[1].split(' ')[0] for c in commands.getoutput("/sbin/ifconfig").split('\n') if 'inet addr' in c ]
144 ips = [ ip for ip in ips if ip not in localips ]
146 eprint.set(align='center',type='b',height=2,width=2)
147 eprint.text('PosBox Status\n')
149 eprint.set(align='center')
152 eprint.text('ERROR: Could not connect to LAN\n\nPlease check that the PosBox is correc-\ntly connected with a network cable,\n that the LAN is setup with DHCP, and\nthat network addresses are available')
154 eprint.text('IP Address:\n'+ips[0]+'\n')
156 eprint.text('IP Addresses:\n')
161 eprint.text('\nHomepage:\nhttp://'+ips[0]+':8069\n')
166 def print_receipt_body(self,eprint,receipt):
169 return string != True and bool(string) and string.strip()
172 return ("{0:."+str(receipt['precision']['price'])+"f}").format(amount)
175 return ("{0:."+str(receipt['precision']['money'])+"f}").format(amount)
177 def quantity(amount):
178 if math.floor(amount) != amount:
179 return ("{0:."+str(receipt['precision']['quantity'])+"f}").format(amount)
183 def printline(left, right='', width=40, ratio=0.5, indent=0):
184 lwidth = int(width * ratio)
185 rwidth = width - lwidth
186 lwidth = lwidth - indent
189 if len(left) != lwidth:
190 left = left + ' ' * (lwidth - len(left))
192 right = right[-rwidth:]
193 if len(right) != rwidth:
194 right = ' ' * (rwidth - len(right)) + right
196 return ' ' * indent + left + right + '\n'
199 taxes = receipt['tax_details']
201 eprint.text(printline(tax['tax']['name'],price(tax['amount']), width=40,ratio=0.6))
204 if receipt['company']['logo']:
205 eprint.set(align='center')
206 eprint.print_base64_image(receipt['company']['logo'])
209 eprint.set(align='center',type='b',height=2,width=2)
210 eprint.text(receipt['company']['name'] + '\n')
212 eprint.set(align='center',type='b')
213 if check(receipt['shop']['name']):
214 eprint.text(receipt['shop']['name'] + '\n')
215 if check(receipt['company']['contact_address']):
216 eprint.text(receipt['company']['contact_address'] + '\n')
217 if check(receipt['company']['phone']):
218 eprint.text('Tel:' + receipt['company']['phone'] + '\n')
219 if check(receipt['company']['vat']):
220 eprint.text('VAT:' + receipt['company']['vat'] + '\n')
221 if check(receipt['company']['email']):
222 eprint.text(receipt['company']['email'] + '\n')
223 if check(receipt['company']['website']):
224 eprint.text(receipt['company']['website'] + '\n')
225 if check(receipt['header']):
226 eprint.text(receipt['header']+'\n')
227 if check(receipt['cashier']):
228 eprint.text('-'*32+'\n')
229 eprint.text('Served by '+receipt['cashier']+'\n')
233 eprint.set(align='center')
234 for line in receipt['orderlines']:
235 pricestr = price(line['price_display'])
236 if line['discount'] == 0 and line['unit_name'] == 'Unit(s)' and line['quantity'] == 1:
237 eprint.text(printline(line['product_name'],pricestr,ratio=0.6))
239 eprint.text(printline(line['product_name'],ratio=0.6))
240 if line['discount'] != 0:
241 eprint.text(printline('Discount: '+str(line['discount'])+'%', ratio=0.6, indent=2))
242 if line['unit_name'] == 'Unit(s)':
243 eprint.text( printline( quantity(line['quantity']) + ' x ' + price(line['price']), pricestr, ratio=0.6, indent=2))
245 eprint.text( printline( quantity(line['quantity']) + line['unit_name'] + ' x ' + price(line['price']), pricestr, ratio=0.6, indent=2))
247 # Subtotal if the taxes are not included
249 if money(receipt['subtotal']) != money(receipt['total_with_tax']):
250 eprint.text(printline('','-------'));
251 eprint.text(printline(_('Subtotal'),money(receipt['subtotal']),width=40, ratio=0.6))
253 #eprint.text(printline(_('Taxes'),money(receipt['total_tax']),width=40, ratio=0.6))
258 eprint.text(printline('','-------'));
259 eprint.set(align='center',height=2)
260 eprint.text(printline(_(' TOTAL'),money(receipt['total_with_tax']),width=40, ratio=0.6))
264 eprint.set(align='center')
265 for line in receipt['paymentlines']:
266 eprint.text(printline(line['journal'], money(line['amount']), ratio=0.6))
269 eprint.set(align='center',height=2)
270 eprint.text(printline(_(' CHANGE'),money(receipt['change']),width=40, ratio=0.6))
271 eprint.set(align='center')
275 if receipt['total_discount'] != 0:
276 eprint.text(printline(_('Discounts'),money(receipt['total_discount']),width=40, ratio=0.6))
279 #eprint.text(printline(_('Taxes'),money(receipt['total_tax']),width=40, ratio=0.6))
282 if check(receipt['footer']):
283 eprint.text('\n'+receipt['footer']+'\n\n')
284 eprint.text(receipt['name']+'\n')
285 eprint.text( str(receipt['date']['date']).zfill(2)
286 +'/'+ str(receipt['date']['month']+1).zfill(2)
287 +'/'+ str(receipt['date']['year']).zfill(4)
288 +' '+ str(receipt['date']['hour']).zfill(2)
289 +':'+ str(receipt['date']['minute']).zfill(2) )
292 driver = EscposDriver()
294 driver.push_task('printstatus')
296 hw_proxy.drivers['escpos'] = driver
298 class EscposProxy(hw_proxy.Proxy):
300 @http.route('/hw_proxy/open_cashbox', type='json', auth='none', cors='*')
301 def open_cashbox(self):
302 _logger.info('ESC/POS: OPEN CASHBOX')
303 driver.push_task('cashbox')
305 @http.route('/hw_proxy/print_receipt', type='json', auth='none', cors='*')
306 def print_receipt(self, receipt):
307 _logger.info('ESC/POS: PRINT RECEIPT')
308 driver.push_task('receipt',receipt)
310 @http.route('/hw_proxy/print_xml_receipt', type='json', auth='none', cors='*')
311 def print_receipt(self, receipt):
312 _logger.info('ESC/POS: PRINT XML RECEIPT')
313 driver.push_task('xml_receipt',receipt)