def _read_reply(self): reply = '' timeouts = 0 while True: if timeouts > RETRIES_BEFORE_TIMEOUT: raise DriverError( _("Timeout communicating with fiscal " "printer")) c = self.read(1) if len(c) != 1: timeouts += 1 continue # STX is always the first char in the reply. Ignore garbage # until STX is received. if len(reply) == 0 and c != STX: log.info('ignoring garbage in reply: %r' % c) continue reply += c if c == ETX and reply[-2] != ESC: break reply += self.read(4) log.debug("<<< %s" % repr(reply)) return Reply(reply, self._command_id)
def set_coupon_footers(self, footers): if len(footers) < HEADER_LINE_NUMBER: for i in range(0, len(footers)): footer = footers[i] if len(footer) < HEADER_LINE_SIZE: self._send_command(CMD_SET_FOOTER, "%02d" % (i + 91), str(footer), response='c') else: raise DriverError( _("This printer supports a" "maximum of 40 characters per line")) else: raise DriverError( _("This printer supports up to 8 lines" "of header"))
def set_tax_rates(self, tax_rates): log.info('set_tax_rates()') if hasattr(self._driver, 'set_tax_rates'): self._driver.set_tax_rates(tax_rates) else: raise DriverError( _("This method is not supported from " "the current printer"))
def set_coupon_headers(self, headers): log.info('set_coupon_headers()') if hasattr(self._driver, 'set_coupon_headers'): self._driver.set_coupon_headers(headers) else: raise DriverError( _("This method is not supported from " "the current printer"))
def set_payment_methods(self, payment_methods): for pm in payment_methods: if (int(pm.get('code')) <= 16): if (len(pm.get('description')) <= 14): data = "%02d%-14s" % (int( pm.get('code')), str(pm.get('description'))) self._send_command(CMD_SET_PAYMENT_METHOD, data, response='c') else: raise DriverError( _("The 'description'" "field has a maximum length of 14 characters")) else: raise DriverError( _("Invalid code: " + pm.get('code') + "" "Try a value between 01-16"))
def set_payment_methods(self, payment_methods): log.info('set_payment_methods()') if hasattr(self._driver, 'set_payment_methods'): self._driver.set_payment_methods(payment_methods) else: raise DriverError( _("This method is not supported from " "the current printer"))
def set_tax_rates(self, tax_rates): if len(tax_rates) == 4: taxes = {tax.get('code'): tax.get('value') for tax in tax_rates} vcodes = set(['!', '#', '"', ' ']) if vcodes.difference(set(taxes.keys())) == set(): data = ("2%05.2f2%05.2f2%05.2f") % ( taxes.get('!'), taxes.get('"'), taxes.get('#')) data = data.replace(".", "") self._send_command(CMD_SET_TAXES, data, response='c') else: raise DriverError( _("This tax codes is not supported from " "the current printer")) else: raise DriverError(_("this printer just can set 4 fixed taxes")) return True
def handle_error(self, error_value, raw): error = int(error_value[2:]) # Page 61-62 if error == 39: raise DriverError('Bad parameters: %r' % raw, error) elif error == 10: raise CouponOpenError(_("Document is already open"), error) elif error == 11: raise CouponNotOpenError(_("Coupon is not open"), error) elif error == 12: raise CouponNotOpenError(_("There's no open document to cancel"), error) elif error == 15: raise CancelItemError(_("There is no such item in " "the coupon"), error) elif error == 16: raise DriverError("Bad discount/markup parameter", error) elif error == 21: log.warning(_('Printer error %s: No paper'), error) elif error == 22: raise DriverError( "Reduce Z was already sent today, try again tomorrow", error) elif error == 23: raise PendingReduceZ elif error == 24: raise DriverError("Bad unit specified: %r" % raw, error) elif error == 42: raise DriverError("Read X has not been sent yet", error) elif error == 45: raise DriverError(_("Required field is blank"), error) else: raise DriverError("Unhandled error: %d" % error, error)
def close_till(self, previous_day=False): self._check() if self.till_closed: raise DriverError( "Reduce Z was already sent today, try again tomorrow") self.till_closed = True self._save_state() self.output.feed("REDUÇÃO Z\n") self.output.feed_line()
def payment_receipt_open(self, identifier, coo, method_id, value): method = self._get_payment_description(method_id) if not method: raise DriverError('Looks like this payment method ' 'is not configured in the printer') value = int(value * 100) self._send_command(CMD_PAYMENT_RECEIPT_OPEN, '%-16s%014d%06d' % (method, value, coo))
def get_coupon_footers(self): log.info('get_coupon_footers()') footers = [] if hasattr(self._driver, 'get_coupon_footers'): footers = self._driver.get_coupon_footers() else: raise DriverError( _("This method is not supported from " "the current printer")) return footers
def handle_error(self, reply): # Page 4-2 # format: |.-NNNNERROR_MESSAGE}| if reply[1] != '-': return reply[2:] errmsg = reply[6:] try: exception, reason = self.errors_dict[errmsg] raise exception(reason) except KeyError: raise DriverError(errmsg)
def cancel_last_coupon(self): # Informações sobre último documento. reply = self._send_command('0908') last_coupon = reply.fields[0] if last_coupon == FISCAL_COUPON: self._cancel_fiscal_coupon() elif last_coupon == NON_FISCAL_COUPON: self._cancel_non_fiscal_coupon() else: raise DriverError(_("Attempt to cancel after emission of another " "DOC"))
def parse_error(self): status = ord(self.statuses) # There is no error to parse. if status == EP375Status.PRINTER_IS_OK: return try: exception, reason = self.errors_dict[status] raise exception(reason) except KeyError: raise DriverError("Unhandled error: %d" % status)
def get_payment_receipt_identifier(self, method_name): """Returns the receipt identifier corresponding to the payment method. @param method_name: this is the payment method name. A receipt with the same name should be configured at the printer. """ constants = self._get_bound_receipt_constants() for id, name in constants: if str(name) == str(method_name): return id raise DriverError(_("Receipt for method %s is not configured") % method_name)
def check_error(self): log.debug("reply_status %s" % self.reply_status) error_code = self.reply_status # Success, do nothing if error_code == '0000': return if error_code in self.error_codes: error = self.error_codes[error_code] error.code = int(error_code, 16) raise error raise DriverError(error="unhandled driver error", code=int(error_code, 16))
def _send_command(self, command, **params): # Page 38-39 parameters = [] for param, value in sorted(params.items()): if isinstance(value, Decimal): value = ('%.03f' % value).replace('.', ',') elif isinstance(value, bytes): value = '"%s"' % bytes2str(value) elif isinstance(value, str): value = '"%s"' % value elif isinstance(value, bool): if value is False: value = 'f' elif value is True: value = 't' elif isinstance(value, datetime.date): value = value.strftime('#%d/%m/%y#') parameters.append('%s=%s' % (param, value)) reply = self.writeline("%d;%s;%s;" % (self._command_id, command, ' '.join(parameters))) if reply[0] != '{': # This happened once after the first command issued after # the power returned, it should probably be handled gracefully raise AssertionError(repr(reply)) # Page 39 sections = reply[1:].split(';') if len(sections) != 4: raise AssertionError retdict = self._parse_return_value(sections[2]) errorcode = int(sections[1]) if errorcode != 0: errordesc = retdict['Circunstancia'] try: exception = self.errors_dict[errorcode] except KeyError: raise DriverError(errordesc, errorcode) raise exception(errordesc, errorcode) return retdict
def __init__(self, string, command_id): checksum = string[-4:] self.string = string[:-4] log.debug('reply %s' % repr(string)) cs = '%04X' % sum([ord(i) for i in self.string]) if cs != checksum: raise DriverError('Erro de checksum') self.string = unescape(self.string) # Verifica header & tail assert self.pop() == STX, 'STX' frame_id = self.pop() if frame_id == '\x80': self.intermediate = True return self.intermediate = False assert frame_id == chr(command_id), ('command_id', command_id) assert self.string[-1] == ETX, 'ETX' # Retira statuses self.printer_status = struct.unpack('>H', str2bytes(self.pop(2)))[0] assert self.pop() == FLD, 'FLD 1' self.fiscal_status = struct.unpack('>H', str2bytes(self.pop(2)))[0] assert self.pop() == FLD, 'FLD 2' # reserved self.pop() r = self.pop(2) self.reply_status = '%02X%02X' % (ord(r[0]), ord(r[1])) assert self.pop() == FLD, 'FLD 3' # reserved self.pop() # Pega dados de retorno fields = self.string[:-1] # FIXME: Maybe we need to de-escape the string self.fields = fields.split(FLD)
def _read_reply(self): rep = '' timeouts = 0 while True: if timeouts > RETRIES_BEFORE_TIMEOUT: raise DriverError(_("Timeout communicating with fiscal " "printer")) c = self.read(1) if len(c) != 1: timeouts += 1 continue if c == self.EOL_DELIMIT: log.debug("<<< %r" % rep) return rep rep += c
def _read_reply(self, size): a = 0 data = '' while True: if a > RETRIES_BEFORE_TIMEOUT: raise DriverError(_("Timeout communicating with fiscal " "printer")) a += 1 reply = self.read(size) if reply is None: continue data += reply if len(data) < size: continue log.debug("<<< %r (%d bytes)" % (data, len(data))) return data
def readline(self): out = '' a = 0 retries = 10 while True: if a > retries: raise DriverError( _("Timeout communicating with fiscal " "printer")) c = self._port.read(1) if not c: a += 1 print 'take %s' % a continue a = 0 if c == self.EOL_DELIMIT: log.debug('<<< %r' % out) return out out += c
def _define_tax_name(self, code, name, entrada=False): try: retdict = self._send_command( 'LeNaoFiscal', CodNaoFiscal=code) except DriverError as e: if e.code != 8057: # Not configured raise else: for retname in ['NomeNaoFiscal', 'DescricaoNaoFiscal']: configured_name = retdict[retname] if configured_name != name: raise DriverError( "The name of the tax code %d is set to %r, " "but it needs to be configured as %r" % ( code, configured_name, name)) try: self._send_command( 'DefineNaoFiscal', CodNaoFiscal=code, DescricaoNaoFiscal=name, NomeNaoFiscal=name, TipoNaoFiscal=entrada) except DriverError as e: if e.code != 8036: raise
def _send_command(self, command, extension='0000', *args): cmd = self._get_package(command, extension, args) self.write(cmd) # Printer should reply with an ACK imediataly ack = self.read(1) if not ack: raise DriverError(_("Timeout communicating with fiscal " "printer")) assert ack == ACK, repr(ack) reply = self._read_reply() # Keep reading while printer sends intermediate replies. while reply.intermediate: log.debug("intermediate") reply = self._read_reply() # send our ACK self.write(ACK) reply.check_error() return reply
class MP25Status(object): PENDING_REDUCE_Z = 66 st1_codes = { 128: (OutofPaperError(_("Printer is out of paper"))), # 64: (AlmostOutofPaper(_("Printer almost out of paper"))), 32: (PrinterError(_("Printer clock error"))), 16: (PrinterError(_("Printer in error state"))), 8: (CommandError(_("First data value in CMD is not ESC (1BH)"))), 4: (CommandError(_("Nonexistent command"))), # 2: (CouponOpenError(_("Printer has a coupon currently open"))), 1: (CommandError(_("Invalid number of parameters")))} st2_codes = { 128: (CommandError(_("Invalid CMD parameter"))), 64: (HardwareFailure(_("Fiscal memory is full"))), 32: (HardwareFailure(_("Error in CMOS memory"))), 16: (PrinterError(_("Given tax is not programmed on the printer"))), 8: (DriverError(_("No available tax slot"))), 4: (CancelItemError(_("The item wasn't added in the coupon or can't " "be cancelled"))), # 2: (PrinterError(_("Owner data (CGC/IE) not programmed on the printer"))), # FIXME: This shouldn't be commented. But it will break the tests. # Need to update the tests for all bematech printers #1: (CommandError(_("Command not executed"))) } st3_codes = { # 7: (CouponOpenError(_("Coupon already Open"))), # 8: (CouponNotOpenError(_("Coupon is closed"))), 13: (PrinterOfflineError(_("Printer is offline"))), 16: (DriverError(_("Surcharge or discount greater than coupon total" "value"))), 17: (DriverError(_("Coupon with no items"))), 20: (PaymentAdditionError(_("Payment method not recognized"))), 22: (PaymentAdditionError(_("Isn't possible add more payments since" "the coupon total value already was " "reached"))), 23: (DriverError(_("Coupon isn't totalized yet"))), 43: (CouponNotOpenError(_("Printer not initialized"))), 45: (PrinterError(_("Printer without serial number"))), 52: (DriverError(_("Invalid start date"))), 53: (DriverError(_("Invalid final date"))), 85: (DriverError(_("Sale with null value"))), 91: (ItemAdditionError(_("Surcharge or discount greater than item" "value"))), 100: (DriverError(_("Invalid date"))), 115: (CancelItemError(_("Item doesn't exists or already was cancelled"))), 118: (DriverError(_("Surcharge greater than item value"))), 119: (DriverError(_("Discount greater than item value"))), 129: (CouponOpenError(_("Invalid month"))), 169: (CouponTotalizeError(_("Coupon already totalized"))), 170: (PaymentAdditionError(_("Coupon not totalized yet"))), 171: (DriverError(_("Surcharge on subtotal already effected"))), 172: (DriverError(_("Discount on subtotal already effected"))), 176: (DriverError(_("Invalid date")))} def __init__(self, reply): self.st1, self.st2, self.st3 = reply[-3:] @property def open(self): return self.st1 & 2 def _check_error_in_dict(self, error_codes, value): for key in error_codes: if key & value: raise error_codes[key] def check_error(self): log.debug("status: st1=%s st2=%s st3=%s" % (self.st1, self.st2, self.st3)) if self.st1 != 0: self._check_error_in_dict(self.st1_codes, self.st1) if self.st2 != 0: self._check_error_in_dict(self.st2_codes, self.st2) # first bit means not executed, look in st3 for more if self.st2 & 1 and self.st3: if self.st3 in self.st3_codes: raise self.st3_codes[self.st3]
class Reply(object): # # Printer flags # error_codes = { '0101': (CommandError(_("Invalid command for current state."))), '0102': (CommandError(_("Invalid command for current document."))), '0203': (CommandError(_("Excess fields"))), '0204': (CommandError(_("Missing fields"))), '0205': (CommandParametersError(_("Field not optional."))), '0206': (CommandParametersError(_("Invalid alphanumeric field."))), '0207': (CommandParametersError(_("Invalid alphabetic field."))), '0208': (CommandParametersError(_("Invalid numeric field."))), '020E': (CommandParametersError(_("Fields with print invalid " "attributes."))), '0304': (OutofPaperError(_("Out of paper."))), '0305': (AlmostOutofPaper(_("Almost out of paper."))), '0801': (CommandError(_("Invalid command with closed " "fiscal journey."))), '090C': (DriverError(_("Payment method not defined."))), '090F': (ItemAdditionError(_("Tax not found."))), '0910': (ItemAdditionError(_("Invalid tax."))), '0A12': (CancelItemError( _("It was not possible cancel the last " "fiscal coupon."))), '0A15': (PrinterError(_("Requires CDC cancellation."))), '0A16': (CancelItemError(_("Invalid item number in fiscal coupon"))), '0E0A': (PrinterError(_("Last non-fiscal coupon not found."))), '0E0B': (PrinterError(_("Payment method not found."))), } def __init__(self, string, command_id): checksum = string[-4:] self.string = string[:-4] log.debug('reply %s' % repr(string)) cs = '%04X' % sum([ord(i) for i in self.string]) if cs != checksum: raise DriverError('Erro de checksum') self.string = unescape(self.string) # Verifica header & tail assert self.pop() == STX, 'STX' frame_id = self.pop() if frame_id == '\x80': self.intermediate = True return self.intermediate = False assert frame_id == chr(command_id), ('command_id', command_id) assert self.string[-1] == ETX, 'ETX' # Retira statuses self.printer_status = struct.unpack('>H', str2bytes(self.pop(2)))[0] assert self.pop() == FLD, 'FLD 1' self.fiscal_status = struct.unpack('>H', str2bytes(self.pop(2)))[0] assert self.pop() == FLD, 'FLD 2' # reserved self.pop() r = self.pop(2) self.reply_status = '%02X%02X' % (ord(r[0]), ord(r[1])) assert self.pop() == FLD, 'FLD 3' # reserved self.pop() # Pega dados de retorno fields = self.string[:-1] # FIXME: Maybe we need to de-escape the string self.fields = fields.split(FLD) def check_error(self): log.debug("reply_status %s" % self.reply_status) error_code = self.reply_status # Success, do nothing if error_code == '0000': return if error_code in self.error_codes: error = self.error_codes[error_code] error.code = int(error_code, 16) raise error raise DriverError(error="unhandled driver error", code=int(error_code, 16)) def pop(self, size=1): """Remove size bites from the begining of self.string and returns it """ head = self.string[:size] self.string = self.string[size:] return head def check_printer_status(self): status = bin(self.printer_status) printer_status = status[2:] return printer_status def check_fiscal_status(self): status = bin(self.fiscal_status) fiscal_status = status[2:] return fiscal_status
def get_payment_constants(self): raise DriverError( _("This command is not supported " "for the current printer"))
result = 0.0 return result # This how the printer needs to be configured. def _define_tax_name(self, code, name, entrada=False): try: retdict = self._send_command('LeNaoFiscal', CodNaoFiscal=code) except DriverError, e: if e.code != 8057: # Not configured raise else: for retname in ['NomeNaoFiscal', 'DescricaoNaoFiscal']: configured_name = retdict[retname] if configured_name != name: raise DriverError( "The name of the tax code %d is set to %r, " "but it needs to be configured as %r" % (code, configured_name, name)) try: self._send_command('DefineNaoFiscal', CodNaoFiscal=code, DescricaoNaoFiscal=name, NomeNaoFiscal=name, TipoNaoFiscal=entrada) except DriverError, e: if e.code != 8036: raise def _delete_tax_name(self, code): try: self._send_command('ExcluiNaoFiscal', CodNaoFiscal=code)