diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/base.py trunk/stoqdrivers/base.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/base.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/base.py	2016-10-13 16:22:19.188315288 -0300
@@ -39,7 +39,7 @@
 
 _ = stoqdrivers_gettext
 
-log = Logger('stoqdrivers.basedev')
+log = Logger('sigdrivers.basedev')
 
 class BaseDevice:
     """ Base class for all device interfaces, responsible for instantiate
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/exceptions.py trunk/stoqdrivers/exceptions.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/exceptions.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/exceptions.py	2016-10-13 16:22:19.428310956 -0300
@@ -36,6 +36,9 @@
 class CommError(Exception):
     "Common communication failures"
 
+class TimeoutError(Exception):
+    "Common communication failures"
+
 class PrinterError(Exception):
     "General printer errors"
 
@@ -47,20 +50,44 @@
         Exception.__init__(self, error)
         self.code = code
 
+class PrinterHeadUp(DriverError):
+    u"Impressora com cabe�a levantada"
+    
+class PdvConnectionError(DriverError):
+    u"N�o foi poss�vel efetuar a conex�o com o servidor."
+
 class OutofPaperError(DriverError):
-    "No paper left"
+    "Impressora sem papel"
+
+class EcfDateError(DriverError):
+    "Data do sistema difere com a data do ECF"
+
+class PrinterGTError(DriverError):
+    "GT lido difere do armazenado"
+
+class PrinterSerialError(DriverError):
+    "Divergencia no No. se Serie do ECF"
+
+class PrinterFileError(DriverError):    
+    u"O Arquivo de identificacao da impressora foi alterado"    
 
 class PrinterOfflineError(DriverError):
-    "Printer is offline"
+    "Impressora off-line"
 
 class AlmostOutofPaper(DriverError):
-    "Almost out of paper"
+    "Impressora quase sem papel"
 
 class HardwareFailure(DriverError):
     "Unknown hardware failure"
 
 class PendingReduceZ(DriverError):
-    "A Reduce Z is pending"
+    "Aguardando reducao Z"
+
+class BlockZ(DriverError):
+    "Bloqueio por RZ"
+
+class SummerTime(DriverError):
+    "Trocar horario de verao apenas apos RZ"
 
 class PendingReadX(DriverError):
     "A Read X is pending"
@@ -95,6 +122,9 @@
 class CouponTotalizeError(DriverError):
     "Error while totalizing a coupon."
 
+class TampaAberta(DriverError):
+    "Tampa aberta!"
+
 class PaymentAdditionError(DriverError):
     "Error while adding a payment."
 
@@ -118,3 +148,11 @@
 
 class InvalidValue(DriverError):
     "The value specified is invalid or is not in the expected range"
+
+class CodigoInvalido(DriverError):
+    """1. C�digo do produto igual a zeros.
+2. C�digo do produto igual a espa�os n�o � ISSQN.
+3. C�digo do acumulador n�o-fiscal n�o-num�rico ou inexistente.
+4. C�digo do acumulador de acr�scimo inv�lido.
+5. C�digo da mensagem informado no comando '<ESC>.09' inexistente.
+6. Formato do comando '<ESC>.09' incorreto."""
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/i18n.pot trunk/stoqdrivers/i18n.pot
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/i18n.pot	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/i18n.pot	2016-10-13 16:22:18.996318755 -0300
@@ -0,0 +1,38 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-05-26 07:53-0300\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: printers/fiscal.py:172
+msgid "You must totalize the coupon before add payments."
+msgstr ""
+
+#: printers/fiscal.py:204
+msgid "You must totalize the coupon before closing it"
+msgstr ""
+
+#: printers/fiscal.py:207
+msgid ""
+"It is not possible close the coupon since there are no payments defined."
+msgstr ""
+
+#: printers/fiscal.py:210
+#, python-format
+msgid ""
+"Isn't possible close the coupon since the payments total (%.2f) doesn't "
+"match the totalized value (%.2f)."
+msgstr ""
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/__init__.py trunk/stoqdrivers/__init__.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/__init__.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/__init__.py	2016-10-13 16:22:19.012318467 -0300
@@ -1,37 +1,36 @@
 # -*- Mode: Python; coding: utf-8 -*-
 # vi:si:et:sw=4:sts=4:ts=4
 
-##
-## Stoqdrivers
-## Copyright (C) 2005-2009 Async Open Source <http://www.async.com.br>
-## All rights reserved
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 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 General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-## USA.
-##
-## Author(s):   Henrique Romano <henrique@async.com.br>
-##              Johan Dahlin <henrique@async.com.br>
-##
-
-__version__ = (0, 9, 11)
-
-from kiwi.environ import Library
-
-__all__ = ["library"]
-
-library = Library("stoqdrivers", root="..")
-if library.uninstalled:
-    library.add_global_resource("conf", "stoqdrivers/conf")
-library.enable_translation()
+"""
+Stoqdrivers
+Copyright (C) 2005-2009 Async Open Source <http://www.async.com.br>
+All rights reserved
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+USA.
+
+Author(s):   Henrique Romano <henrique@async.com.br>
+             Johan Dahlin <henrique@async.com.br>
+"""
+
+__version__ = '5H1'
+
+# from kiwi.environ import Library
+#
+# __all__ = ["library"]
+# library = Library("stoqdrivers", root=".")
+# if library.uninstalled:
+#    library.add_global_resource("conf", "stoqdrivers/conf")
+# library.enable_translation()
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/locale/po/es_ES.po trunk/stoqdrivers/locale/po/es_ES.po
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/locale/po/es_ES.po	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/locale/po/es_ES.po	2016-10-13 16:22:19.016318394 -0300
@@ -0,0 +1,614 @@
+# # Copyright (C) 2005, 2008 Async Open Source <http://www.async.com.br>
+# # All rights reserved
+# #
+# # This program is free software; you can redistribute it and/or modify
+# # it under the terms of the GNU General Public License as published by
+# # the Free Software Foundation; either version 2 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 General Public License for more details.
+# #
+# # You should have received a copy of the GNU General Public License
+# # along with this program; if not, write to the Free Software
+# # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+# # USA.
+# #
+# # Author(s):   Rafael Villar Burke     <pachi@rvburke.com>
+# #
+# #
+msgid ""
+msgstr ""
+"Project-Id-Version: Stoqdrivers 0.3\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-01 13:14-0300\n"
+"PO-Revision-Date: 2007-01-26 12:57-0200\n"
+"Last-Translator: Lincol Molica <lincoln@async.com.br>\n"
+"Language-Team: Brazilian portuguese <pt_BR@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../stoqdrivers/configparser.py:77
+#, python-format
+msgid "Config file not found in: `%s', `%s' and `%s'"
+msgstr "Archivo de configuración no encontrado en: `%s', `%s' y `%s'"
+
+#: ../stoqdrivers/configparser.py:88 ../stoqdrivers/configparser.py:96
+#, python-format
+msgid "Invalid section: %s"
+msgstr "Sección Incorrecta: %s"
+
+#: ../stoqdrivers/configparser.py:90
+#, python-format
+msgid "%s does not have option: %s"
+msgstr "%s no tiene la opción: %s"
+
+#: ../stoqdrivers/constants.py:39
+msgid "Weight unit"
+msgstr "Unidad de Peso"
+
+#: ../stoqdrivers/constants.py:40
+msgid "Meters unit"
+msgstr "Unidad en metros"
+
+#: ../stoqdrivers/constants.py:41
+msgid "Liters unit"
+msgstr "Unidad en litros"
+
+#: ../stoqdrivers/constants.py:42
+msgid "Empty unit"
+msgstr "Sin Unidad"
+
+#: ../stoqdrivers/constants.py:43
+msgid "ICMS"
+msgstr "ICMS"
+
+#: ../stoqdrivers/constants.py:44
+msgid "Substitution"
+msgstr "Sustitución Tributaria"
+
+#: ../stoqdrivers/constants.py:45
+msgid "Exemption"
+msgstr "Deducción"
+
+#: ../stoqdrivers/constants.py:46
+msgid "No tax"
+msgstr "No Tributado"
+
+#: ../stoqdrivers/constants.py:47
+msgid "Service tax"
+msgstr "IVA (2)"
+
+#: ../stoqdrivers/constants.py:48
+msgid "Money Payment Method"
+msgstr "Forma de Pago Efectivo"
+
+#: ../stoqdrivers/constants.py:49
+msgid "Check Payment Method"
+msgstr "Forma de Pago Cheque"
+
+#: ../stoqdrivers/constants.py:50
+msgid "Debit card Payment Method"
+msgstr "Forma de Pago Tarjeta de Débito"
+
+#: ../stoqdrivers/constants.py:51
+msgid "Credit card Payment Method"
+msgstr "Forma de Pago Tarjeta de Crédito"
+
+#: ../stoqdrivers/constants.py:52
+msgid "Bill Payment Method"
+msgstr "Forma de Pago Pagaré"
+
+#: ../stoqdrivers/constants.py:53
+msgid "Financial Payment Method"
+msgstr "Forma de Pago Financiera"
+
+#: ../stoqdrivers/constants.py:54
+msgid "Gift certificate Payment Method"
+msgstr "Forma de Pago Vale de Compra"
+
+#: ../stoqdrivers/constants.py:55
+#, fuzzy
+msgid "Multiple Payment Method"
+msgstr "Forma de Pago Pagaré"
+
+#: ../stoqdrivers/base.py:78
+#, python-format
+msgid "There is no section named `%s'!"
+msgstr "No tiene sección llamada `%s'!"
+
+#: ../stoqdrivers/printers/fiscal.py:166
+msgid "You must totalize the coupon before add payments."
+msgstr "Debe totalizar el comprobante antes de añadir pagos."
+
+#: ../stoqdrivers/printers/fiscal.py:198
+msgid "You must totalize the coupon before closing it"
+msgstr "Debe totalizar el comprobante antes de cerrarlo"
+
+#: ../stoqdrivers/printers/fiscal.py:201
+msgid ""
+"It is not possible close the coupon since there are no payments defined."
+msgstr "No es posible cerrar el comprobante ya que no se han definido pagos"
+
+#: ../stoqdrivers/printers/fiscal.py:204
+#, python-format
+msgid ""
+"Isn't possible close the coupon since the payments total (%.2f) doesn't "
+"match the totalized value (%.2f)."
+msgstr ""
+"No fue posible cerrar el comprobante puesto que el total de pagos (%.2f) no "
+"coincide con el valor total (%.2f)."
+
+#: ../stoqdrivers/printers/virtual/Simple.py:111
+msgid "There is no coupon opened!"
+msgstr "¡No hay un comprobante abierto!"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:115
+msgid "There is a coupon already open"
+msgstr "Ya hay un comprobante abierto"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:139
+msgid "The coupon is already totalized, you can't add items anymore."
+msgstr "El comprobante ya está totalizado, no es posible añadir más artículos"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:149
+#, python-format
+msgid "There is no item with this ID (%d)"
+msgstr "No existe un artículo con ID (%d)"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:152
+msgid "The coupon is already totalized, you can't cancel items anymore."
+msgstr ""
+"El comprobante ya está totalizado, ya no es posible cancelar artículos."
+
+#: ../stoqdrivers/printers/virtual/Simple.py:166
+msgid "The coupon can't be totalized, since there is no items added"
+msgstr ""
+"El comprobante no puede ser totalizado, ya que no tiene artículos añadidos"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:169
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:191
+msgid "The coupon is already totalized"
+msgstr "El comprobante ya está totalizado"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:181
+msgid "Coupon totalized must be greater than zero!"
+msgstr "¡El total del comprobante debe ser mayor que cero!"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:190
+msgid "Isn't possible add payments to the coupon since it isn't totalized"
+msgstr "No es posible añadir pagos al comprobante si no ha sido totalizado"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:199
+msgid "Isn't possible close the coupon since it isn't totalized yet!"
+msgstr "¡No es posible cerrar el comprobante si no ha sido totalizado!"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:202
+msgid "Isn't possible close the coupon since there is no payments added."
+msgstr ""
+"No es posible cerrar el comprobante puesto que no se han añadido pagos."
+
+#: ../stoqdrivers/printers/virtual/Simple.py:205
+msgid "The payments total value doesn't match the totalized value."
+msgstr "El valor total de pagos no coincide con el valor totalizado."
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:154
+msgid "A Read X is pending"
+msgstr "Está pendiente una Lectura X"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:155
+msgid "The command is invalid"
+msgstr "El comando es incorrecto"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:158
+msgid "A coupon already exist"
+msgstr "Ya existe un comprobante"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:160
+msgid "Parameters invalid."
+msgstr "Parámetros incorrectos"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:162
+msgid "There is no coupon opened"
+msgstr "No hay un comprobante abierto"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:163
+msgid "Reduce Z already done."
+msgstr "Reducción Z ya hecha"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:165
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:168
+msgid "Printer clock isn't programmed yet."
+msgstr "El reloj de la impresora todavía no ha sido programado."
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:171
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:173
+msgid "Problem in fiscal printer"
+msgstr "Problema en la máquina registradora"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:176
+msgid "There is no items added to coupon"
+msgstr "No se han incluído artículos en el comprobante"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:178
+msgid "Out of paper"
+msgstr "Sin papel"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:179
+msgid "A Reduce Z is pending"
+msgstr "Reducción Z pendiente"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:182
+msgid "Invalid state for command execution"
+msgstr "Estado incorrecto para la ejecución del comando"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:185
+msgid "It is not possible cancel the item since it wasn't added yet"
+msgstr "No es posible cancelar el artículo ya que todavía no se ha añadido"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:194
+msgid "There is no such item to cancel in the coupon"
+msgstr "No existe el artículo que se desea eliminar en el comprobante"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:197
+#: ../stoqdrivers/printers/daruma/FS345.py:272
+msgid "There's no open document to cancel"
+msgstr "No existe un documento abierto que cancelar"
+
+#. if we catch InvalidState here, probably the printer is with
+#. a read X pending, so..
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:472
+msgid "A read X is pending."
+msgstr "Lectura X pendiente"
+
+#: ../stoqdrivers/printers/fiscnet/FiscNetECF.py:498
+msgid "Coupon already opened."
+msgstr "Comprobante ya abierto"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:108
+msgid "Fiscal memory has changed"
+msgstr "Memoria contable modificada"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:109
+msgid "No manufacture number"
+msgstr "Sin número de manufactura"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:110
+msgid "Print buffer is full"
+msgstr "El buffer de la impresora está lleno"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:111
+msgid "No item(s) to cancel found"
+msgstr "No se encontraron artículos que cancelar"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:112
+msgid "The requested command doesn't exist"
+msgstr "El comando solicitado no existe"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:116
+msgid "Fiscal EPROM disconnected"
+msgstr "Memoria contable desconectada"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:117
+msgid "Incorrect version of the basic software"
+msgstr "Versión incorrecta del software básico"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:118
+msgid "Error on the fiscal variables"
+msgstr "Error en las variables contables"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:119
+msgid "No cliche"
+msgstr "Sin cliche"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:120
+msgid "No company data. Has the printer been initialized?"
+msgstr "Sin datos de empresa. ¿Se inicializó la impresora?"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:126
+msgid "Invalid command"
+msgstr "Comando incorrecto"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:127
+msgid "Invalid command parameters"
+msgstr "Parámetros de comando incorrectos"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:129
+msgid "Fiscal memory without logotype"
+msgstr "Memoria contable sin logotipo"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:130
+msgid "Write error on the Fiscal Memory"
+msgstr "Error de escritura en la memoria contable"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:131
+msgid "Invalid state"
+msgstr "Estado incorrecto"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:133
+msgid "Printer is running out of paper"
+msgstr "A impresora está operando sin papel"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:134
+msgid "Printer hardware failure"
+msgstr "Error de hardware en la impresora"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:135
+#: ../stoqdrivers/printers/dataregis/EP375.py:481
+#: ../stoqdrivers/printers/daruma/FS345.py:218
+msgid "Pending Reduce Z"
+msgstr "Reducción Z pendiente"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:138
+msgid "Discount in subtotal with sale subjects to ICMS and ISSQN isn't allowed"
+msgstr ""
+"Los descuentos en el subtotal de ventas sujetas al IVA e IVA(2) no están "
+"permitidos"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:147
+#: ../stoqdrivers/printers/dataregis/EP375.py:483
+msgid "Pending Read X"
+msgstr "Lectura X pendiente"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:148
+msgid "Attempt of reduce Z with date previous than last"
+msgstr "Tentativa de reducción Z con fecha anterior a la última"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:152
+msgid "No more fiscal memory :("
+msgstr "No existe más memoria contable :("
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:153
+#: ../stoqdrivers/printers/dataregis/EP375.py:602
+msgid "Reduce Z already done"
+msgstr "Reducción Z ya hecha"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:529
+msgid "You have specified an invalid item id to cancel!"
+msgstr "¡Se especificó un código de artículo incorrecto para su cancelación!"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:220
+msgid "Mechanical failure"
+msgstr "Error Mecánico"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:222
+msgid "Not properly authenticated"
+msgstr "Autentificación incorrectamente"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:224
+msgid "No paper"
+msgstr "Sin papel"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:226
+msgid "Offline"
+msgstr "Desconectado"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:240
+msgid "readZ is already emitted"
+msgstr "La lectura Z ya ha sido emitida"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:268
+msgid "Document is already open"
+msgstr "Documento ya abierto"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:270
+#: ../stoqdrivers/printers/daruma/FS345.py:346
+msgid "Coupon is not open"
+msgstr "El comprobante no está abierto"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:275
+msgid "There is no such item in the coupon"
+msgstr "No existe dicho artículo en el comprobante"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:280
+#, fuzzy, python-format
+msgid "Printer error %s: No paper"
+msgstr "Impresora en estado de error"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:291
+msgid "Required field is blank"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:414
+msgid "Coupon already open"
+msgstr "Documento ya abierto"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:488
+#, fuzzy
+msgid "No client"
+msgstr "Sin cliche"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:489
+msgid "No document"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:490
+msgid "No address"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:505
+msgid "It is not possible to close the coupon"
+msgstr "No es posible cerrar el comprobante"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:545
+#, python-format
+msgid "Receipt for method %s is not configured"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:147
+msgid "Printer is out of paper"
+msgstr "Impresora sin papel"
+
+#. 64: (AlmostOutofPaper(_("Printer almost out of paper"))),
+#: ../stoqdrivers/printers/bematech/MP25.py:149
+msgid "Printer clock error"
+msgstr "Error en el reloj de la impresora"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:150
+msgid "Printer in error state"
+msgstr "Impresora en estado de error"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:151
+msgid "First data value in CMD is not ESC (1BH)"
+msgstr "El primer valor en CMD no es ESC (1BH)"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:152
+msgid "Nonexistent command"
+msgstr "Comando Inexistente"
+
+#. 2: (CouponOpenError(_("Printer has a coupon currently open"))),
+#: ../stoqdrivers/printers/bematech/MP25.py:154
+msgid "Invalid number of parameters"
+msgstr "Número incorrecto de parámetros"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:157
+msgid "Invalid CMD parameter"
+msgstr "Parámetro CMD incorrecto"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:158
+msgid "Fiscal memory is full"
+msgstr "Memoria contable llena"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:159
+msgid "Error in CMOS memory"
+msgstr "Error en la memoria CMOS"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:160
+msgid "Given tax is not programmed on the printer"
+msgstr "El impuesto proporcionado no está programada en la impresora"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:161
+msgid "No available tax slot"
+msgstr "No hay espacio para el impuesto"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:162
+msgid "The item wasn't added in the coupon or can't be cancelled"
+msgstr "El artículo no se añadió al comprobante o no se puede cancelar"
+
+#. 7: (CouponOpenError(_("Coupon already Open"))),
+#. 8: (CouponNotOpenError(_("Coupon is closed"))),
+#: ../stoqdrivers/printers/bematech/MP25.py:174
+msgid "Printer is offline"
+msgstr "Impresora desconectada"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:175
+#, fuzzy
+msgid "Surcharge or discount greater than coupon totalvalue"
+msgstr "Recargo o descuento mayor que el valor total del comprobante"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:177
+msgid "Coupon with no items"
+msgstr "Comprobante sin artículos"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:178
+msgid "Payment method not recognized"
+msgstr "Método de pago no reconocido"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:179
+#, fuzzy
+msgid ""
+"Isn't possible add more payments sincethe coupon total value already was "
+"reached"
+msgstr ""
+"No es posible añadir más pagos puesto que ya se alcanzó el valor total del "
+"comprobante"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:182
+msgid "Coupon isn't totalized yet"
+msgstr "El comprobante todavía no se ha totalizado"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:183
+msgid "Printer not initialized"
+msgstr "Impresora no inicializada"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:184
+msgid "Printer without serial number"
+msgstr "Impresora sin número de serie"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:185
+msgid "Invalid start date"
+msgstr "Fecha de inicio incorrecta"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:186
+msgid "Invalid final date"
+msgstr "Fecha de término incorrecta"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:187
+msgid "Sale with null value"
+msgstr "Venta con valor nulo"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:188
+#, fuzzy
+msgid "Surcharge or discount greater than itemvalue"
+msgstr "Recargo o descuento mayor que el valor del artículo"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:190
+#: ../stoqdrivers/printers/bematech/MP25.py:199
+msgid "Invalid date"
+msgstr "Fecha Incorrecta"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:191
+msgid "Item doesn't exists or already was cancelled"
+msgstr "Artículo inexistente o ya cancelado"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:192
+msgid "Surcharge greater than item value"
+msgstr "Recargo mayor que el valor del artículo"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:193
+msgid "Discount greater than item value"
+msgstr "Descuento mayor que el valor del artículo"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:194
+msgid "Invalid month"
+msgstr "Mes incorrecto"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:195
+msgid "Coupon already totalized"
+msgstr "Comprobante ya totalizado"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:196
+msgid "Coupon not totalized yet"
+msgstr "Comprobante no totalizado todavía"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:197
+msgid "Surcharge on subtotal already effected"
+msgstr "Recargo al subtotal ya efectuado"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:198
+msgid "Discount on subtotal already effected"
+msgstr "Descuento al subtotal ya efectuado"
+
+#~ msgid "Peripheral is not connected to AUX"
+#~ msgstr "El periférico no está conectado al puerto"
+
+#~ msgid "readX is not emitted yet"
+#~ msgstr "La lectura X aún no ha sido emitida"
+
+#~ msgid "Printer almost out of paper"
+#~ msgstr "Impresora casi sin papel"
+
+#~ msgid "Printer has a coupon currently open"
+#~ msgstr "La registradora ya tiene un comprobante abierto"
+
+#~ msgid "Owner data (CGC/IE) not programmed on the printer"
+#~ msgstr ""
+#~ "Los datos de la empresa (CGC/IE) no están programados en la impresora"
+
+#~ msgid "Command not executed"
+#~ msgstr "Comando no ejecutado"
+
+#~ msgid "Coupon already Open"
+#~ msgstr "Comprobante ya abierto"
+
+#~ msgid "Coupon is closed"
+#~ msgstr "El comprobante está cerrado"
+
+#~ msgid "ticket already open"
+#~ msgstr "Comprobante ya abierto"
+
+#~ msgid "ticket already Open"
+#~ msgstr "Comprobante ya abierto"
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/locale/po/POTFILES.list trunk/stoqdrivers/locale/po/POTFILES.list
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/locale/po/POTFILES.list	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/locale/po/POTFILES.list	2016-10-13 16:22:19.012318467 -0300
@@ -0,0 +1 @@
+recursive-include stoqdrivers *.py
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/locale/po/pt_BR.po trunk/stoqdrivers/locale/po/pt_BR.po
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/locale/po/pt_BR.po	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/locale/po/pt_BR.po	2016-10-13 16:22:19.016318394 -0300
@@ -0,0 +1,611 @@
+# # Copyright (C) 2005, 2008 Async Open Source <http://www.async.com.br>
+# # All rights reserved
+# #
+# # This program is free software; you can redistribute it and/or modify
+# # it under the terms of the GNU General Public License as published by
+# # the Free Software Foundation; either version 2 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 General Public License for more details.
+# #
+# # You should have received a copy of the GNU General Public License
+# # along with this program; if not, write to the Free Software
+# # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+# # USA.
+# #
+# # Author(s):   Bruno Rafael Garcia     <brg@async.com.br>
+# #      Evandro Vale Miquelito  <evandro@async.com.br>
+# #      Silvio Rangel <silvio@async.com.br>
+# #
+msgid ""
+msgstr ""
+"Project-Id-Version: Stoqdrivers 0.3\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-01 13:14-0300\n"
+"PO-Revision-Date: 2011-05-26 08:34-0300\n"
+"Last-Translator: \n"
+"Language-Team: Brazilian portuguese <pt_BR@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../stoqdrivers/configparser.py:77
+#, python-format
+msgid "Config file not found in: `%s', `%s' and `%s'"
+msgstr "Arquivo de configuração não encontrado em: `%s', `%s' e `%s'"
+
+#: ../stoqdrivers/configparser.py:88
+#: ../stoqdrivers/configparser.py:96
+#, python-format
+msgid "Invalid section: %s"
+msgstr "Seção Inválida: %s"
+
+#: ../stoqdrivers/configparser.py:90
+#, python-format
+msgid "%s does not have option: %s"
+msgstr "%s não tem a opção: %s"
+
+#: ../stoqdrivers/constants.py:39
+msgid "Weight unit"
+msgstr "Unidade de Peso"
+
+#: ../stoqdrivers/constants.py:40
+msgid "Meters unit"
+msgstr "Unidade em metros"
+
+#: ../stoqdrivers/constants.py:41
+msgid "Liters unit"
+msgstr "Unidade em litros"
+
+#: ../stoqdrivers/constants.py:42
+msgid "Empty unit"
+msgstr "Sem Unidade"
+
+#: ../stoqdrivers/constants.py:43
+msgid "ICMS"
+msgstr "ICMS"
+
+#: ../stoqdrivers/constants.py:44
+msgid "Substitution"
+msgstr "Substituição Tributária"
+
+#: ../stoqdrivers/constants.py:45
+msgid "Exemption"
+msgstr "Isenção"
+
+#: ../stoqdrivers/constants.py:46
+msgid "No tax"
+msgstr "Não Tributado"
+
+#: ../stoqdrivers/constants.py:47
+msgid "Service tax"
+msgstr "ISS"
+
+#: ../stoqdrivers/constants.py:48
+msgid "Money Payment Method"
+msgstr "Forma de Pagamento Dinheiro"
+
+#: ../stoqdrivers/constants.py:49
+msgid "Check Payment Method"
+msgstr "Forma de Pagamento Cheque"
+
+#: ../stoqdrivers/constants.py:50
+msgid "Debit card Payment Method"
+msgstr "Forma de Pagamento Cartão de Débito"
+
+#: ../stoqdrivers/constants.py:51
+msgid "Credit card Payment Method"
+msgstr "Forma de Pagamento Cartão de Crédito"
+
+#: ../stoqdrivers/constants.py:52
+msgid "Bill Payment Method"
+msgstr "Forma de Pagamento Boletos"
+
+#: ../stoqdrivers/constants.py:53
+msgid "Financial Payment Method"
+msgstr "Forma de Pagamento Financeira"
+
+#: ../stoqdrivers/constants.py:54
+msgid "Gift certificate Payment Method"
+msgstr "Forma de Pagamento Vale Compras"
+
+#: ../stoqdrivers/constants.py:55
+msgid "Multiple Payment Method"
+msgstr "Multiplos Métodos de Pagamentos"
+
+#: ../stoqdrivers/base.py:78
+#, python-format
+msgid "There is no section named `%s'!"
+msgstr "Não há seção com o nome `%s'!"
+
+#: ../stoqdrivers/printers/fiscal.py:166
+msgid "You must totalize the coupon before add payments."
+msgstr "Você deve totalizar o cupom antes de adicionar pagamentos"
+
+#: ../stoqdrivers/printers/fiscal.py:198
+msgid "You must totalize the coupon before closing it"
+msgstr "Você deve totalizar o cupom antes de fechá-lo"
+
+#: ../stoqdrivers/printers/fiscal.py:201
+msgid "It is not possible close the coupon since there are no payments defined."
+msgstr "Não é possível fechar o cupom se não houver pagamentos definidos"
+
+#: ../stoqdrivers/printers/fiscal.py:204
+#, python-format
+msgid "Isn't possible close the coupon since the payments total (%.2f) doesn't match the totalized value (%.2f)."
+msgstr "Não foi possível fechar o cupom porque o total de pagamentos (%.2f) não confere com o valor totalizado (%.2f)."
+
+#: ../stoqdrivers/printers/virtual/Simple.py:111
+msgid "There is no coupon opened!"
+msgstr "Não há cupom aberto!"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:115
+msgid "There is a coupon already open"
+msgstr "Já existe um cupom aberto"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:139
+msgid "The coupon is already totalized, you can't add items anymore."
+msgstr "O cupom já está totalizado, não é possível adicionar mais itens"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:149
+#, python-format
+msgid "There is no item with this ID (%d)"
+msgstr "Não há item com o ID (%d)"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:152
+msgid "The coupon is already totalized, you can't cancel items anymore."
+msgstr "O cupom já está totalizado, não é mais possível cancelar itens."
+
+#: ../stoqdrivers/printers/virtual/Simple.py:166
+msgid "The coupon can't be totalized, since there is no items added"
+msgstr "O cupom não pode ser totalizado, desde que não existem itens adicionados"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:169
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:191
+msgid "The coupon is already totalized"
+msgstr "O cupom já está totalizado"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:181
+msgid "Coupon totalized must be greater than zero!"
+msgstr "Cupom totalizado deve ser maior que zero!"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:190
+msgid "Isn't possible add payments to the coupon since it isn't totalized"
+msgstr "Não é possível adicionar pagamentos ao cupom se ele não foi totalizado"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:199
+msgid "Isn't possible close the coupon since it isn't totalized yet!"
+msgstr "Não é possível fechar o cupom se ele ainda não foi totalizado!"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:202
+msgid "Isn't possible close the coupon since there is no payments added."
+msgstr "Não é possível fechar o cupom se não houver pagamentos adicionados"
+
+#: ../stoqdrivers/printers/virtual/Simple.py:205
+msgid "The payments total value doesn't match the totalized value."
+msgstr "O valor total de pagamentos não confere com o valor totalizado"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:154
+msgid "A Read X is pending"
+msgstr "Uma Leitura X está pendente"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:155
+msgid "The command is invalid"
+msgstr "O comando é inválido"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:158
+msgid "A coupon already exist"
+msgstr "O cupom já existe"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:160
+msgid "Parameters invalid."
+msgstr "Parâmetros inválido"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:162
+msgid "There is no coupon opened"
+msgstr "Não há cupom aberto"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:163
+msgid "Reduce Z already done."
+msgstr "Redução Z já feita"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:165
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:168
+msgid "Printer clock isn't programmed yet."
+msgstr "Relógio da impressora ainda não foi programadoi"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:171
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:173
+msgid "Problem in fiscal printer"
+msgstr "Problema na impressora fiscal"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:176
+msgid "There is no items added to coupon"
+msgstr "Não há itens adicionados ao cupom"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:178
+msgid "Out of paper"
+msgstr "Sem papel"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:179
+msgid "A Reduce Z is pending"
+msgstr "Redução Z pendente"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:182
+msgid "Invalid state for command execution"
+msgstr "Estado inválido para execução do comando"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:185
+msgid "It is not possible cancel the item since it wasn't added yet"
+msgstr "Não é possível cancelar o item se ele ainda não foi adicionado"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:194
+msgid "There is no such item to cancel in the coupon"
+msgstr "Não há tal item a cancelar no presente cupom"
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:197
+#: ../stoqdrivers/printers/daruma/FS345.py:272
+msgid "There's no open document to cancel"
+msgstr "Não há documento aberto para cancelar"
+
+#. if we catch InvalidState here, probably the printer is with
+#. a read X pending, so..
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:472
+msgid "A read X is pending."
+msgstr "Leitura X pendente"
+
+#: ../stoqdrivers/printers/fiscnet/FiscNetECF.py:498
+msgid "Coupon already opened."
+msgstr "Cupom já aberto"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:108
+msgid "Fiscal memory has changed"
+msgstr "Memória fiscal modificada"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:109
+msgid "No manufacture number"
+msgstr "Sem número de manufatura"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:110
+msgid "Print buffer is full"
+msgstr "O buffer da impressora está cheio"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:111
+msgid "No item(s) to cancel found"
+msgstr "Item(s) para cancelamento não encontrado"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:112
+msgid "The requested command doesn't exist"
+msgstr "O comando solicitado não existe"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:116
+msgid "Fiscal EPROM disconnected"
+msgstr "Memória fiscal desconectada"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:117
+msgid "Incorrect version of the basic software"
+msgstr "Versão incorreta do software básico"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:118
+msgid "Error on the fiscal variables"
+msgstr "Erro nas variáveis fiscais"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:119
+msgid "No cliche"
+msgstr "Sem cliche"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:120
+msgid "No company data. Has the printer been initialized?"
+msgstr "Sem dados de empresa. A impressora foi inicializada?"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:126
+msgid "Invalid command"
+msgstr "Comando inválido"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:127
+msgid "Invalid command parameters"
+msgstr "Parâmetros de comando inválidos"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:129
+msgid "Fiscal memory without logotype"
+msgstr "Memória fiscal sem logotipo"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:130
+msgid "Write error on the Fiscal Memory"
+msgstr "Escrever erro na memória fiscal"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:131
+msgid "Invalid state"
+msgstr "Estado inválido"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:133
+msgid "Printer is running out of paper"
+msgstr "A impressora está operando sem papel"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:134
+msgid "Printer hardware failure"
+msgstr "Falha de hardware na impressora"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:135
+#: ../stoqdrivers/printers/dataregis/EP375.py:481
+#: ../stoqdrivers/printers/daruma/FS345.py:218
+msgid "Pending Reduce Z"
+msgstr "Redução Z pendente"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:138
+msgid "Discount in subtotal with sale subjects to ICMS and ISSQN isn't allowed"
+msgstr "Desconto em subtotal com venda sujeita à ICMS e ISSQN não é permitido"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:147
+#: ../stoqdrivers/printers/dataregis/EP375.py:483
+msgid "Pending Read X"
+msgstr "Leitura X pendente"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:148
+msgid "Attempt of reduce Z with date previous than last"
+msgstr "Tentativa de redução Z com data anterior à última"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:152
+msgid "No more fiscal memory :("
+msgstr "Não há mais memória fiscal :("
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:153
+#: ../stoqdrivers/printers/dataregis/EP375.py:602
+msgid "Reduce Z already done"
+msgstr "Redução Z já está feita"
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:529
+msgid "You have specified an invalid item id to cancel!"
+msgstr "Foi especificado um código de item inválido para cancelamento"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:220
+msgid "Mechanical failure"
+msgstr "Falha Mecânica"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:222
+msgid "Not properly authenticated"
+msgstr "Não autenticado corretamente"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:224
+msgid "No paper"
+msgstr "Sem papel"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:226
+msgid "Offline"
+msgstr "Desconectado"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:240
+msgid "readZ is already emitted"
+msgstr "Leitura X ainda já foi emitida"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:268
+msgid "Document is already open"
+msgstr "Documento já está aberto"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:270
+#: ../stoqdrivers/printers/daruma/FS345.py:346
+msgid "Coupon is not open"
+msgstr "O Cupom não está aberto"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:275
+msgid "There is no such item in the coupon"
+msgstr "Este item não se encontra no cupom"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:280
+#, python-format
+msgid "Printer error %s: No paper"
+msgstr "Erro na Impressora %s: Sem papel"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:291
+msgid "Required field is blank"
+msgstr "Campo requerido está em branco"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:414
+msgid "Coupon already open"
+msgstr "Cupom já aberto"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:488
+msgid "No client"
+msgstr "Sem cliche"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:489
+msgid "No document"
+msgstr "Sem documento"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:490
+msgid "No address"
+msgstr "Sem endereço"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:505
+msgid "It is not possible to close the coupon"
+msgstr "Não é possível fechar o cupom"
+
+#: ../stoqdrivers/printers/daruma/FS345.py:545
+#, python-format
+msgid "Receipt for method %s is not configured"
+msgstr "Recibo para o método %s não está configurado"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:147
+msgid "Printer is out of paper"
+msgstr "Impressora sem papel"
+
+#. 64: (AlmostOutofPaper(_("Printer almost out of paper"))),
+#: ../stoqdrivers/printers/bematech/MP25.py:149
+msgid "Printer clock error"
+msgstr "Erro no relógio da impressora"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:150
+msgid "Printer in error state"
+msgstr "Impressora em estado de erro"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:151
+msgid "First data value in CMD is not ESC (1BH)"
+msgstr "Primeiro valor em CMD não é ESC (1BH)"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:152
+msgid "Nonexistent command"
+msgstr "Comando Inexistente"
+
+#. 2: (CouponOpenError(_("Printer has a coupon currently open"))),
+#: ../stoqdrivers/printers/bematech/MP25.py:154
+msgid "Invalid number of parameters"
+msgstr "Número inválido de parâmetros"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:157
+msgid "Invalid CMD parameter"
+msgstr "Parâmetro CMD inválido"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:158
+msgid "Fiscal memory is full"
+msgstr "Memória fiscal está cheia"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:159
+msgid "Error in CMOS memory"
+msgstr "Erro na memória CMOS"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:160
+msgid "Given tax is not programmed on the printer"
+msgstr "A taxa fornecida não está programada na impressora"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:161
+msgid "No available tax slot"
+msgstr "Não há espaço para taxa"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:162
+msgid "The item wasn't added in the coupon or can't be cancelled"
+msgstr "O item não foi adicionado ao cupom ou não pode ser cancelado"
+
+#. 7: (CouponOpenError(_("Coupon already Open"))),
+#. 8: (CouponNotOpenError(_("Coupon is closed"))),
+#: ../stoqdrivers/printers/bematech/MP25.py:174
+msgid "Printer is offline"
+msgstr "Impressora desconectada"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:175
+msgid "Surcharge or discount greater than coupon totalvalue"
+msgstr "Acréscimo ou desconto maior que o valor total do cupom"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:177
+msgid "Coupon with no items"
+msgstr "Cupom sem itens"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:178
+msgid "Payment method not recognized"
+msgstr "Método de pagamento não reconhecido"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:179
+msgid "Isn't possible add more payments sincethe coupon total value already was reached"
+msgstr "Não é possível adicionar mais pagamentos pois o valor total do cupom já foi atingido"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:182
+msgid "Coupon isn't totalized yet"
+msgstr "O cupom ainda não foi totalizado"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:183
+msgid "Printer not initialized"
+msgstr "Impressora não inicializada"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:184
+msgid "Printer without serial number"
+msgstr "Impressora sem número serial"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:185
+msgid "Invalid start date"
+msgstr "Data de inicio inválida"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:186
+msgid "Invalid final date"
+msgstr "Data de término inválida"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:187
+msgid "Sale with null value"
+msgstr "Venda com valor nulo"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:188
+msgid "Surcharge or discount greater than itemvalue"
+msgstr "Acréscimo ou desconto maior que o valor do item"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:190
+#: ../stoqdrivers/printers/bematech/MP25.py:199
+msgid "Invalid date"
+msgstr "Data Inválida"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:191
+msgid "Item doesn't exists or already was cancelled"
+msgstr "Item inexistente ou já cancelado"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:192
+msgid "Surcharge greater than item value"
+msgstr "Acréscimo maior que o valor do item"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:193
+msgid "Discount greater than item value"
+msgstr "Desconto maior que o valor do item"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:194
+msgid "Invalid month"
+msgstr "Mês inválido"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:195
+msgid "Coupon already totalized"
+msgstr "Cupom já totalizado"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:196
+msgid "Coupon not totalized yet"
+msgstr "Cupom não totalizado ainda"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:197
+msgid "Surcharge on subtotal already effected"
+msgstr "Acréscimo ao sub-total já efetuado"
+
+#: ../stoqdrivers/printers/bematech/MP25.py:198
+msgid "Discount on subtotal already effected"
+msgstr "Desconto ao subtotal já efetuado"
+
+#~ msgid "Peripheral is not connected to AUX"
+#~ msgstr "Periférico não está conectado à porta"
+
+#~ msgid "readX is not emitted yet"
+#~ msgstr "Leitura X ainda não foi emitida"
+
+#~ msgid "Printer almost out of paper"
+#~ msgstr "Impressora quase sem papel"
+
+#~ msgid "Printer has a coupon currently open"
+#~ msgstr "A impressora tem um cupom aberto atualmente"
+
+#~ msgid "Owner data (CGC/IE) not programmed on the printer"
+#~ msgstr "Dados da empresa (CGC/IE) não programados na impressora"
+
+#~ msgid "Command not executed"
+#~ msgstr "Comando não executado"
+
+#~ msgid "Coupon already Open"
+#~ msgstr "Cupom já aberto"
+
+#~ msgid "Coupon is closed"
+#~ msgstr "Cupom está fechado"
+
+#~ msgid "IOF tax"
+#~ msgstr "Imposto de IOF"
+
+#~ msgid ""
+#~ ">>> Given file object in read-only mode! Using standard output to write "
+#~ "logs!"
+#~ msgstr ""
+#~ ">>> O arquivo está no modo somente-leitura! Usando saída padrào para logs!"
+
+#~ msgid ""
+#~ ">>> Couldn't access specified file! Using standard output to write logs!"
+#~ msgstr ""
+#~ ">>> Não foi possível acessar o arquivo especificado! Usando a saída "
+#~ "padrão para gravar logs"
+
+#~ msgid "There is a coupon opened"
+#~ msgstr "Há um cupom aberto"
+
+#~ msgid "Pending read X"
+#~ msgstr "Leitura X pendente"
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/locale/po/stoqdrivers.pot trunk/stoqdrivers/locale/po/stoqdrivers.pot
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/locale/po/stoqdrivers.pot	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/locale/po/stoqdrivers.pot	2016-10-13 16:22:19.016318394 -0300
@@ -0,0 +1,554 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-01 13:14-0300\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ../stoqdrivers/configparser.py:77
+#, python-format
+msgid "Config file not found in: `%s', `%s' and `%s'"
+msgstr ""
+
+#: ../stoqdrivers/configparser.py:88 ../stoqdrivers/configparser.py:96
+#, python-format
+msgid "Invalid section: %s"
+msgstr ""
+
+#: ../stoqdrivers/configparser.py:90
+#, python-format
+msgid "%s does not have option: %s"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:39
+msgid "Weight unit"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:40
+msgid "Meters unit"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:41
+msgid "Liters unit"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:42
+msgid "Empty unit"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:43
+msgid "ICMS"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:44
+msgid "Substitution"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:45
+msgid "Exemption"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:46
+msgid "No tax"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:47
+msgid "Service tax"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:48
+msgid "Money Payment Method"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:49
+msgid "Check Payment Method"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:50
+msgid "Debit card Payment Method"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:51
+msgid "Credit card Payment Method"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:52
+msgid "Bill Payment Method"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:53
+msgid "Financial Payment Method"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:54
+msgid "Gift certificate Payment Method"
+msgstr ""
+
+#: ../stoqdrivers/constants.py:55
+msgid "Multiple Payment Method"
+msgstr ""
+
+#: ../stoqdrivers/base.py:78
+#, python-format
+msgid "There is no section named `%s'!"
+msgstr ""
+
+#: ../stoqdrivers/printers/fiscal.py:166
+msgid "You must totalize the coupon before add payments."
+msgstr ""
+
+#: ../stoqdrivers/printers/fiscal.py:198
+msgid "You must totalize the coupon before closing it"
+msgstr ""
+
+#: ../stoqdrivers/printers/fiscal.py:201
+msgid ""
+"It is not possible close the coupon since there are no payments defined."
+msgstr ""
+
+#: ../stoqdrivers/printers/fiscal.py:204
+#, python-format
+msgid ""
+"Isn't possible close the coupon since the payments total (%.2f) doesn't "
+"match the totalized value (%.2f)."
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:111
+msgid "There is no coupon opened!"
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:115
+msgid "There is a coupon already open"
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:139
+msgid "The coupon is already totalized, you can't add items anymore."
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:149
+#, python-format
+msgid "There is no item with this ID (%d)"
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:152
+msgid "The coupon is already totalized, you can't cancel items anymore."
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:166
+msgid "The coupon can't be totalized, since there is no items added"
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:169
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:191
+msgid "The coupon is already totalized"
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:181
+msgid "Coupon totalized must be greater than zero!"
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:190
+msgid "Isn't possible add payments to the coupon since it isn't totalized"
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:199
+msgid "Isn't possible close the coupon since it isn't totalized yet!"
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:202
+msgid "Isn't possible close the coupon since there is no payments added."
+msgstr ""
+
+#: ../stoqdrivers/printers/virtual/Simple.py:205
+msgid "The payments total value doesn't match the totalized value."
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:154
+msgid "A Read X is pending"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:155
+msgid "The command is invalid"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:158
+msgid "A coupon already exist"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:160
+msgid "Parameters invalid."
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:162
+msgid "There is no coupon opened"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:163
+msgid "Reduce Z already done."
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:165
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:168
+msgid "Printer clock isn't programmed yet."
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:171
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:173
+msgid "Problem in fiscal printer"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:176
+msgid "There is no items added to coupon"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:178
+msgid "Out of paper"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:179
+msgid "A Reduce Z is pending"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:182
+msgid "Invalid state for command execution"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:185
+msgid "It is not possible cancel the item since it wasn't added yet"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:194
+msgid "There is no such item to cancel in the coupon"
+msgstr ""
+
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:197
+#: ../stoqdrivers/printers/daruma/FS345.py:272
+msgid "There's no open document to cancel"
+msgstr ""
+
+#. if we catch InvalidState here, probably the printer is with
+#. a read X pending, so..
+#: ../stoqdrivers/printers/sweda/IFS9000I.py:472
+msgid "A read X is pending."
+msgstr ""
+
+#: ../stoqdrivers/printers/fiscnet/FiscNetECF.py:498
+msgid "Coupon already opened."
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:108
+msgid "Fiscal memory has changed"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:109
+msgid "No manufacture number"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:110
+msgid "Print buffer is full"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:111
+msgid "No item(s) to cancel found"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:112
+msgid "The requested command doesn't exist"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:116
+msgid "Fiscal EPROM disconnected"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:117
+msgid "Incorrect version of the basic software"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:118
+msgid "Error on the fiscal variables"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:119
+msgid "No cliche"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:120
+msgid "No company data. Has the printer been initialized?"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:126
+msgid "Invalid command"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:127
+msgid "Invalid command parameters"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:129
+msgid "Fiscal memory without logotype"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:130
+msgid "Write error on the Fiscal Memory"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:131
+msgid "Invalid state"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:133
+msgid "Printer is running out of paper"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:134
+msgid "Printer hardware failure"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:135
+#: ../stoqdrivers/printers/dataregis/EP375.py:481
+#: ../stoqdrivers/printers/daruma/FS345.py:218
+msgid "Pending Reduce Z"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:138
+msgid "Discount in subtotal with sale subjects to ICMS and ISSQN isn't allowed"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:147
+#: ../stoqdrivers/printers/dataregis/EP375.py:483
+msgid "Pending Read X"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:148
+msgid "Attempt of reduce Z with date previous than last"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:152
+msgid "No more fiscal memory :("
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:153
+#: ../stoqdrivers/printers/dataregis/EP375.py:602
+msgid "Reduce Z already done"
+msgstr ""
+
+#: ../stoqdrivers/printers/dataregis/EP375.py:529
+msgid "You have specified an invalid item id to cancel!"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:220
+msgid "Mechanical failure"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:222
+msgid "Not properly authenticated"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:224
+msgid "No paper"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:226
+msgid "Offline"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:240
+msgid "readZ is already emitted"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:268
+msgid "Document is already open"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:270
+#: ../stoqdrivers/printers/daruma/FS345.py:346
+msgid "Coupon is not open"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:275
+msgid "There is no such item in the coupon"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:280
+#, python-format
+msgid "Printer error %s: No paper"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:291
+msgid "Required field is blank"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:414
+msgid "Coupon already open"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:488
+msgid "No client"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:489
+msgid "No document"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:490
+msgid "No address"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:505
+msgid "It is not possible to close the coupon"
+msgstr ""
+
+#: ../stoqdrivers/printers/daruma/FS345.py:545
+#, python-format
+msgid "Receipt for method %s is not configured"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:147
+msgid "Printer is out of paper"
+msgstr ""
+
+#. 64: (AlmostOutofPaper(_("Printer almost out of paper"))),
+#: ../stoqdrivers/printers/bematech/MP25.py:149
+msgid "Printer clock error"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:150
+msgid "Printer in error state"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:151
+msgid "First data value in CMD is not ESC (1BH)"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:152
+msgid "Nonexistent command"
+msgstr ""
+
+#. 2: (CouponOpenError(_("Printer has a coupon currently open"))),
+#: ../stoqdrivers/printers/bematech/MP25.py:154
+msgid "Invalid number of parameters"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:157
+msgid "Invalid CMD parameter"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:158
+msgid "Fiscal memory is full"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:159
+msgid "Error in CMOS memory"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:160
+msgid "Given tax is not programmed on the printer"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:161
+msgid "No available tax slot"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:162
+msgid "The item wasn't added in the coupon or can't be cancelled"
+msgstr ""
+
+#. 7: (CouponOpenError(_("Coupon already Open"))),
+#. 8: (CouponNotOpenError(_("Coupon is closed"))),
+#: ../stoqdrivers/printers/bematech/MP25.py:174
+msgid "Printer is offline"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:175
+msgid "Surcharge or discount greater than coupon totalvalue"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:177
+msgid "Coupon with no items"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:178
+msgid "Payment method not recognized"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:179
+msgid ""
+"Isn't possible add more payments sincethe coupon total value already was "
+"reached"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:182
+msgid "Coupon isn't totalized yet"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:183
+msgid "Printer not initialized"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:184
+msgid "Printer without serial number"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:185
+msgid "Invalid start date"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:186
+msgid "Invalid final date"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:187
+msgid "Sale with null value"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:188
+msgid "Surcharge or discount greater than itemvalue"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:190
+#: ../stoqdrivers/printers/bematech/MP25.py:199
+msgid "Invalid date"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:191
+msgid "Item doesn't exists or already was cancelled"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:192
+msgid "Surcharge greater than item value"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:193
+msgid "Discount greater than item value"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:194
+msgid "Invalid month"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:195
+msgid "Coupon already totalized"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:196
+msgid "Coupon not totalized yet"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:197
+msgid "Surcharge on subtotal already effected"
+msgstr ""
+
+#: ../stoqdrivers/printers/bematech/MP25.py:198
+msgid "Discount on subtotal already effected"
+msgstr ""
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/logs/__init__.py trunk/stoqdrivers/logs/__init__.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/logs/__init__.py	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/logs/__init__.py	2016-10-13 16:22:19.188315288 -0300
@@ -0,0 +1,3 @@
+#!/usr/bin/python
+# -*- encoding: utf-8 -*-
+
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/logs/log.py trunk/stoqdrivers/logs/log.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/logs/log.py	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/logs/log.py	2016-10-13 16:22:19.188315288 -0300
@@ -0,0 +1,57 @@
+#!/usr/bin/python
+# -*- encoding: utf-8 -*-
+
+import os
+import logging
+from datetime import datetime
+
+root_path = '/usr/local/sigpdv/logs/'
+LOG_FILE = os.path.join(root_path, 'stoqdrivers.%s.log' % datetime.now().strftime('%d-%m-%Y'))
+# traced = TraceContext(logfile=LOG_FILE)
+
+
+class StoqdriversLog(object):
+    def __init__(self, stdout=True):
+        # Criar logger
+        logger = logging.getLogger('stoqdrivers')
+        logger.setLevel(logging.DEBUG)
+        logger.propagate = 0
+
+        # Adicionar arquivo logger
+        file_handler = logging.FileHandler(LOG_FILE)
+
+        # Criar formato e setar formato do cabeçalho
+        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
+        file_handler.setFormatter(formatter)
+
+        # Adicionar para gravar no logger
+        logger.addHandler(file_handler)
+
+        self.log = logger
+        self.stdout = stdout
+
+    def debug(self, text):
+        self.log.setLevel(logging.DEBUG)
+        self.log.debug(text)
+
+    def info(self, text):
+        self.log.setLevel(logging.INFO)
+        self.log.info(text)
+
+    def warning(self, text):
+        self.log.setLevel(logging.WARNING)
+        self.log.warn(text)
+
+    def error(self, text):
+        self.log.setLevel(logging.ERROR)
+        self.log.error(text)
+
+    def critical(self, text):
+        self.log.setLevel(logging.CRITICAL)
+        self.log.critical(text)
+
+
+traced = StoqdriversLog()
+
+if __name__ == '__main__':
+    traced.info(LOG_FILE)
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/bematech/MP20.py trunk/stoqdrivers/printers/bematech/MP20.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/bematech/MP20.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/bematech/MP20.py	2016-10-13 16:22:19.312313050 -0300
@@ -47,7 +47,7 @@
 from stoqdrivers.printers.bematech.MP25 import (MP25, MP25Status, CMD_STATUS,
                                                 CMD_COUPON_OPEN)
 
-log = Logger('stoqdrivers.bematech.MP20')
+log = Logger('sigdrivers.bematech.MP20')
 
 CMD_ADD_ITEM_SIMPLE = 9
 
@@ -63,6 +63,7 @@
     LAST_ITEM_ID = 12
     NUMBER_TILL = 14
     EMISSION_DATE = 23
+    MOV_DATE = 27
     TOTALIZERS = 29
     PAYMENT_METHODS = 32
     SERIAL = 0
@@ -80,6 +81,7 @@
         LAST_ITEM_ID: ('2s', True),
         NUMBER_TILL: ('2s', True),
         EMISSION_DATE: ('6s', False),
+        MOV_DATE: ('3s', False),
         TOTALIZERS: ('2s', False),
         #  1 + (52 * 16) + (52 * 10) + (52 * 10) + (52 * 1)
         #  1 + 832 + 520 + 520 + 52: 1925
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/bematech/MP2100.py trunk/stoqdrivers/printers/bematech/MP2100.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/bematech/MP2100.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/bematech/MP2100.py	2016-10-13 16:22:19.312313050 -0300
@@ -30,5 +30,5 @@
 from stoqdrivers.printers.bematech.MP25 import MP25
 
 class MP2100(MP25):
-    model_name = "Bematech MP2100 TH FI"
+    model_name = "MP2100 TH FI"
 
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/bematech/MP25.py trunk/stoqdrivers/printers/bematech/MP25.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/bematech/MP25.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/bematech/MP25.py	2017-01-23 16:54:44.669415181 -0200
@@ -1,4 +1,4 @@
-# -*- Mode: Python; coding: iso-8859-1 -*-
+# -*- Mode: Python; coding: utf-8 -*-
 # vi:si:et:sw=4:sts=4:ts=4
 
 ##
@@ -29,32 +29,31 @@
 """
 Bematech MP25 driver
 """
-
-import datetime
+import re
+import binascii
 from decimal import Decimal
-import struct
-
+from docutils.nodes import description
 from kiwi.datatypes import currency
-from kiwi.log import Logger
+from stoqdrivers.logs.log import traced as log
 from kiwi.python import Settable
-from zope.interface import implements
-
-from stoqdrivers.serialbase import SerialBase
-from stoqdrivers.exceptions import (DriverError, OutofPaperError, PrinterError,
-                                    CommandError, CouponOpenError,
-                                    HardwareFailure,
-                                    PrinterOfflineError, PaymentAdditionError,
-                                    ItemAdditionError, CancelItemError,
-                                    CouponTotalizeError, CouponNotOpenError)
+from stoqdrivers.enum import TaxType, UnitType
+from stoqdrivers.exceptions import DriverError, OutofPaperError, PrinterError, \
+    CommandError, CouponOpenError, HardwareFailure, PrinterOfflineError, CodigoInvalido, \
+    PaymentAdditionError, ItemAdditionError, CancelItemError, CouponTotalizeError, \
+    CouponNotOpenError, AlmostOutofPaper, BlockZ, PendingReduceZ, PrinterHeadUp, AuthenticationFailure
 from stoqdrivers.interfaces import ICouponPrinter
-from stoqdrivers.printers.capabilities import Capability
 from stoqdrivers.printers.base import BaseDriverConstants
-from stoqdrivers.enum import TaxType, UnitType
+from stoqdrivers.printers.capabilities import Capability
+from stoqdrivers.serialbase import SerialBase
 from stoqdrivers.translation import stoqdrivers_gettext
+from zope.interface import implements
+import datetime
+import struct
+
+
 
 _ = stoqdrivers_gettext
 
-log = Logger('stoqdrivers.bematech.MP25')
 
 CASH_IN_TYPE = "SU"
 CASH_OUT_TYPE = "SA"
@@ -67,13 +66,18 @@
 CMD_READ_X = 6
 CMD_READ_MEMORY = 8
 CMD_COUPON_CANCEL = 14 # MP25 aceita 3 parametros opicionais
+CMD_SUMMER_TIME = 18 # Horário de Verão
 CMD_STATUS = 19
+CMD_OPEN_TILL = 22 # Abrir Gaveta
 CMD_ADD_VOUCHER = 25
 CMD_READ_TAXCODES = 26
-CMD_READ_TOTALIZERS = 27
+CMD_READ_TOTALIZERS_PARCIAL = 27 # tot. parciais nao completos
+CMD_READ_TOTALIZERS = 87 # totalizadores parciais completos
+CMD_READ_LAST_REDUCE = 88
 CMD_GET_COUPON_SUBTOTAL = 29
 CMD_GET_COUPON_NUMBER = 30
-CMD_CANCEL_ITEM = 31
+CMD_CANCEL_LAS_ITEM = 13 # cancela o ultimo item
+CMD_CANCEL_ITEM = 31 # cancela um item especifico
 CMD_COUPON_TOTALIZE = 32 # MP 20 Nao permite desconto simultaneo
 CMD_COUPON_CLOSE = 34
 CMD_READ_REGISTER = 35 # Registradores diferentes
@@ -81,59 +85,97 @@
 CMD_PAYMENT_RECEIPT_OPEN = 66 # MP20 suporta somente 3 primeiro parametros
 CMD_PAYMENT_RECEIPT_PRINT = 67
 CMD_PAYMENT_RECEIPT_CLOSE = 21 # Parece o mesmo, mas com nomes diferentes na mp20 e 25
+CMD_GERENCIAL_REPORT_OPEN = 83
+CMD_GERENCIAL_REPORT_PRINT = 67
+CMD_GERENCIAL_REPORT_CLOSE = 21
+CMD_DISCOUNT_SURCHARGE_AFTER = 93 # Desconto/Acresciomo para o item no final do cupom
+
 CMD_PROGRAM_PAYMENT_METHOD = 71
 CMD_ADD_PAYMENT = 72
+
 # Be carefull with these commands, They are only available for MP25 (not mp20)
 CMD_CANCEL_LAST = 81
 CMD_PAYMENT_RECEIPT_PRINT_DUPLICATE = 91
 
-
 NAK = 21
 ACK = 6
 STX = 2
 
-RETRIES_BEFORE_TIMEOUT = 5
+RETRIES_BEFORE_TIMEOUT = 10
+
+TWOPLACES = Decimal(10) ** -2
+TREPLACES = Decimal(10) ** -3
 
 # Page 51
+def isbitset(value, bit):
+    # BCD crap
+    return (int(value, 16) >> bit) & 1 == 1
+
 class MP25Registers(object):
     TOTAL = 3
     TOTAL_CANCELATIONS = 4
     TOTAL_DISCOUNT = 5
+    TOTAL_TESTE = 11
     COO = 6
+    COO_INICIAL_FINAL = 72
     GNF = 7
+    CFC = 8
     NUMBER_REDUCTIONS_Z = 9
     CRO = 10
     LAST_ITEM_ID = 12
+    CLICHE = 64
     NUMBER_TILL = 14
+    READ_LOJA = 15
+    FISCAL_FLAGS = 17
     EMISSION_DATE = 23
+    DATE_LAST_RZ = 26
+    MOV_DATE = 27
     TRUNC_FLAG = 28
     TOTALIZERS = 29
     PAYMENT_METHODS = 32
     SERIAL = 40
     FIRMWARE = 41
+    READ_IE = 43
+    CDC = 52
+    GRG = 54
     CCF = 55
+    DATE_MOV_LAST_RZ = 67
+    GT = 73
 
     # (size, bcd)
     formats = {
         TOTAL: ('9s', True),
         TOTAL_CANCELATIONS: ('7s', True),
         TOTAL_DISCOUNT: ('7s', True),
+        TOTAL_TESTE: ('7s', True),
         COO: ('3s', True),
+        COO_INICIAL_FINAL: ('6s', True),
+        READ_IE: ('20s', False),
+        READ_LOJA: ('2s', True),
         GNF: ('3s', True),
+        CFC: ('2s', True),
+        CDC: ('2s', True),
+        GRG: ('3s', True),
         NUMBER_REDUCTIONS_Z: ('2s', True),
         CRO: ('2s', True),
         LAST_ITEM_ID: ('2s', True),
         NUMBER_TILL: ('2s', True),
-        EMISSION_DATE: ('6s', False),
+        FISCAL_FLAGS: ('1s', False),
+        EMISSION_DATE: ('6s', True),
+        DATE_LAST_RZ: ('6s', True),
+        MOV_DATE: ('3s', True),
         TRUNC_FLAG: ('1s', False),
         TOTALIZERS: ('2s', False),
+        CLICHE: ('6s', False),
         #  1 + (52 * 16) + (52 * 10) + (52 * 10) + (52 * 1)
         #  1 + 832 + 520 + 520 + 52: 1925
         PAYMENT_METHODS: ('b832s520s520s52s', False),
         SERIAL: ('20s', False),
         FIRMWARE: ('3s', True),
         CCF: ('3s', True),
-    }
+        DATE_MOV_LAST_RZ: ('3s', True),
+        GT: ('18s', True),
+        }
 
 
 class MP25Constants(BaseDriverConstants):
@@ -147,20 +189,20 @@
 
 class MP25Status(object):
     st1_codes = {
-        128: (OutofPaperError(_("Printer is out of paper"))),
-        # 64: (AlmostOutofPaper(_("Printer almost out of paper"))),
+        128: (OutofPaperError(_("Impressora sem papel"))),
+#        64: (AlmostOutofPaper(_("Impressora com pouco papel"))),
         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"))),
+        2: (CouponOpenError(_("Impressora tem um cupom aberto atualmente"))),
         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"))),
+        16: (PrinterError("Alíquota não programada")),
         8: (DriverError(_("No available tax slot"))),
         4: (CancelItemError(_("The item wasn't added in the coupon or can't "
                               "be cancelled"))),
@@ -168,66 +210,271 @@
         # 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")))
+        1: (CommandError("Comando nao executado"))
         }
 
     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")))}
+         #0: Comando ok
+         #1 Comando inválido
+         #2 Erro desconhecido
+         #3 Número de parâmetro inválido
+         #4 Tipo de parâmetro inválido
+         #5 Todas alíquotas já programadas
+         #6 Totalizador não fiscal já programado
+         #7 Cupom fiscal aberto
+         #8 Cupom fiscal fechado
+         #9 ECF ocupado
+         #0 Impressora em erro
+         11: (DriverError("Impressora sem papel")),
+         12: (PrinterHeadUp("Impressora com cabeça levantada")),
+         13: (PrinterOfflineError("Impressora OFF LINE")),
+         14: (DriverError("Alíquota não programada")),
+         15: (DriverError("Terminador de string faltando")),
+         16: (DriverError("Acréscimo ou desconto maior que o total do cupom fiscal")),
+         17: (DriverError("Cupom fiscal sem item vendido")),
+         18: (DriverError("Comando nao efetivado")),
+         19: (DriverError("Sem espaço para novas formas de pagamento")),
+         20: (PaymentAdditionError("Forma de pagamento não programada")),
+         21: (PaymentAdditionError("Índice maior que número de forma de pagamento")),
+         22: (DriverError("Formas de pagamento encerradas")),
+         23: (DriverError("Cupom não totalizado")),
+         24: (DriverError("Comando maior que 7F")),
+         25: (DriverError("Cupom fiscal aberto e sem item")),
+         26: (DriverError("Cancelamento não imediatamento após")),
+         27: (DriverError("Cancelamento já efetuado")),
+         28: (DriverError("Comprovante de crédito ou débito não permitido ou já emitido")),
+         29: (DriverError("Meio de pagamento não permite TEF")),
+         30: (DriverError("Sem comprovante não fiscal aberto")),
+         31: (DriverError("Comprovante de crédito ou débito já aberto")),
+         32: (DriverError("Reimpressão não permitida")),
+         33: (DriverError("Comprovante não fiscal já aberto")),
+         34: (DriverError("Totalizador não fiscal não programado")),
+         35: (DriverError("Cupom não fiscal sem item vendido")),
+         36: (DriverError("Acréscimo e desconto maior que total CNF")),
+         37: (DriverError("Meio de pagamento não indicado")),
+         38: (DriverError("Meio de pagamento diferente do total de recebimento")),
+         39: (DriverError("Não permitido mais de uma sangria ou suprimento")),
+         40: (DriverError("Relatório já programado")),
+         41: (DriverError("Relatório gerencial não programado")),
+         42: (DriverError("Relatório gerencial não permitido")),
+         43: (CouponNotOpenError("MFD não inicializada")),
+         44: (DriverError("MFD ausente")),
+         45: (DriverError("MFD sem número de série")),
+         46: (DriverError("MFD já inicializada")),
+         47: (DriverError("MFD lotada")),
+         48: (DriverError("Cupom não fiscal aberto")),
+         49: (DriverError("Memória fiscal desconectada")),
+         50: (DriverError("Memória fiscal sem número de série da MFD")),
+         51: (DriverError("Memória fiscal lotada")),
+         52: (DriverError("Data inicial inválida")),
+         53: (DriverError("Data final inválida")),
+         54: (DriverError("Contador de Redução Z inicial inválido")),
+         55: (DriverError("Contador de Redução Z final inválido")),
+         56: (DriverError("Erro de alocação")),
+         57: (DriverError("Dados do RTC incorretos")),
+         58: (DriverError("Data anterior ao último documento emitido")),
+         59: (DriverError("Fora de intervenção técnica")),
+         60: (DriverError("Em intervenção técnica")),
+         61: (DriverError("Erro na memória de trabalho")),
+         #62: (DriverError("Já houve movimento no dia")),
+         63: (BlockZ("Bloqueio por RZ")),
+         64: (DriverError("Forma de pagamento aberta")),
+         65: (DriverError("Aguardando primeiro proprietário")),
+         66: (DriverError("Aguardando RZ")),
+         67: (DriverError("ECF ou loja igual a zero")),
+         68: (DriverError("Cupom adicional não permitido")),
+         69: (DriverError("Desconto maior que total vendido em ICMS")),
+         70: (DriverError("Recebimento não fiscal nulo não permitido")),
+         71: (DriverError("Acréscimo ou desconto maior que total não fiscal")),
+         72: (DriverError("Memória fiscal lotada para novo cartucho")),
+         73: (DriverError("Erro de gravação na MF")),
+         74: (DriverError("Erro de gravação na MFD")),
+         75: (DriverError("Dados do RTC anteriores ao último documento armazenado")),
+         76: (DriverError("Memória fiscal sem espaço para gravar leituras da MFD")),
+         77: (DriverError("Memória fiscal sem espaço para gravar versão do SB")),
+         78: (DriverError("Descrição igual a default não permitido")),
+         79: (DriverError("Extrapolado número de repetições permitidas")),
+         80: (DriverError("Segunda via do comprovante de crédito ou débito não permitido")),
+         81: (DriverError("Parcelamento fora da sequência")),
+         82: (DriverError("Comprovante de crédito ou débito aberto")),
+         83: (DriverError("Texto com sequência de ESC inválida")),
+         84: (DriverError("Texto com sequência de ESC incompleta")),
+         85: (DriverError("Venda com valor nulo")),
+         86: (DriverError("Estorno de valor nulo")),
+         87: (DriverError("Forma de pagamento diferente do total da sangria")),
+         88: (DriverError("Redução não permitida em intervenção técnica")),
+         89: (DriverError("Aguardando RZ para entrada em intervenção técnica")),
+         90: (DriverError("Forma de pagamento com valor nulo não permitido")),
+         91: (ItemAdditionError("Acréscimo ou desconto maior que valor do ítem")),
+         92: (DriverError("Autenticação não permitida")),
+         93: (DriverError("Timeout na validação")),
+         94: (DriverError("Comando não executado em impressora bilhete de passagem")),
+         95: (DriverError("Comando não executado em impressora de Cupom Fiscal")),
+         96: (DriverError("Cupom Não Fiscal fechado")),
+         97: (DriverError("Parâmetro não ASCII em campo ASCII")),
+         98: (DriverError("Parâmetro não ASCII num em campo ASCII num")),
+         99: (DriverError("Tipo de transporte inválido")),
+         100: (DriverError("Data e hora inválida")),
+         101: (CodigoInvalido("Sem relatório gerencial")),
+         102: (DriverError("Número do totalizador não fiscal inválido")),
+         103: (DriverError("Parâmetro de acréscimo ou desconto inválido")),
+         104: (DriverError("Acréscimo ou desconto em sangria ou suprimento não permitido")),
+         105: (DriverError("Número do relatório gerencial inválido")),
+         106: (DriverError("Forma de pagamento fonte não programada")),
+         107: (DriverError("Forma de pagamento destino não programada")),
+         108: (DriverError("Estorno maior que forma pagamento")),
+         109: (DriverError("Caracter numérico na codificação GT não permitido")),
+         110: (DriverError("Erro na inicialização da MF")),
+         111: (DriverError("Nome do totalizador em branco não permitido")),
+         112: (DriverError("Data e hora anteriores ao último doc armazenado")),
+         113: (DriverError("Parâmetro de acréscimo ou desconto inválido")),
+         114: (DriverError("Item anterior aos trezentos últimos")),
+         115: (CancelItemError("Item não existe ou já cancelado")),
+         116: (DriverError("Código com espaços não permitido")),
+         117: (DriverError("Descrição sem caracter alfabético não permitido")),
+         118: (DriverError("Acréscimo maior que valor do item")),
+         119: (DriverError("Desconto maior que valor do item")),
+         120: (DriverError("Desconto em ISS não permitido")),
+         121: (DriverError("Acréscimo em item já efetuado")),
+         122: (DriverError("Desconto em item já efetuado")),
+         123: (DriverError("Erro na Memória Fiscal chamar credenciado")),
+         124: (DriverError("Aguardando gravação na Memória Fiscal")),
+         125: (DriverError("Caracter repetido na codificação do GT")),
+         126: (DriverError("Versão já gravada na memória fiscal")),
+         127: (DriverError("Estouro de capacidade no cheque (não utilizado neste modelo)")),
+         128: (DriverError("Timeout na leitura do cheque (não utilizado neste modelo)")),
+         129: (CouponOpenError("Mês inválido (não utilizado neste modelo)")),
+         130: (DriverError("Coordenada inválida (não utilizado neste modelo)")),
+         131: (DriverError("Sobreposição de texto (não utilizado neste modelo)")),
+         132: (DriverError("Sobreposição de texto no valor (não utilizado neste modelo)")),
+         133: (DriverError("Sobreposição de texto no extenso (não utilizado neste modelo)")),
+         134: (DriverError("Sobreposição de texto no favorecido (não utilizado neste modelo)")),
+         135: (DriverError("Sobreposição de texto na localidade (não utilizado neste modelo)")),
+         136: (DriverError("Sobreposição de texto no opcional (não utilizado neste modelo)")),
+         137: (DriverError("Sobreposição de texto no dia (não utilizado neste modelo)")),
+         138: (DriverError("Sobreposição de texto no mês (não utilizado neste modelo)")),
+         139: (DriverError("Sobreposição de texto no ano (não utilizado neste modelo)")),
+         140: (DriverError("Usando MFD de outro ECF")),
+         141: (DriverError("Primeiro dado diferente de esc ou 1C")),
+         142: (DriverError("Não permitido alterar sem intervenção técnica")),
+         143: (DriverError("Dados da última RZ corrompidos")),
+         144: (DriverError("Comando não permitido no modo inicialização")),
+         145: (DriverError("Aguardando acerto de relógio")),
+         146: (DriverError("MFD já inicializada para outra memoria fiscal")),
+         147: (DriverError("Aguardando acerto do relógio ou desbloqueio pelo teclado")),
+         148: (DriverError("Valor forma de pagamento maior que máximo permitido")),
+         149: (DriverError("Razão social em branco")),
+         150: (DriverError("Nome de fantasia em branco")),
+         151: (DriverError("Endereço em branco")),
+         152: (DriverError("Estorno de CDC não permitido")),
+         153: (DriverError("Dados do proprietário iguais ao atual")),
+         154: (DriverError("Estorno de forma de pagamento não permitido")),
+         155: (DriverError("Descricao forma de pagamento igual já programada")),
+         156: (DriverError("Acerto de horário de verão só imediatamente após Redução Z")),
+         157: (DriverError("Intervenção técnica não permitida MF reservada para Redução Z")),
+         158: (DriverError("Senha CNPJ inválida")),
+         159: (DriverError("Timeout na inicialização de nova MF")),
+         160: (DriverError("Dados da MFD não encontrados")),
+         161: (DriverError("Sangria ou Suprimento devem ser únicos no comprovante não fiscal")),
+         162: (CodigoInvalido("Índice da forma de pagamento nulo não permitido")),
+         163: (DriverError("UF de destino inválida")),
+         164: (DriverError("Tipo de transporte incompatível com a UF de destino")),
+         165: (DriverError("Descrição do primeiro item do Bilhete de passagem diferente de “TARIFA”")),
+         166: (DriverError("Aguardando impressão de cheque ou autenticação (não utilizado neste modelo)")),
+         167: (DriverError("Programação de CNPJ e IE preenchido com espaços em branco não permitido")),
+         168: (DriverError("Programação de UF preenchido com espaços em branco não permitido")),
+         169: (DriverError("Número de impressões da fita detalhe nesta intervenção técnica esgotado")),
+         170: (PaymentAdditionError("Cupom fiscal já sub-totalizado")),
+         171: (PaymentAdditionError("Cupom não sub-totalizado")),
+         172: (PaymentAdditionError("Acréscimo em sub-total já efetuado")),
+         173: (PaymentAdditionError("Desconto em sub-total já efetuado")),
+         174: (PaymentAdditionError("Acréscimo com valor nulo não permitido")),
+         175: (PaymentAdditionError("Desconto com valor nulo não permitido")),
+         176: (PaymentAdditionError("Cancelamento de acréscimo ou desconto em sub-total não permitido")),
+         177: (DriverError("Data inválida")),
+         178: (DriverError("Valor do cheque nulo não permitido (não utilizado neste modelo)")),
+         179: (DriverError("Valor do cheque inválido (não utilizado neste modelo)")),
+         180: (DriverError("Cheque sem a informação da localidade não permitido (não utilizado neste modelo)")),
+         181: (DriverError("Cancelamento de acréscimo em item não permitido")),
+         182: (DriverError("Cancelamento de desconto em item não permitido")),
+         183: (DriverError("Número máximo de itens atingido")),
+         184: (DriverError("Número do item nulo não permitido")),
+         185: (DriverError("Mais que duas alíquotas diferentes no Bilhete de Passagem não permitido")),
+         186: (DriverError("Acréscimo ou Desconto em item não permitido")),
+         187: (DriverError("Cancelamento de acréscimo ou desconto em item não permitido")),
+         188: (DriverError("Clichê já impresso")),
+         189: (DriverError("Texto opcional do cheque excedeu o máximo de linhas permitido (não utilizado neste modelo)")),
+         190: (DriverError("Impressão automática no verso não disponível neste equipamento")),
+         191: (DriverError("Timeout na inserção do cheque (não utilizado neste modelo)")),
+         192: (DriverError("Overflow na capacidade de texto do comprovante de débito ou crédito")),
+         193: (DriverError("Programação de espaços entre cupons menor que o mínimo permitido")),
+         194: (DriverError("Equipamento não possui leitor de cheque (não utilizado neste modelo)")),
+         195: (DriverError("Programação de alíquota com valor nulo não permitido")),
+         196: (DriverError("Parâmetro baud rate inválido")),
+         197: (DriverError("Configuração permitida somente pela porta do fisco")),
+         198: (DriverError("Valor total do item excede 11 dígitos")),
+         199: (DriverError("Programação da moeda com espaços em branco não permitido")),
+         200: (DriverError("Casa decimais devem ser programadas com 2 ou 3")),
+         201: (DriverError("Não permite cadastrar usuários diferentes na mesma MFD")),
+         202: (DriverError("Identificação do consumidor não permitido para sangria ou suprimento")),
+         203: (DriverError("Casas decimais em quantidade maior do que a permitida")),
+         204: (DriverError("Casas decimais do unitário maior do que o permitido")),
+         205: (DriverError("Posição reservada para ICMS")),
+         206: (DriverError("Posição reservada para ISS")),
+         207: (DriverError("Todas as alíquotas com a mesma vinculação não permitido")),
+         208: (DriverError("Data de embarque anterior a data de emissão")),
+         209: (DriverError("Alíquota para ISSQN não permitida sem inicialização da Inscrição Municipal")),
+         210: (DriverError("Pacote do clichê gráfico fora da seqüência")),
+         211: (DriverError("Espaço para armazenamento do clichê gráfico esgotado")),
+         212: (DriverError("Clichê gráfico não disponível para confirmação")),
+         213: (DriverError("CRC do clichê gráfico diferente do informado")),
+         214: (DriverError("Intervalo inválido")),
+         215: (DriverError("Usuário já programado")),
+         217: (DriverError("Detectada abertura do equipamento"))
+        }
 
     def __init__(self, reply):
         self.st1, self.st2, self.st3 = reply[-3:]
 
+
     @property
     def open(self):
-        return self.st1 & 2
+        return bool(self.st1 == 2)
+    @property
+    def pagamento_existe(self):
+        return bool(self.st1 == 66)
+    @property
+    def out_paper(self):
+        return self.st1 == 64
+    @property
+    def block_z(self):
+        return self.st3 == 66
+
+    @property
+    def off_line(self):
+        return self.st3 == 13
 
     def _check_error_in_dict(self, error_codes, value):
         for key in error_codes:
             if key & value:
+                # st3 eh o detalhamento de erro do st1 ou st2
+                if self.st3 in self.st3_codes:
+                    raise self.st3_codes[self.st3]
+
                 raise error_codes[key]
 
     def check_error(self):
-        log.debug("status: st1=%s st2=%s st3=%s" %
-                    (self.st1, self.st2, self.st3))
-
+#        print "status: st1=%s st2=%s st3=%s" % (self.st1, self.st2, self.st3)
+        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]
-
+    def check_error_two(self):
+        if self.st1 not in [0, 64]:
+            self.check_error()
 
 #
 # Helper functions
@@ -242,7 +489,7 @@
 def dec2bcd(dec):
     return chr(dec % 10 + (dec / 10) * 16)
 
-def dec2bin(n, trim=-1):
+def dec2bin(n, trim= -1):
     a = ""
     while n > 0:
         if n % 2 == 0:
@@ -253,7 +500,7 @@
 
     if trim != -1:
         if len(a) < trim:
-            a = ("0" * (trim-len(a))) + a
+            a = ("0" * (trim - len(a))) + a
     return a
 
 #
@@ -277,7 +524,7 @@
 
     def __init__(self, port, consts=None):
         self._consts = consts or MP25Constants
-        port.set_options(read_timeout=2, write_timeout=5)
+        port.set_options(read_timeout=3, write_timeout=5)
         SerialBase.__init__(self, port)
         # XXX: Seems that Bematech doesn't contains any variable with the
         # coupon remainder value, so I need to manage it by myself.
@@ -315,13 +562,28 @@
     def _read_reply(self, size):
         a = 0
         data = ''
+        ack = False
         while True:
             if a > RETRIES_BEFORE_TIMEOUT:
-                raise DriverError("Timeout")
+                raise DriverError("ECF Desconectada/Desligada ou Sem Papel")
 
             a += 1
-            reply = self.read(size)
-            if reply is None:
+            if not ack:
+                reply = self.read(1)
+            else: # caso retornou o ack mas esta demorando no retorno do resto do comando
+                reply = self.read(size-1) # tamanho menos o ACK
+
+            if not reply:
+                continue
+
+            if reply == chr(6): # ACK = comando foi executado na impressora
+                ack = True
+                data += reply
+                reply = self.read(size-1) # tamanho menos o ACK
+            elif reply == 21 or reply == chr(15): # NAK = erro na execução do comando ou timeout na recepcao dos dados pela impressora
+                raise DriverError("Erro na execução do comando/ECF Desligada ou Sem Papel")
+            elif not ack:  # o elemento lido nao eh ACK e nem NAC ou seja, deve ser lixo no buffer
+                a = 0
                 continue
 
             data += reply
@@ -336,6 +598,7 @@
         status.check_error()
 
     def _send_command(self, command, *args, **kwargs):
+        log.debug("_send_command(%r, %r %r')" % (command, args, kwargs))
         fmt = ''
         if 'response' in kwargs:
             fmt = kwargs.pop('response')
@@ -357,22 +620,41 @@
                 raise NotImplementedError(type(arg))
 
         data = self._create_packet(cmd)
+
         self.write(data)
 
         format = self.reply_format % fmt
-        reply = self._read_reply(struct.calcsize(format))
+        # Condição implementada abaixo é um tratamento p/ não prosseguir c/ retorno invalido da ecf.
+        # Aki é verificado se o primeiro bit é igual 06H e se o comando enviado pertencia a lista de
+        # comandos.
+        while True:
+            reply = self._read_reply(struct.calcsize(format))
+            if reply[0] != chr(6) and command in [29, 35]:
+                self.write(data)
+            else:
+                break
+        if command == 71:
+            return reply
         retval = struct.unpack(format, reply)
 
         if raw:
+            #self._check_error(retval)
             return retval
 
-        self._check_error(retval)
-
         response = retval[1:-self.status_size]
         if len(response) == 1:
             response = response[0]
         return response
 
+    def retorno_de_variaveis(self, var, tam):
+        retval = self._send_command(35, var, response='%ds' % tam)
+        return binascii.b2a_hex(retval)
+
+    def ler_ultima_reducao(self):
+        retval = self._send_command(62, 55, response='308s')
+        retval = binascii.b2a_hex(retval)
+        return retval
+
     def _read_register(self, reg):
         try:
             fmt, bcd = self.registers.formats[reg]
@@ -390,37 +672,58 @@
 
     def _get_coupon_subtotal(self):
         subtotal = self._send_command(CMD_GET_COUPON_SUBTOTAL, response='7s')
+
         if subtotal:
             return Decimal(bcd2dec(subtotal)) / Decimal("1e2")
         # Busted subtotal
         return Decimal("0.0")
 
     def _get_last_item_id(self):
-        return self._read_register(self.registers.LAST_ITEM_ID)
+        try:
+            res = self._read_register(self.registers.LAST_ITEM_ID)
+        except Exception as e:
+            log.error(str(e))
+            res = 999
+
+        return res
 
     def _get_coupon_number(self):
         coupon_number = self._send_command(CMD_GET_COUPON_NUMBER, response='3s')
         return bcd2dec(coupon_number)
 
-    def get_status(self, val=None):
-        if val is None:
-            val = self._send_command(CMD_STATUS, raw=True)
+    def get_status(self, val=None, cmd_status=None):
+        log.debug("get_status(%r, %r)" % (val, cmd_status))
+
+        # if not val:
+        #     val = self._send_command(1, raw=True)
 
+        if cmd_status is None:
+            cmd_status = CMD_STATUS
+
+        if val is None:
+            val = self._send_command(cmd_status, raw=True)
+            print 'st', val
+            #val = self._send_command(1, raw=True)
         return MP25Status(val)
 
-    def _add_voucher(self, type, value):
+    def _add_voucher(self, type, value, text=''):
         assert len(type) == 2
-
         status = self.get_status()
         if status.open:
             self._send_command(CMD_COUPON_CANCEL)
+        ret = self._send_command(CMD_ADD_VOUCHER, type, "%014d%s" % (int(value * Decimal('1e2')), text), raw=True)
+        self.get_status(ret).check_error()
 
-        self._send_command(CMD_ADD_VOUCHER, type, "%014d" % int(value * Decimal('1e2')))
-
-    def _configure_payment_methods(self):
+    def _configure_payment_methods(self, descricao):
         # Do one at a time, if you need it.
         ret = self._send_command(CMD_PROGRAM_PAYMENT_METHOD,
-                                 '%-16s1' % 'Cartao Credito', raw=True)
+                                 '%-16s' % descricao[:80], response='3', raw=False)
+        try:
+            int(ret[1:3])
+            return ret[1:3]
+        except:
+            print 'Finalizadora cheia'
+            return False
         #ret = self._send_command(CMD_PROGRAM_PAYMENT_METHOD,
         #                         '%-16s1' % 'Cartao Debito', raw=True)
         #ret = self._send_command(CMD_PROGRAM_PAYMENT_METHOD,
@@ -432,7 +735,48 @@
 
     def summarize(self):
         """ Prints a summary of all sales of the day """
-        self._send_command(CMD_READ_X)
+        return self._send_command(CMD_READ_X, raw=True)
+
+    def get_totalizadores(self, total=False):
+        '''
+        Esta funcao busca cada totalizador da ecf e seus valores
+        '''
+        total_cancelations = self._read_register(self.registers.TOTAL_CANCELATIONS)
+        total_discount = self._read_register(self.registers.TOTAL_DISCOUNT)
+
+        length, names = self._send_command(CMD_READ_TAXCODES, response='b32s') # quantidade e nome dos totalizadores
+        status = self._read_register(self.registers.TOTALIZERS)
+        status = struct.unpack('>H', status)[0]
+        if total: # pega todos totalizadores (ver manual da bematec)
+            cmd_totalizadores = CMD_READ_TOTALIZERS
+            response = '436s'
+        else: # nao pega todos totalizadores
+            cmd_totalizadores = CMD_READ_TOTALIZERS_PARCIAL
+            response = '219s'
+
+        values = self._send_command(cmd_totalizadores, response=response)
+        taxes = []
+        for i in range(length):
+            if 1 << 15 - i & status != 0:
+                type = 'ISS'
+            else:
+                type = 'ICMS'
+
+            name = bcd2hex(names[i * 2:i * 2 + 2])
+            value = bcd2dec(values[i * 7:i * 7 + 7])
+            taxes.append(('%02d' % (i+1), value / Decimal(100), type))
+
+        taxes.append(('ACRESC', bcd2dec(values[182:182 + 7]) / Decimal(100), 'ISS'))
+        taxes.append(('ACRESC', bcd2dec(values[161:161 + 7]) / Decimal(100), 'ICMS'))
+        taxes.append(('CANC', bcd2dec(values[189:189 + 7]) / Decimal(100), 'ISS'))
+        taxes.append(('CANC', total_cancelations / Decimal(100), 'ICMS'))
+        taxes.append(('DESC', bcd2dec(values[175:175 + 7]) / Decimal(100), 'ISS'))
+        taxes.append(('DESC', total_discount / Decimal(100), 'ICMS'))
+        taxes.append(('I', bcd2dec(values[112:119]) / Decimal(100), 'ICMS'))
+        taxes.append(('N', bcd2dec(values[119:126]) / Decimal(100), 'ICMS'))
+        taxes.append(('F', bcd2dec(values[126:133]) / Decimal(100), 'ICMS'))
+        return taxes
+
 
     def close_till(self, previous_day=False):
         """ Close the till for the day, no other actions can be done after this
@@ -443,21 +787,26 @@
         # não é fechado no mesmo dia
         self._send_command(CMD_REDUCE_Z)
 
-    def till_add_cash(self, value):
-        self._add_voucher(CASH_IN_TYPE, value)
+    def till_add_cash(self, value, totalizador=0, meio=0):
+        self._add_voucher(CASH_IN_TYPE, value, text='')
 
-    def till_remove_cash(self, value):
-        self._add_voucher(CASH_OUT_TYPE,value)
+    def till_remove_cash(self, value, cod=None, text=''):
+        self._add_voucher(CASH_OUT_TYPE, value, text)
 
-    def till_read_memory(self, start, end):
+    def till_read_memory(self, start, end, full=False):
+        i = 'i' if not full else 'I'
         self._send_command(CMD_READ_MEMORY,
-                           '%s%sI' % (start.strftime('%d%m%y'),
-                                      end.strftime('%d%m%y')))
+                           '%s%s%s' % (start.strftime('%d%m%y'),
+                                      end.strftime('%d%m%y'),
+                                      i))
 
-    def _till_read_memory_to_serial(self, start, end):
-        ret = self._send_command(CMD_READ_MEMORY,
-                           '%s%sR' % (start.strftime('%d%m%y'),
-                                      end.strftime('%d%m%y')))
+    def till_read_memory_to_serial(self, start, end, full=False):
+        r = 'r' if not full else 'R'
+        self._send_command(CMD_READ_MEMORY,
+                           '%s%s%s' % (start.strftime('%d%m%y'),
+                                       end.strftime('%d%m%y'),
+                                       r)
+                           )
 
         data = ''
         while True:
@@ -469,9 +818,25 @@
 
         return data
 
-    def till_read_memory_by_reductions(self, start, end):
+    def till_read_memory_by_reductions_to_serial(self, start, end, full=False):
+        r = 'r' if not full else 'R'
+        ret = self._send_command(CMD_READ_MEMORY,
+                                 '%06d%06d%s' % (start, end, r))
+        data = ''
+        while True:
+            c = self.read(1)
+            if ord(c) == 0x03:
+                break
+
+            data += unicode(c, self.coupon_printer_charset)
+
+        return data
+
+
+    def till_read_memory_by_reductions(self, start, end, full=False):
+        i = 'i' if not full else 'I'
         self._send_command(CMD_READ_MEMORY,
-                           '%06d%06dI' % (start, end))
+                           '%06d%06d%s' % (start, end, i))
 
     def coupon_identify_customer(self, customer, address, document):
         self._customer_name = customer
@@ -483,16 +848,22 @@
 
     def coupon_open(self):
         """ This needs to be called before anything else """
-        self._send_command(CMD_COUPON_OPEN,
-                            "%-29s%-30s%-80s" % (self._customer_document,
-                                                 self._customer_name,
-                                                 self._customer_address))
-
+        if self._customer_name:
+            ret = self._send_command(CMD_COUPON_OPEN,
+                                "%-29s%-30s%-80s" % (self._customer_document,
+                                                     self._customer_name,
+                                                     self._customer_address), raw=True)
+        else:
+            ret = self._send_command(CMD_COUPON_OPEN, raw=True)
+        self.get_status(ret).check_error()
+        return ret
     def coupon_cancel(self):
         """ Can only be called when a coupon is opened. It needs to be possible
         to open new coupons after this is called.
         """
-        self._send_command(CMD_COUPON_CANCEL)
+        retcmd = self._send_command(CMD_COUPON_CANCEL, raw=True)
+        self.get_status(retcmd).check_error()
+
 
     def cancel_last_coupon(self):
         """Cancel the last non fiscal coupon or the last sale."""
@@ -504,10 +875,10 @@
         new coupons after this is called.
         """
         # FIXME This will break the drivers recorded tests. Record new ones.
-        #self._send_command(CMD_COUPON_CLOSE, message)
-        self._send_command(CMD_COUPON_CLOSE)
+        message = re.sub(r'NOTA LEGAL:', chr(27) + chr(69) + r'NOTA LEGAL:' + chr(27) + chr(70) , message)
+        self._send_command(CMD_COUPON_CLOSE, message)
         self._reset()
-        return self._get_coupon_number()
+        return True
 
     def coupon_add_item(self, code, description, price, taxcode,
                         quantity=Decimal("1.0"), unit=UnitType.EMPTY,
@@ -518,12 +889,14 @@
         by stoq. In this case, the payments added will be higher than the ECF
         expects, and a cents change will be printed.
         """
+        if taxcode in ['F', 'I', 'N']:
+            taxcode += taxcode
         if unit == UnitType.CUSTOM:
             unit = unit_desc
         else:
             unit = self._consts.get_value(unit)
 
-        data = ("%02s"     # taxcode
+        data = ("%s"     # taxcode
                 "%09d"     # value
                 "%07d"     # quantity
                 "%010d"    # discount
@@ -532,38 +905,61 @@
                 "%2s"      # unit
                 "%-48s\0"  # code
                 "%-200s\0" # description
-                % (taxcode,
+                % (taxcode.rjust(2, '0'),
                    price * Decimal("1e3"),
                    quantity * Decimal("1e3"),
                    discount * Decimal("1e2"),
                    markup * Decimal("1e2"),
                    0, unit, code, description))
-        self._send_command(CMD_ADD_ITEM, data)
+        di = datetime.datetime.now()
+
+        retval = self._send_command(CMD_ADD_ITEM, data, raw=True)
+
+        self.get_status(retval).check_error()
         return self._get_last_item_id()
 
+    def desc_acres_item(self, desc_acres, item, valor):
+        '''
+        Efetua desconto acrescimo no item
+        :param desc_acres: 'A' para acrescimo, 'D' para desconto, 1 byte
+        :param item: numero de sequencia do item no cupom, 3 bytes
+        :param valor: valor do desconto/acrescimo,  8 bytes
+        :return:
+        '''
+
+        parametros = desc_acres + \
+                     "%03d" % item + \
+                     "%08d" % (Decimal(valor).quantize(TWOPLACES) * Decimal("1e2"))
+        return self._send_command(CMD_DISCOUNT_SURCHARGE_AFTER, parametros)
+
+    def cancel_last_item(self):
+        return self._send_command(CMD_CANCEL_LAS_ITEM)
+
     def coupon_cancel_item(self, item_id=None):
         """ Cancel an item added to coupon; if no item id is specified,
         cancel the last item added. """
         last_item = self._get_last_item_id()
         if item_id is None:
-            item_id = last_item
-        elif item_id not in xrange(1, last_item+2):
+            return self.cancel_last_item()
+
+        elif item_id not in xrange(1, last_item + 2):
             raise CancelItemError("There is no such item with ID %r"
                                   % item_id)
-        self._send_command(CMD_CANCEL_ITEM, "%04d" % (item_id,))
+        retcmd = self._send_command(CMD_CANCEL_ITEM, "%04d" % (item_id,), raw=True)
+        self.get_status(retcmd).check_error()
 
     def coupon_add_payment(self, payment_method, value, description=u""):
-        self._send_command(CMD_ADD_PAYMENT,
-                           "%s%014d%s" % (payment_method[:2],
-                                          int(value * Decimal('1e2')),
-                                          description[:80]))
+        #payment_method = '01'
+        retval = self._send_command(CMD_ADD_PAYMENT, "%s%014d%s" % (
+        payment_method, int(value * Decimal('1e2')), description[:80]), raw=True)
+        if retval[3] != 23:
+            self.get_status(retval).check_error()
         self.remainder_value -= value
         if self.remainder_value < 0.0:
             self.remainder_value = Decimal("0.0")
         return self.remainder_value
 
-    def coupon_totalize(self, discount=currency(0), markup=currency(0),
-                        taxcode=TaxType.NONE):
+    def coupon_totalize(self, discount=currency(0), markup=currency(0), taxcode=TaxType.NONE):
 
         if discount:
             type = 'd'
@@ -576,9 +972,10 @@
             # be specified.
             type = 'a'
             value = 0
-
-        self._send_command(CMD_COUPON_TOTALIZE, '%s%014d' % (
-            type, int(value * Decimal('1e2'))))
+        vlr = '%s%014d' % (type, int(value * Decimal('1e2')))
+        retval = self._send_command(CMD_COUPON_TOTALIZE, vlr, raw=True)
+        if retval[2] == 1:
+            self.get_status(retval).check_error()
 
         totalized_value = self._get_coupon_subtotal()
         self.remainder_value = totalized_value
@@ -594,33 +991,84 @@
             if value == method_id:
                 return name
 
-    def payment_receipt_open(self, identifier, coo, method_id, value):
-        method = self._get_payment_description(method_id)
+    def gerencial_report_open(self, id=None):
+        if id:
+            st = self._send_command(CMD_GERENCIAL_REPORT_OPEN, '%02d' % id, raw=True)
+        else:
+            st = self._send_command(CMD_GERENCIAL_REPORT_PRINT, raw=True)
+        self.get_status(st).check_error()
+
+    def gerencial_report_print(self, text):
+        imprime_linha = ''
+        for line in text.split('\n'):
+            if len(imprime_linha + '\n' + line) > 620: # limite de caracteres do comando CMD_GERENCIAL_REPORT_PRINT
+                self._send_command(CMD_GERENCIAL_REPORT_PRINT, imprime_linha)
+                imprime_linha = ''
+
+            imprime_linha += '\n' + line
+
+        self._send_command(CMD_GERENCIAL_REPORT_PRINT, imprime_linha) # imprime a o restante do texto
+
+    def gerencial_report_close(self):
+        self._send_command(CMD_GERENCIAL_REPORT_CLOSE)
+
+    def payment_receipt_open(self, identifier, coo, method, value):
+        method = self._get_payment_description(method)
         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))
+        value = int(value * 100)
+        res = self._send_command(CMD_PAYMENT_RECEIPT_OPEN,
+                           '%-16s%014d%06d' % (method, value, coo), raw=True)
+        # verifica o status do retorno do comando
+        MP25Status(res).check_error()
+        return True
+
+    def get_corta_papel(self): # a MP 2100 nao possuem comando para cortar papel
+        try: # 62, 75 => corta papel, 7 = linhas a avancar, 1=corte parcial
+            res = self._send_command(62, 75, 7, 1, raw=True)
+            MP25Status(res).check_error()
+        except Exception as e:
+            log.error("Erro ao cortar papel: %s" % str(e))
+            return False
+
+        return True
 
     def payment_receipt_print(self, text):
+        imprime_linha = ''
         for line in text.split('\n'):
-            self._send_command(CMD_PAYMENT_RECEIPT_PRINT, line + '\n')
+            if len(imprime_linha + '\n' + line) > 620: # limite de caracteres do comando CMD_PAYMENT_RECEIPT_PRINT
+                self._send_command(CMD_PAYMENT_RECEIPT_PRINT, imprime_linha)
+                imprime_linha = ''
+
+            if line == 'corta_papel':
+                if imprime_linha:
+                    self._send_command(CMD_PAYMENT_RECEIPT_PRINT, imprime_linha)
+                    imprime_linha = ''
+
+                if not self.get_corta_papel():
+                    imprime_linha = '\n------------------------------------------------\n\n'
+
+                continue
+
+            imprime_linha += '\n' + line
+
+        self._send_command(CMD_PAYMENT_RECEIPT_PRINT, imprime_linha) # imprime a o restante do texto
 
     def payment_receipt_close(self):
-        ret = self._send_command(CMD_PAYMENT_RECEIPT_CLOSE)
+        self._send_command(CMD_PAYMENT_RECEIPT_CLOSE)
 
     def payment_receipt_print_duplicate(self):
         self._send_command(CMD_PAYMENT_RECEIPT_PRINT_DUPLICATE)
 
     def get_capabilities(self):
         return dict(
-            item_code=Capability(max_len=13),
+            item_code=Capability(max_len=14),
             item_id=Capability(digits=4),
             items_quantity=Capability(min_size=1, digits=4, decimals=3),
             item_price=Capability(digits=6, decimals=2),
-            item_description=Capability(max_len=29),
+            item_description=Capability(max_len=200),
             payment_value=Capability(digits=12, decimals=2),
             promotional_message=Capability(max_len=320),
             payment_description=Capability(max_len=48),
@@ -652,9 +1100,28 @@
     def get_coo(self):
         return self._read_register(self.registers.COO)
 
+    def get_coo_inicial_final(self):
+        return self._read_register(self.registers.COO_INICIAL_FINAL)
+
+    def get_read_ie(self):
+        return self._read_register(self.registers.READ_IE)
+
+    def get_num_loja(self):
+        return self._read_register(self.registers.READ_LOJA)
+
+    def get_num_ecf(self):
+        return self._read_register(self.registers.NUMBER_TILL)
+
     def get_gnf(self):
         return self._read_register(self.registers.GNF)
 
+    def get_cfc(self):
+        return self._read_register(self.registers.CFC)
+    def get_grg(self):
+        return self._read_register(self.registers.GRG)
+    def get_cdc(self):
+        return self._read_register(self.registers.CDC)
+
     def get_crz(self):
         return self._read_register(self.registers.NUMBER_REDUCTIONS_Z)
 
@@ -667,22 +1134,22 @@
         service = False
         constants = []
         for i in range(16):
-            value = bcd2dec(data[i*2:i*2+2])
+            value = bcd2dec(data[i * 2:i * 2 + 2])
             if not value:
                 continue
 
-            if 1 << 15-i & status == 0:
+            if 1 << 15 - i & status == 0:
                 tax = TaxType.CUSTOM
             else:
                 tax = TaxType.SERVICE
             constants.append((tax,
-                              '%02d' % (i+1,),
+                              '%02d' % (i + 1,),
                               Decimal(value) / 100))
 
         constants.extend([
             (TaxType.SUBSTITUTION, 'FF', None),
-            (TaxType.EXEMPTION,    'II', None),
-            (TaxType.NONE,         'NN', None),
+            (TaxType.EXEMPTION, 'II', None),
+            (TaxType.NONE, 'NN', None),
             ])
 
         return constants
@@ -691,11 +1158,37 @@
         status = self._read_register(self.registers.PAYMENT_METHODS)[1]
         methods = []
         for i in range(20):
-            method = status[i*16:i*16+16]
+            method = status[i * 16:i * 16 + 16]
             if method != '\x00' * 16:
-                methods.append(('%02d' % (i+1), method.strip()))
+                methods.append(('%02d' % (i + 1), method.strip()))
         return methods
 
+    def get_grande_total(self):
+        grande_total = self._read_register(self.registers.TOTAL)
+        grande_total = grande_total / Decimal(100)
+        return grande_total
+
+    def get_date_last_mov_rz(self):
+        '''
+        :return: Retorna a data do movimento da ultima reducao Z emitida
+        '''
+        str_date_mov_last_rz = str(self._read_register(self.registers.DATE_MOV_LAST_RZ))
+        try:
+            if len(str_date_mov_last_rz) == 5:
+                str_date_mov_last_rz = '0' + str_date_mov_last_rz
+
+            date_last_mov_rz = datetime.datetime(year=2000 + int(str_date_mov_last_rz[4:6]),
+                                                 month=int(str_date_mov_last_rz[2:4]),
+                                                 day=int(str_date_mov_last_rz[:2])).date()
+        except Exception as e:
+            # quando emite um Z que nao eh pendente em um dia que nao teve movimentacao, a data do movimento
+            # do dele nao existe.
+            log.error('get_date_last_mov_rz: %s' % str(e))
+            date_last_mov_rz = 0
+
+        return date_last_mov_rz
+
+
     def get_sintegra(self):
         opening_date = self._read_register(self.registers.EMISSION_DATE)
         cro = self._read_register(self.registers.CRO)
@@ -703,54 +1196,148 @@
         # -1
         crz = self._read_register(self.registers.NUMBER_REDUCTIONS_Z)
         coo = self._get_coupon_number()
+        ccf = self.get_ccf()
+        gnf = self.get_gnf()
+        grg = self.get_grg()
+        cdc = self.get_cdc()
+        serial = self.get_serial()
         total_cancelations = self._read_register(self.registers.TOTAL_CANCELATIONS)
         total_discount = self._read_register(self.registers.TOTAL_DISCOUNT)
 
-        # Avbr function TACBrECFBematech.GetVendaBruta
-        registers = self._send_command(62 , 55, response='308s')
-        coupon_end = int(bcd2hex(registers)[568:568+6])
+        registers = self._send_command(62, 55, response='308s')
+        if self.get_status().st3 == 143:
+            raise self.get_status().st3_codes[143]
+
+        #  cupom inicial
+        try:
+            coupon_start = int(bcd2hex(registers)[568:568 + 6]) + 1
+        except:
+            coupon_start = bcd2hex(registers)[568:568 + 6]
+
+        # cupom final
+        coupon_end = coo
 
         grande_total = self._read_register(self.registers.TOTAL)
-        grande_total = grande_total/Decimal(100)
-        total_bruto = bcd2dec(registers[1:10])/Decimal(100)
+        grande_total = grande_total / Decimal(100)
+
+        # este tratamento eh devido ao erro que da ao tentar ler o GT do ultimo Z, a string dos
+        # registradores retornada de "self._send_command(62, 55, response='308s')" vem toda
+        # desconfigurada, neste percebi que pode ser quando a ECF eh nova e nao houve Z ainda
+        try:
+            total_bruto = bcd2dec(registers[1:10]) / Decimal(100)
+        except:
+            try:
+                registers = self._send_command(62, 55, response='308s')
+                total_bruto = bcd2dec(registers[1:10]) / Decimal(100)
+            except Exception as e:
+                log.error('GT ultimo Z: %s', str(e))
+                total_bruto = Decimal(0)
 
         length, names = self._send_command(CMD_READ_TAXCODES, response='b32s')
         status = self._read_register(self.registers.TOTALIZERS)
         status = struct.unpack('>H', status)[0]
-        values = self._send_command(CMD_READ_TOTALIZERS, response='219s')
 
+        values = self._send_command(CMD_READ_TOTALIZERS, response='436s')
+
+        date = '%012d' % opening_date
         taxes = []
         for i in range(length):
-            if 1 << 15-i & status != 0:
+
+            if 1 << 15 - i & status != 0:
                 type = 'ISS'
             else:
                 type = 'ICMS'
 
-            name = bcd2hex(names[i*2:i*2+2])
-            value = bcd2dec(values[i*7:i*7+7])
-            taxes.append((name, value/Decimal(100), type))
-
-        taxes.append(('CANC', total_cancelations/Decimal(100), 'ICMS'))
-        taxes.append(('DESC', total_discount/Decimal(100), 'ICMS'))
-        taxes.append(('I', bcd2dec(values[112:119])/Decimal(100), 'ICMS'))
-        taxes.append(('N', bcd2dec(values[119:126])/Decimal(100), 'ICMS'))
-        taxes.append(('F', bcd2dec(values[126:133])/Decimal(100), 'ICMS'))
-        date = bcd2hex(opening_date[:6])
+            name = bcd2hex(names[i * 2:i * 2 + 2])
+            value = bcd2dec(values[i * 7:i * 7 + 7])
+            taxes.append((name, value / Decimal(100), type))
+
+        taxes.append(('ACRESC', bcd2dec(values[182:182 + 7]) / Decimal(100), 'ISS'))
+        taxes.append(('ACRESC', bcd2dec(values[161:161 + 7]) / Decimal(100), 'ICMS'))
+        taxes.append(('CANC', bcd2dec(values[189:189 + 7]) / Decimal(100), 'ISS'))
+        taxes.append(('CANC', total_cancelations / Decimal(100), 'ICMS'))
+        taxes.append(('DESC', bcd2dec(values[175:175 + 7]) / Decimal(100), 'ISS'))
+        taxes.append(('DESC', total_discount / Decimal(100), 'ICMS'))
+        taxes.append(('I', bcd2dec(values[112:119]) / Decimal(100), 'ICMS'))
+        taxes.append(('N', bcd2dec(values[119:126]) / Decimal(100), 'ICMS'))
+        taxes.append(('F', bcd2dec(values[126:133]) / Decimal(100), 'ICMS'))
+
+        serial_id = '%03d' % self._read_register(self.registers.NUMBER_TILL)
+
+        str_date_last_rz = str(self._read_register(self.registers.DATE_LAST_RZ))
+        try:
+            if len(str_date_last_rz) == 11:
+                str_date_last_rz = '0' + str_date_last_rz
+
+            date_last_rz = datetime.date(year=2000 + int(str_date_last_rz[4:6]),
+                                         month=int(str_date_last_rz[2:4]),
+                                         day=int(str_date_last_rz[:2]))
+        except Exception as e:
+            date_last_rz = {0: str_date_last_rz, 1: str(e)}
+
+        date_last_mov_rz = self.get_date_last_mov_rz()
+        if not date_last_mov_rz:
+            date_last_mov_rz = date_last_rz
+
 
         return Settable(
-             opening_date=datetime.date(year=2000+int(date[4:6]),
-                                        month=int(date[2:4]),
-                                        day=int(date[:2])),
-             serial=self.get_serial(),
-             serial_id='%03d' % self._read_register(self.registers.NUMBER_TILL),
-             coupon_start=0,
-             coupon_end=coupon_end,
-             cro=cro,
-             crz=crz,
-             coo=coo,
-             period_total=grande_total - total_bruto,
-             total=grande_total,
-             taxes=taxes)
+            date_last_rz=date_last_rz,
+            date_last_mov_rz=date_last_mov_rz,
+            opening_date=date_last_rz,
+            serial=serial,
+            serial_id=serial_id,
+            coupon_start=coupon_start,
+            coupon_end=coupon_end,
+            cro=cro,
+            crz=crz,
+            coo=coo,
+            ccf=ccf,
+            gnf=gnf,
+            grg=grg,
+            cdc=cdc,
+            period_total=grande_total - total_bruto,
+            total=grande_total,
+            taxes=taxes
+        )
+
+    def get_tax(self):
+        total_cancelations = self._read_register(self.registers.TOTAL_CANCELATIONS)
+        total_discount = self._read_register(self.registers.TOTAL_DISCOUNT)
+        # Avbr function TACBrECFBematech.GetVendaBruta
+
+        length, names = self._send_command(CMD_READ_TAXCODES, response='b32s')
+        status = self._read_register(self.registers.TOTALIZERS)
+        status = struct.unpack('>H', status)[0]
+
+        values = self._send_command(CMD_READ_TOTALIZERS, response='436s')
+
+        #        values2 = self._send_command(27, response='218s')
+        #        return (bcd2hex(values), bcd2hex(values2))
+
+        taxes = []
+        for i in range(length):
+
+            if 1 << 15 - i & status != 0:
+                type = 'ISS'
+            else:
+                type = 'ICMS'
+
+            name = bcd2hex(names[i * 2:i * 2 + 2])
+            value = bcd2dec(values[i * 7:i * 7 + 7])
+            taxes.append((name, value / Decimal(100), type))
+
+        taxes.append(('ACRESC', bcd2dec(values[182:182 + 7]) / Decimal(100), 'ISS'))
+        taxes.append(('ACRESC', bcd2dec(values[161:161 + 7]) / Decimal(100), 'ICMS'))
+        taxes.append(('CANC', bcd2dec(values[189:189 + 7]) / Decimal(100), 'ISS'))
+        taxes.append(('CANC', total_cancelations / Decimal(100), 'ICMS'))
+        taxes.append(('DESC', bcd2dec(values[175:175 + 7]) / Decimal(100), 'ISS'))
+        taxes.append(('DESC', total_discount / Decimal(100), 'ICMS'))
+        taxes.append(('I', bcd2dec(values[112:119]) / Decimal(100), 'ICMS'))
+        taxes.append(('N', bcd2dec(values[119:126]) / Decimal(100), 'ICMS'))
+        taxes.append(('F', bcd2dec(values[126:133]) / Decimal(100), 'ICMS'))
+
+        return taxes
+
 
     def get_firmware_version(self):
         """Return the firmware version"""
@@ -759,3 +1346,68 @@
         ret = '%0*d' % (6, ret)
         firmware = "%s:%s:%s" % (ret[0:2], ret[2:4], ret[4:6])
         return firmware
+    ##
+    # By Christian Douglas
+    ##
+    def set_summertime(self, value=True):
+        ret_cmd = self._send_command(CMD_SUMMER_TIME, raw=True)
+        self._check_error(ret_cmd)
+        return True
+
+    def get_summertime(self):
+        ret_cmd = bcd2hex(self._read_register(self.registers.FISCAL_FLAGS))
+        return isbitset(str(ret_cmd), 2)
+
+
+    def open_cash_drawer(self, sec=0):
+        return self._send_command(CMD_OPEN_TILL)
+
+    def get_status_cash_drawer(self):
+        retval = self._send_command(23, response='1s', raw=True)
+        result = True if retval[1] == '\xff' else False
+        return result
+
+    def get_date_sb(self):
+        ret_cmd = self._read_register(self.registers.CLICHE)
+        print ret_cmd, binascii.b2a_hex(ret_cmd)
+
+    def get_data_mov(self):
+        log.debug("get_data_mov")
+        ret_cmd = self._read_register(self.registers.MOV_DATE)
+        if ret_cmd == 0:
+            ret_cmd = 10100
+        return datetime.datetime.strptime('%06d' % ret_cmd, "%d%m%y").date()
+
+    def get_ecf_date(self):
+        log.debug("get_ecf_date")
+        ret_cmd = self._read_register(self.registers.EMISSION_DATE)
+        if ret_cmd == 0:
+            ret_cmd = 10100000000
+        return datetime.datetime.strptime('%012d' % ret_cmd, "%d%m%y%H%M%S")
+
+    def get_last_reduce(self):
+        rethex = binascii.b2a_hex(self._send_command(88, response='621s'))
+        GT_INI_FIM = self._read_register(self.registers.GT) or ''
+        GT_INI = float(str(GT_INI_FIM)[:18] or 0)
+        GTANT = Decimal(str( GT_INI / 100.00))
+        retval = {'CRO': int(rethex[2:6]),
+                  'CRZ': int(rethex[6:10]),
+                  'COO': int(rethex[10:16]),
+                  'GNF': int(rethex[16:22]),
+                  'CCF': int(rethex[22:28]),
+                  'GRG': int(rethex[28:34]),
+                  'CFD': int(rethex[34:40]),
+                  'NFC': int(rethex[40:44]),
+                  'CFC': int(rethex[44:48]),
+                  'COM': rethex[48:168],
+                  'CER': rethex[166:288],
+                  'CDC': int(rethex[288:292]),
+                  'NCN': int(rethex[292:296]),
+                  'CCDC': int(rethex[296:300]),
+                  'GT': Decimal(rethex[300:318]) / Decimal(100),
+                  'VBD': Decimal(rethex[300:318]) / Decimal(100) - GTANT}
+        return retval
+
+    def get_pagamento_existe(self):
+        ret_cmd = bcd2hex(self._read_register(self.registers.FISCAL_FLAGS))
+        return isbitset(str(ret_cmd), 1)
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/bematech/MP4000.py trunk/stoqdrivers/printers/bematech/MP4000.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/bematech/MP4000.py	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/printers/bematech/MP4000.py	2016-10-13 16:22:19.316312978 -0300
@@ -0,0 +1,34 @@
+# -*- Mode: Python; coding: iso-8859-1 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Stoqdrivers
+## Copyright (C) 2008 Async Open Source <http://www.async.com.br>
+## All rights reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 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 General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+## USA.
+##
+## Author(s):   Ronaldo Maia <romaia@async.com.br>
+##
+"""
+Bematech MP4000 driver
+"""
+
+from stoqdrivers.printers.bematech.MP25 import MP25
+
+class MP4000(MP25):
+    model_name = "MP4000 TH FI"
+
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/daruma/FS2100.py trunk/stoqdrivers/printers/daruma/FS2100.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/daruma/FS2100.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/daruma/FS2100.py	2016-10-13 16:22:19.344312473 -0300
@@ -1,6 +1,7 @@
 # -*- Mode: Python; coding: iso-8859-1 -*-
 # vi:si:et:sw=4:sts=4:ts=4
-
+from kiwi.python import Settable
+import re
 ##
 ## Stoqdrivers
 ## Copyright (C) 2005 Async Open Source <http://www.async.com.br>
@@ -30,14 +31,12 @@
 import operator
 from decimal import Decimal
 import time
-
+import datetime
 from stoqdrivers.printers.daruma.FS345 import FS345, CMD_GET_TAX_CODES
 from stoqdrivers.enum import UnitType, TaxType
-from stoqdrivers.exceptions import DriverError
-
-from kiwi.log import Logger
-
-log = Logger('stoqdrivers.daruma')
+from stoqdrivers.exceptions import DriverError, CommandError, CloseCouponError
+import random
+from stoqdrivers.logs.log import traced as log
 
 ENQ = 05
 ACK = 06
@@ -48,7 +47,20 @@
 GS  = 29
 FF  = 255
 
+CMD_ACIONA_GUILHOTINA = 123
 CMD_ADD_ITEM = 201
+CMD_DISCOUNT_SURCHARGE = 202 # desconto/acrescimo no item depois que o mesmo foi vendido
+CMD_READ_REGISTER = 200
+CMD_GET_TAX_CODES = 231
+CMD_GET_DATES = 250
+# REGISTRADORES
+REG_GT = 01
+REG_CRO = 23
+REG_CRZ = 24
+REG_SERIAL = 78
+
+TWOPLACES = Decimal(10) ** -2
+TREPLACES = Decimal(10) ** -3
 
 class FS2100(FS345):
     model_name = "Daruma FS 2100"
@@ -57,14 +69,24 @@
                         quantity=Decimal("1.0"), unit=UnitType.EMPTY,
                         discount=Decimal("0.0"),
                         surcharge=Decimal("0.0"), unit_desc=""):
+
+        TAX_DICT = {'F': 17,
+                    'I': 19,
+                    'N': 21
+        }
+
+        if taxcode in TAX_DICT.keys():
+            taxcode = TAX_DICT.get(taxcode)
+        else:
+            taxcode = taxcode.rjust(2, '0') 
+
         if surcharge:
-            d = 2
+            d = 3
             E = surcharge
         else:
-            d = 0
+            d = 1
             E = discount
-
-        E = int(E)
+#        E = int(E)
         price = price
         quantity = quantity
 
@@ -83,8 +105,8 @@
                 '%07d' # Quantity
                 '%08d' # Unitary price
                 '%d'   # 0=Discount(%) 1=Discount($) 2=Surcharge(%) 3=Surcharge($)
-                '%04d' # Discount/Surcharge value
-                '%07d' # *Padding* (since we have discount/surcharge only in %)
+                '%011d' # Discount/Surcharge value
+#                '%07d' # *Padding* (since we have discount/surcharge only in %)
                 '%02d' # Description size
                 '%14s' # Code
                 '%3s'  # Unit of measure
@@ -93,26 +115,41 @@
                 % (taxcode, int(quantity * Decimal('1e3')),
                    int(price * Decimal('1e2')), d,
                    int(E * Decimal('1e2')),
-                   0, desc_size, code, unit, description[:233]))
-
+                   desc_size, code, unit, description[:233]))
+        print '>', data
         value = self.send_new_command('F', CMD_ADD_ITEM, data)
-        return int(value[3:6])
+        return int(value[8:11])
+
+    def desc_acres_item(self, desc_acres, item, valor):
+        if desc_acres == 'D':
+            desc_acres = 1  # 0 = % desc; 1 = R$ desc; 2 = % acres; 3 = R$ acres  (1 bytes)
+        else:
+            desc_acres = 3
+
+        item = "%03d" % item # (3 bytes)
+        valor = "%011d" % (Decimal(valor).quantize(TWOPLACES) * Decimal("1e2")) # Valor em centavos (11 bytes)
+        data = "%s%s%s" % (item, desc_acres, valor)
+
+        self.send_new_command('F', CMD_DISCOUNT_SURCHARGE, data)
+        return True
 
     def send_command(self, command, extra=''):
         """As seen on ACBr code
 
-        Nas Darumas com MFD, em algumas situações o ECF pode ficar
-        temporariamente inoperantente, enquanto a compactação da MFD está
-        sendo efetuada. Nessa situação, o ECF retorna os seguintes códigos
-        de erro: 35 - Relogio Inoperante ou 99 (não documentado).
+        Nas Darumas com MFD, em algumas situa��es o ECF pode ficar
+        temporariamente inoperantente, enquanto a compacta��o da MFD est�
+        sendo efetuada. Nessa situa��o, o ECF retorna os seguintes c�digos
+        de erro: 35 - Relogio Inoperante ou 99 (n�o documentado).
 
-        Segundo o Suporte Técnico da Daruma, quando este problema ocorre,
-        devemos aguardar até que ele consiga responder corretamente.
+        Segundo o Suporte T�cnico da Daruma, quando este problema ocorre,
+        devemos aguardar at� que ele consiga responder corretamente.
 
-        Esta rotina irá tentar enviar o comando por 10 vezes. Caso ela
+        Esta rotina ir� tentar enviar o comando por 10 vezes. Caso ela
         recebe os erros 35 ou 99... ele aguarda 100 milisegundos e tenta um
         novo envio...
         """
+        if command == 1:
+            return False
 
         for t in range(10):
             try:
@@ -129,22 +166,22 @@
         # After we have tried to send the command T times, we need to read
         # all the responses. This is really crap
         for i in range(t):
-            reply = self._read_reply()
+            reply = self.readline()
             log.debug('Ignoring reply: %r' % reply)
 
         return retval
 
     def _check_response(self, retcode, raw):
         # Compatible with the fs345/fs2100
-        print retcode
-        compatible_error = retcode[1:3]
-        extended_error = retcode[3:6]
+
+        compatible_error = retcode[3:6]
+        extended_error = retcode[7:9]
         warning_code = retcode[6:8]
         log.debug('FS2100 >>> Error %s - Extended: %s - Warning: %s' %
                     (compatible_error, extended_error, warning_code))
         if int(compatible_error):
             # Mimic FS345 error format
-            error_code = ':E%s' % compatible_error
+            error_code = int(compatible_error)
             self.handle_error(error_code, raw)
 
     def send_new_command(self, prefix, command, extra='', ignore_error=False):
@@ -156,6 +193,7 @@
         data = chr(command) + extra
 
         log.debug('FS2100 >>> %r %d' % (data, len(data)))
+
         data = chr(FS) + prefix + data
 
         checksum = reduce(operator.xor, [ord(d) for d in data], 0)
@@ -167,12 +205,15 @@
 
         if not ignore_error:
             self._check_response(retval, data)
-
         return retval[1:]
 
     def _get_compatibility_mode(self):
         return self.send_new_command('R', 200, '138')
 
+    def get_tax(self):
+        sintegra = self.get_sintegra()
+        return sintegra.taxes
+
     def get_tax_constants(self):
         tax_codes = self.send_command(CMD_GET_TAX_CODES)[1:]
         lower_case = 'abcdefghijklmnop'
@@ -203,3 +244,344 @@
 
         return constants
 
+    # POR CHRISTIAN DOUGLAS
+    def _read_registers(self, reg, ignore_error=True):
+        reg = '%.3d' % reg
+        retval = self.send_new_command('R', 200, reg, ignore_error)
+        return retval[4:]
+
+    def get_corta_papel(self):
+        self._read_registers(CMD_ACIONA_GUILHOTINA)
+
+    def get_serial(self):
+        retval = self._read_registers(78)
+        return retval[:-1]
+
+    def get_coo(self):
+        retval = self._read_registers(26)
+        return int(retval)
+
+    def get_ccf(self):
+        retval = self._read_registers(30)
+        return int(retval)
+
+    def get_crz(self):
+        retval = self._read_registers(24)
+        return int(retval)
+    def get_grg(self):
+        retval = self._read_registers(33)
+        return int(retval)
+    def get_cdc(self):
+        retval = self._read_registers(45)
+        return int(retval)
+    def get_cro(self):
+        retval = self._read_registers(23)
+        return int(retval)
+
+    def get_gnf(self):
+        retval = self._read_registers(28)
+        return int(retval)
+
+    def get_data_mov(self):
+        retval = self._read_registers(70)
+        dia = int(retval[:2])
+        mes = int(retval[2:4])
+        ano = int(retval[4:8])
+        return datetime.datetime(ano, mes, dia).date()
+
+
+    def get_ecf_date(self):
+        retval = self._read_registers(66)
+        dia = int(retval[:2])
+        mes = int(retval[2:4])
+        ano = int(retval[4:8])
+        hora = int(retval[8:10])
+        minutos = int(retval[10:12])
+        segundos = int(retval[12:])
+        return datetime.datetime(ano, mes, dia, hora, minutos, segundos)
+
+    def get_date_sb(self):
+        retval = self._read_registers(85)
+        return datetime.datetime.strptime(retval, "%d%m%Y%H%M%S")
+
+    def _get_coupon_subtotal(self):
+        retval = self._read_registers(47)
+        retval = Decimal('%s.%s' % (retval[:-2], retval[10:]))
+        return retval
+
+
+    def _send_command(self, command, raw=''):
+        return self.send_command(command, extra=raw)
+
+    def _add_payment(self, payment_method, value, description=''):
+        rv = self.send_new_command('F', 209, '%.02d%012d%s\xff' % (int(payment_method),
+                                                                   int(value * Decimal('1e2')),
+                                                                   description[:48]), ignore_error=False)
+        # FIXME: Why and when does this happen?
+        #        Avoids/Fixes bug 3467 at least
+        return rv #Decimal(rv) / Decimal('1e2')
+
+    def _configure_payment_methods(self, descricao):
+        ''' Metodo para verificar as finalizadoras cadastradas na ecf e rotornar o index de acordo com a descrição
+        informada.'''
+        retval = self._read_registers(126)
+        retval = retval.replace('\xff', '')
+        i = 15
+        x = 0
+        ind = 0
+        finalizadoraNaEcf = {}
+        while True:
+            ret = retval[x:i] # Descri��o da finalizadora na ecf
+            if not ret:
+                break
+            x = i
+            i += 15
+            ind += 1 # index na ecf
+            finalizadoraNaEcf[ret] = str(ind).rjust(2, '0')
+        try:
+            result = finalizadoraNaEcf[descricao.ljust(15, ' ')]
+        except:
+            result =  False
+        return result
+
+
+    def coupon_add_payment(self, payment_method, value, description=''):
+        
+        self._verify_coupon_open()
+        return self._add_payment(payment_method, value, description)
+
+    def get_summertime(self):
+        retval = self._read_registers(108)
+        return bool(int(retval))
+
+    def open_cash_drawer(self, sec):
+        return self.send_new_command('N', 206, '%.03d' % sec)
+    
+    def get_status_cash_drawer(self):
+        s = self._get_status()
+        ret = self.status_check(s, 1, 3)
+        return ret 
+        
+    def summarize(self):
+        self.send_new_command('F', 235, '%d' % 0)
+
+    def coupon_close(self, message=''):
+        self._check_status()
+        self._verify_coupon_open()
+
+        if (self._customer_name or self._customer_address or self._customer_document):
+            customer_name = self._customer_name or _("No client")
+            customer_document = self._customer_document or _("No document")
+            customer_address = self._customer_address or _("No address")
+            self.send_command(201,
+                              "%- 84s%- 84s%- 84s" % (customer_name,
+                                                      customer_address,
+                                                      customer_document))
+        try:
+            message = re.sub(r'NOTA LEGAL:', chr(27) + 'G1' + r'NOTA LEGAL:' + chr(27) + 'G0' , message)
+            self.send_new_command('F', 210, '0%s\xff' % message, ignore_error=True)
+        except DriverError as e:
+            raise CloseCouponError(("Nao foi possivel encerrar o cupom"))
+        self._reset()
+        return True
+
+    def till_remove_cash(self, value, cod=None, text=''):
+        data = "%011d%s\xff" % (int(value * Decimal('1e2')), text)
+        self.send_new_command('F', 227, data)
+
+    def till_add_cash(self, value, totalizador=0, meio=0):
+        data = "%011d\xff" % int(value * Decimal('1e2'))
+        self.send_new_command('F', 236, data)
+
+    def close_till(self, previous_day=False):
+        date = time.strftime('%d%m%y%H%M%S', time.localtime())
+        self.send_new_command('F', 234, date)
+
+    def set_summertime(self, value):
+        raw = '%d' % int(value)
+        retval = self.send_new_command('C', 200, raw)
+        return retval
+
+    def gerencial_report_open(self, id=0):
+        return self.send_new_command('F', 230, '%.02d' % id, ignore_error=True)
+
+    def gerencial_report_print(self, text):
+        for line in text.split('\n'):
+            self.send_new_command('F', 231, line + chr(255), ignore_error=True)
+
+    def gerencial_report_close(self):
+        self.send_new_command('F', 232, ignore_error=True)
+
+    def get_sintegra(self):
+        registries = self._get_registers()
+        fiscal_registries = self._get_fiscal_registers()
+
+        tax_codes = self.send_command(CMD_GET_TAX_CODES)[1:]
+
+        taxes = []
+        for i in range(14):
+            reg = tax_codes[i*5+1:i*5+5]
+            if reg == '////':
+                continue
+            reg = reg.replace('.', '')
+
+            if tax_codes[i*5] in 'ABCDEFGHIJKLMNOP':
+                type = 'ICMS'
+            else:
+                type = 'ISS'
+
+            sold = fiscal_registries[88+(i*14):102+(i*14)]
+            taxes.append((reg, Decimal(sold)/100, type))
+
+        taxes.append(('ACRESC', Decimal(fiscal_registries[313:326]) / 100, 'ICMS'))
+        taxes.append(('DESC', Decimal(fiscal_registries[19:32]) / 100, 'ICMS'))
+        taxes.append(('CANC', Decimal(fiscal_registries[33:46]) / 100, 'ICMS'))
+        taxes.append(('I', Decimal(fiscal_registries[47:60]) / 100, 'ICMS'))
+        taxes.append(('N', Decimal(fiscal_registries[61:74]) / 100, 'ICMS'))
+        taxes.append(('F', Decimal(fiscal_registries[75:88]) / 100, 'ICMS'))
+
+        total_sold = sum(value for _, value, _ in taxes)
+
+        old_total = Decimal(fiscal_registries[:18]) / 100
+        cancelled = Decimal(fiscal_registries[33:46]) / 100
+        period_total = total_sold
+
+        opening_date = self.get_data_mov()
+
+        identifier = self.get_serial()
+        return Settable(
+             opening_date=opening_date,
+             date_last_rz=opening_date,
+             date_last_mov_rz=opening_date,
+             serial=identifier[1:9],
+             serial_id=int(identifier[13:17]),
+             coupon_start=int(registries[:6]),
+             coupon_end=int(registries[7:12]),
+             cro=self.get_cro(),
+             crz=self.get_crz(),
+             coo=self.get_coo(),
+             ccf=self.get_ccf(),
+             gnf=self.get_gnf(),
+             grg=self.get_grg(),
+             cdc=self.get_cdc(),
+             period_total=period_total,
+             total=period_total + old_total,
+             taxes=taxes)
+
+
+    def get_status(self, val=None, cmd_status=None):
+        status = self._get_status()
+        class Status(object):
+            def __init__(self, parent=None):
+                self.status = status
+                self.parent = parent
+            @property
+            def open(self):
+                return self.parent.status_check(status, 4, 2)
+            @property
+            def pagamento_existe(self):
+                return self.parent.status_check(status, 1, 2)
+            @property
+            def out_paper(self):
+                return self.parent.status_check(status, 2, 0)
+            @property
+            def block_z(self):
+                return self.parent.status_check(status, 2, 1)
+
+            @property
+            def off_line(self):
+                st = self.parent.status_check(status, 2, 3)
+                return st
+            def check_error(self):
+                self.parent._check_status()
+            def check_error_two(self):
+                if self.open:
+                    self.parent._check_status()
+
+        return Status(self)
+
+    def payment_receipt_open(self, identifier, coo, method, value):
+        print 'new', identifier, method, coo, value
+        value = int(value * 100)
+        data = '%s%s%06d%012d\xff\xff\xff' % (method, '01', coo, value)
+        self.send_new_command('F', 212, data)
+        
+    def payment_receipt_print(self, text):
+        for line in text.split('\n'):
+            self.send_new_command('F', 213, line + '\xff')
+
+
+    def payment_receipt_close(self):
+        self.send_new_command('F', 214)
+
+    def get_date_last_mov_rz(self):
+        '''
+        :return: Retorna a data do movimento da ultima reducao Z emitida
+        '''
+        ultimo_z = self.get_last_reduce()
+        data = ultimo_z.get('date_last_rz')
+        try:
+            if len(data) == 5:
+                data = '0' + data
+
+            data = datetime.datetime(year=int(data[4:8]),
+                                     month=int(data[2:4]),
+                                     day=int(data[:2])).date()
+        except Exception as e:
+            log.error('get_date_last_mov_rz: %s' % str(e))
+            return 0
+
+        return data
+
+    def get_last_reduce(self):
+        '''
+        Pega os dados da ultima reducao Z emiditda
+        :return:
+        '''
+        retcmd = self._read_registers(140)
+        print retcmd
+        gtatual = self.get_grande_total()
+        retval = {
+            'date_last_rz': str(retcmd[0:8]),
+            'GT': Decimal(retcmd[8:26]) / Decimal('100'),
+            'VBD': Decimal(retcmd[8:26]) / Decimal('100') - Decimal(retcmd[26:44]) / Decimal('100'),
+            'CRO': int(retcmd[922:926]),
+            'CRZ': int(retcmd[926:930]),
+            'CRZR': int(retcmd[930:934]),
+            'COO': int(retcmd[934:940]),
+            'GNF': int(retcmd[940:946]),
+            'CCF': int(retcmd[946:952]),
+            'CVC': int(retcmd[952:958]),
+            'GRG': int(retcmd[958:964]),
+            'CFD': int(retcmd[964:970]),
+            'CBP': int(retcmd[970:976]),
+            'NFC': int(retcmd[980:984]),
+            'CMV': int(retcmd[988:992]),
+            'CFC': int(retcmd[992:996]),
+            'CNC': int(retcmd[996:1000]),
+            'CBC': int(retcmd[1000:1004]),
+            'NCN': int(retcmd[1004:1008]),
+            'CDC': int(retcmd[1008:1012]),
+            'CON': int(retcmd[1012:1092]),
+            'CER': int(retcmd[1092:1172])
+            #'NFC': retcmd[40:44],
+            #'CFC': retcmd[44:48],
+            #'COM': retcmd[48:168],
+            #'CER': retcmd[166:288],
+            #'CDC': retcmd[288:292],
+            #'NCN': retcmd[292:296],
+            #'CCDC': retcmd[296:300],
+        }
+        return retval
+
+    def get_read_ie(self):
+        retval = self._read_registers(91)
+        return retval.rstrip()
+
+    def get_num_loja(self):
+        retval = self._read_registers(129)
+        return retval
+
+    def get_num_ecf(self):
+        retval = self._read_registers(107)
+        return retval
\ Falta o caracter nova linha no final do arquivo
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/daruma/FS345.py trunk/stoqdrivers/printers/daruma/FS345.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/daruma/FS345.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/daruma/FS345.py	2017-01-23 16:54:44.617415511 -0200
@@ -1,4 +1,4 @@
-# -*- Mode: Python; coding: iso-8859-1 -*-
+# -*- Mode: Python; coding: utf-8 -*-
 # vi:si:et:sw=4:sts=4:ts=4
 
 ##
@@ -27,12 +27,13 @@
 """
 Daruma FS345 driver
 """
+import re
 import datetime
 from decimal import Decimal
 import time
 
 from kiwi.datatypes import currency
-from kiwi.log import Logger
+from stoqdrivers.logs.log import traced as log
 from kiwi.python import Settable
 from zope.interface import implements
 
@@ -44,24 +45,23 @@
 from stoqdrivers.enum import TaxType, UnitType
 from stoqdrivers.exceptions import (DriverError, PendingReduceZ,
                                     HardwareFailure, ReduceZError,
-                                    AuthenticationFailure, CommError,
+                                    AuthenticationFailure, CodigoInvalido,
                                     CouponNotOpenError,
                                     OutofPaperError, PrinterOfflineError,
                                     CouponOpenError, CancelItemError,
-                                    CloseCouponError)
+                                    CloseCouponError,
+                                    TimeoutError)
 from stoqdrivers.translation import stoqdrivers_gettext
 
 abicomp.register_codec()
 
 _ = stoqdrivers_gettext
 
-log = Logger('stoqdrivers.daruma')
-
 CMD_STATUS = '\x1d\xff'
 
 CMD_SET_OWNER = 190
-# [ESC] 191 Grava��o da indica��o de mudan�a de moeda
-# [ESC] 192 Interven��o T�cnica
+# [ESC] 191 Gravação da indicação de mudança de moeda
+# [ESC] 192 Intervenção Técnica
 CMD_GET_MODEL = 195
 CMD_GET_FIRMWARE = 199
 CMD_OPEN_COUPON = 200
@@ -75,8 +75,11 @@
 CMD_GET_X = 207
 CMD_REDUCE_Z = 208
 CMD_READ_MEMORY = 209
-# [ESC] 210 Emiss�o de Cupom Adicional
-# [ESC] 211 Abertura de Relat�rio Gerencial (Leitura X)
+# [ESC] 210 Emissão de Cupom Adicional
+# [ESC] 211 Abertura de Relatório Gerencial (Leitura X)
+CMD_GERENCIAL_REPORT_OPEN = 211
+CMD_GERENCIAL_REPORT_CLOSE = 212
+CMD_GERENCIAL_REPORT_PRINT = 213
 CMD_CLOSE_NON_FISCAL_BOUND_RECEIPT = 212        # Pg. 37
 CMD_PRINT_LINE_NON_FISCAL_BOUND_RECEIPT = 213   # Pg. 37
 CMD_ADD_ITEM_1L13D = 214
@@ -87,18 +90,18 @@
 CMD_OPEN_NON_FISCAL_BOUND_RECEIPT = 219 # Pg 36
 CMD_CONFIGURE_TAXES = 220
 CMD_LAST_RECORD = 221
-# [ESC] 223 Descri��o de produto em 3 linhas com c�digo de 13 d�gitos
+# [ESC] 223 Descrição de produto em 3 linhas com código de 13 dígitos
 #           (Formato fixo p/ Quantidade 5,3)
 CMD_ADD_ITEM_3L13D53U = 223
-# [ESC] 225 Descri��o de produto com pre�o unit�rio com 3 decimais
+# [ESC] 225 Descrição de produto com preço unitário com 3 decimais
 CMD_DESCRIBE_NON_FISCAL_RECEIPT = 226   # Pg. 42
-# [ESC] 227 Subtotaliza��o de Cupom Fiscal
-# [ESC] 228 Configura��o da IF
+# [ESC] 227 Subtotalização de Cupom Fiscal
+# [ESC] 228 Configuração da IF
 CMD_GET_CONFIGURATION = 229
-# [ESC] 230 Leitura do rel�gio interno da impressora
+# [ESC] 230 Leitura do relógio interno da impressora
 CMD_GET_TAX_CODES = 231
-# [ESC] 232 Leitura do clich� do propriet�rio
-# [ESC] 234 Retransmiss�o de mensagens da IF
+# [ESC] 232 Leitura do clichê do proprietário
+# [ESC] 234 Retransmissão de mensagens da IF
 CMD_GET_IDENTIFIER = 236
 CMD_GET_PERSONAL_MESSAGES = 238
 CMD_GET_DOCUMENT_STATUS = 239
@@ -108,28 +111,93 @@
 CMD_CLOSE_COUPON = 243
 CMD_GET_REGISTRIES = 244
 CMD_GET_TOTALIZERS = 244
-# [ESC] 246 Leitura Hor�ria
-# [ESC] 247 Descri��o Estendida
+# [ESC] 246 Leitura Horária
+# [ESC] 247 Descrição Estendida
 # [ESC] 248 Estorno de forma de pagamento
 CMD_GET_DATES = 250
-# [ESC] 251 Leitura das informa��es cadastrais do usu�rio
-# [ESC] V   Controle de hor�rio de ver�o
+# [ESC] 251 Leitura das informações cadastrais do usuário
+# [ESC] V   Controle de horário de verão
 
 CASH_IN_TYPE = 'B'
 CASH_OUT_TYPE = 'A'
+CMD_ACIONA_GUILHOTINA = 'm'
 
 RETRIES_BEFORE_TIMEOUT = 5
 
+DICT_ERROS = {
+         1: 'Comando habilitado apenas em MIT',
+         2: 'Erro na gravação da MF',
+         3: 'Capacidade da MF esgotada',
+         4: 'Erro no relógio',
+         5: 'Erro Drástico',
+         6: 'Erro de leitura na MF',
+         7: 'Comando habilitado apenas em modo fiscal',
+        10: 'Documento aberto',
+        11: 'Documento não aberto',
+        12: 'Não há documento',
+        13: 'Caracter numérico inválido',
+        14: 'Capacidade da MT esgotada',
+        15: 'Item não encontrado na MT',
+        16: 'Erro de Sintaxe',
+        17: 'Overflow na acumulação',
+        18: 'Selecionado totalizador sem alíquota programada',
+        19: 'MF vazia',
+        20: 'Nenhum campo requer atualização',
+        21: 'Pouco papel',
+        22: 'RZ já emitida',
+        23: 'RZ do movimento anterior ainda pendente',
+        24: 'Desconto ou acréscimo de valor inválido',
+        25: 'Caracter inválido',
+        27: 'Impressora de cheques não disponível',
+        28: 'Valor zero não permitido',
+        29: 'Último documento não foi CF',
+        30: 'Totalizador NF inválido',
+        31: 'Autenticação não permitida neste momento',
+        32: 'Cupom Adicional inibido por comando',
+        35: 'Relógio inoperante',
+        36: 'Versão incorreta do SB',
+        38: 'Meio de pagamento inválido',
+        39: 'Erro na seqüência de fechamento de documento',
+        40: 'Dia fiscal aberto',
+        41: 'Data inválida',
+        42: 'Dia fiscal não aberto',
+        43: 'Não pode emitir CCD',
+        44: 'Operação inválida',
+        45: 'Campo em branco não permitido',
+        48: 'Estorno não é possível',
+        49: 'Pagamento não encontrado na MT',
+        50: 'Fim do papel',
+        51: 'Nenhum usuário cadastrado',
+        52: 'Memória Fiscal não encontrada',
+        61: 'Queda de energia',
+        74: 'Operação com ICMS não permitida',
+        75: 'Operação com ISS não permitida',
+        76: 'Desconto em ISS inibido',
+        77: 'IOF inibido',
+        78: 'Deve ajustar o relógio',
+        79: 'Comando de subtotal não mais suportado',
+        81: 'Banco não cadastrado',
+        82: 'Texto inválido',
+        83: 'Extenso do cheque não cabe',
+        84: 'Impressão do verso não permitida no momento',
+        85: 'Erro de leitura',
+        86: 'Erro na carga',
+        87: 'Parâmetro inválido',
+        90: 'Erro na comunicação',
+}
+
 def isbitset(value, bit):
     # BCD crap
     return (int(value, 16) >> bit) & 1 == 1
 
+
 def ifset(value, bit, false='', true=''):
     if not isbitset(value, bit):
         return false
     else:
         return true
 
+
 class FS345Constants(BaseDriverConstants):
     _constants = {
         UnitType.WEIGHT:      'Kg',
@@ -158,12 +226,19 @@
         self._customer_name = u""
         self._customer_document = u""
         self._customer_address = u""
+    def get_grande_total(self):
+        return Decimal('12452.00')
 
     def send_command(self, command, extra=''):
+        if command == 1:
+            return self._check_status(True)
+        if type(extra) == bool:
+            extra = ''
         raw = chr(command) + extra
+
         while True:
             self.write(self.CMD_PREFIX + raw + self.CMD_SUFFIX)
-            retval = self._read_reply()
+            retval = self.readline()
             if retval.startswith(':E'):
                 try:
                     self.handle_error(retval, raw)
@@ -176,29 +251,10 @@
 
         return retval[1:]
 
-    def _read_reply(self):
-        rep = ''
-        timeouts = 0
-
-        while True:
-            if timeouts > RETRIES_BEFORE_TIMEOUT:
-                raise DriverError('Timeout')
-
-            c = self.read(1)
-            if len(c) != 1:
-                timeouts += 1
-                continue
-
-            if c == self.EOL_DELIMIT:
-                log.debug("<<< %r" % rep)
-                return rep
-
-            rep += c
-
     # Status
     def _get_status(self):
         self.write(CMD_STATUS)
-        return self._read_reply()
+        return self.readline()
 
     def status_check(self, S, byte, bit):
         return isbitset(S[byte], bit)
@@ -216,19 +272,21 @@
             print 'Cashier drawer is', ifset(status[1], 3, 'closed', 'open')
 
         if self.needs_reduce_z():
-            raise PendingReduceZ(_('Pending Reduce Z'))
+            raise PendingReduceZ('Redução Z Pendente')
         if self.status_check(status, 1, 2):
             raise HardwareFailure(_('Mechanical failure'))
         if not self.status_check(status, 1, 1):
             raise AuthenticationFailure(_('Not properly authenticated'))
         if self.status_check(status, 1, 0):
-            raise OutofPaperError(_('No paper'))
+            raise OutofPaperError(_('Sem Papel'))
         if self.status_check(status, 2, 3):
             raise PrinterOfflineError(_("Offline"))
         #if not self.status_check(status, 2, 2):
         #     raise CommError(_("Peripheral is not connected to AUX"))
         if self.status_check(status, 2, 0):
-            log.info('Almost out of paper')
+            log.info('Impressora com pouco papel')
+        if self.status_check(status, 6, 1):
+            raise ReduceZError(_("readZ is already emitted"))
 
         if verbose:
             S3 = status[3]
@@ -237,8 +295,6 @@
             print 'Guillotine', ifset(S3, 1, 'disabled', 'enabled')
             print 'Auto close CF?', ifset(S3, 0, 'no', 'yes')
 
-        if self.status_check(status, 6, 1):
-            raise ReduceZError(_("readZ is already emitted"))
 
         # FIXME: I am not sure we should be doing this here. This method
         # should only check the status, and not emit any other command.
@@ -247,6 +303,9 @@
 
         return status
 
+    def get_corta_papel(self):
+        self.send_command(CMD_ACIONA_GUILHOTINA)
+
     def needs_reduce_z(self, status=None):
         if not status:
             status = self._get_status()
@@ -261,35 +320,39 @@
     # Error handling
 
     def handle_error(self, error_value, raw):
-        error = int(error_value[2:])
+        try:
+            error = int(error_value)
+        except:
+            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)
+        dict_cod_erros = {
+            10: CouponOpenError("Documento sendo emitido"),
+            11: CouponNotOpenError("Documento não foi aberto"),
+            12: CouponNotOpenError("Não existe documento a cancelar"),
+            15: CancelItemError(_("There is no such item in the coupon"), error),
+            16: DriverError("Bad discount/markup parameter", error),
+            21: DriverError(_('Printer error %s: No paper'), error),
+            22: DriverError("Reduce Z was already sent today, try again tomorrow", error),
+            23: PendingReduceZ,
+            24: DriverError("Bad unit specified: %r" % raw, error),
+            39: DriverError('Bad parameters: %r'  % raw, error),
+            40: DriverError("Indice do RG inválido", error),
+            42: CodigoInvalido("Read X has not been sent yet"),
+            45: DriverError(_("Required field is blank"), error),
+            60: DriverError(("Configuração permitida apenas imediatamente a RZ")),
+            67: DriverError("Data informada não coincide com a data do ECF"),
+            72: OutofPaperError(_("Impressora sem papel"), error),
+            78: CouponOpenError(_("CF Aberto"), error),
+            82: CouponOpenError("Não foi possível Abrir o Cupom"),
+            90: DriverError('Totalizador sem alíquota programada'),
+            100: DriverError('Desconto ou acréscimo supera valor bruto'),
+            129: CodigoInvalido('Rel. Gerencial não programado'),
+            144: TimeoutError('ECF OFF LINE'),
+        }
+        if error in dict_cod_erros:
+            raise dict_cod_erros.get(error)
+
         else:
             raise DriverError("Unhandled error: %d" % error, error)
 
@@ -359,7 +422,7 @@
 
     def _add_payment(self, payment_method, value, description=''):
         rv = self.send_command(CMD_DESCRIBE_PAYMENT_FORM,
-                               '%c%012d%s\xff' % (payment_method,
+                               '%c%012d%s\xff' % (int(payment_method),
                                                   int(value * Decimal('1e2')),
                                                   description[:48]))
         # FIXME: Why and when does this happen?
@@ -368,9 +431,10 @@
             rv = rv[8:]
         return Decimal(rv) / Decimal('1e2')
 
-    def _add_voucher(self, type, value):
-        data = "%s1%s%012d\xff" % (type, "0" * 12, # padding
-                                   int(value * Decimal('1e2')))
+    def _add_voucher(self, type, value, text=''):
+        data = "%s1%s%012d%s\xff" % (type, "0" * 12, # padding
+                                     int(value * Decimal('1e2')),
+                                     text[:40]) # maximo 40 caracteres
         self.send_command(CMD_OPEN_VOUCHER, data)
 
     def _configure_taxes(self):
@@ -413,7 +477,12 @@
         status = self._check_status()
         if self._is_open(status):
             raise CouponOpenError(_("Coupon already open"))
-        self.send_command(CMD_OPEN_COUPON)
+        res = self.send_command(CMD_OPEN_COUPON)
+        if res[:1] == 'A':
+            return True
+
+        log.error("Erro 'coupon_open()': %s" % DICT_ERROS.get(int(res[1:])))
+        return False
 
     def coupon_add_item(self, code, description, price, taxcode,
                         quantity=Decimal("1.0"), unit=UnitType.EMPTY,
@@ -456,6 +525,7 @@
         if not self.needs_reduce_z():
             self._check_status()
         self.send_command(CMD_CANCEL_COUPON)
+        self.readline()
 
     def cancel_last_coupon(self):
         """Cancel the last non fiscal coupon or the last sale."""
@@ -498,18 +568,20 @@
                                                       customer_document))
         LINE_LEN = 48
         msg_len = len(message)
+        print 'FS345', message
         if msg_len > LINE_LEN:
             l = []
             for i in range(0, msg_len, LINE_LEN):
                 l.append(message[i:i+LINE_LEN])
             message = '\n'.join(l)
+        message = re.sub(r'NOTA LEGAL:', chr(27) + 'B1' + r'NOTA LEGAL:' + chr(27) + 'B0', message)
         try:
             self.send_command(CMD_CLOSE_COUPON, message + '\xff')
         except DriverError:
             raise CloseCouponError(_("It is not possible to close the "
                                      "coupon"))
         self._reset()
-        return self._get_coupon_number()
+        return True
 
     def summarize(self):
         self.send_command(CMD_GET_X)
@@ -562,22 +634,35 @@
     def payment_receipt_close(self):
         self.send_command(CMD_CLOSE_NON_FISCAL_BOUND_RECEIPT)
 
-    def till_add_cash(self, value):
+    def gerencial_report_open(self, id):
+        self.send_command(CMD_GERENCIAL_REPORT_OPEN)
+
+    def gerencial_report_print(self, text):
+        for line in text.split('\n'):
+            self.send_command(CMD_GERENCIAL_REPORT_PRINT, line + chr(255))
+
+    def gerencial_report_close(self):
+        self.send_command(CMD_GERENCIAL_REPORT_CLOSE)
+
+    def till_add_cash(self, value, totalizador=0, meio=0):
         self._add_voucher(CASH_IN_TYPE, value)
-        self._add_payment('A', value, '')
+#        self._add_payment('A', value, '')
 
-    def till_remove_cash(self, value):
+    def till_remove_cash(self, value, cod=None):
         self._add_voucher(CASH_OUT_TYPE, value)
 
-    def till_read_memory(self, start, end):
+    def till_read_memory(self, start, end, full=False):
         # Page 39
         self.send_command(CMD_READ_MEMORY, 'x%s%s' % (start.strftime('%d%m%y'),
                                                       end.strftime('%d%m%y')))
 
-    def _till_read_memory_to_serial(self, start, end):
+    def _till_read_memory_to_serial(self, start, end, full=False, by_reduction=False):
         # Page 39
-        ret = self.send_command(CMD_READ_MEMORY, 's%s%s' % (start.strftime('%d%m%y'),
-                                                            end.strftime('%d%m%y')))
+        if by_reduction:
+            ret = self.send_command(CMD_READ_MEMORY, 's00%04d00%04d' % (start, end))
+        else:
+            ret = self.send_command(CMD_READ_MEMORY, 's%s%s' % (start.strftime('%d%m%y'), end.strftime('%d%m%y')))
+
         data = ''
         while True:
             line = self.readline()
@@ -586,14 +671,20 @@
             data += line
 
         data = unicode(data, 'cp860')
-        return (ret, data)
+        return data
 
-    def till_read_memory_by_reductions(self, start, end):
+    def till_read_memory_to_serial(self, start, end, full=False):
+        return self._till_read_memory_to_serial(start, end, full=False)
+
+    def till_read_memory_by_reductions_to_serial(self, start, end, full=False):
+        return self._till_read_memory_to_serial(start, end, full=False, by_reduction=True)
+
+    def till_read_memory_by_reductions(self, start, end, full=False):
         # Page 39
         self.send_command(CMD_READ_MEMORY, 'x00%04d00%04d' % (start, end))
 
     def get_capabilities(self):
-        return dict(item_code=Capability(max_len=13),
+        return dict(item_code=Capability(max_len=14),
                     item_id=Capability(digits=3),
                     items_quantity=Capability(min_size=1, digits=5, decimals=3),
                     item_price=Capability(min_size=0, digits=7, decimals=3),
@@ -620,7 +711,7 @@
 
     def get_serial(self):
         identifier = self.send_command(CMD_GET_IDENTIFIER)
-        return identifier[1:9]
+        return identifier[1:21]
 
     def get_ccf(self):
         # Daruma FS345 does not have ccf. See:
@@ -683,6 +774,35 @@
 
         return methods
 
+    def get_totalizadores(self, total=False):
+        '''
+        Esta funcao busca cada totalizador da ecf e seus valores
+        '''
+        fiscal_registries = self._get_fiscal_registers()
+        tax_codes = self.send_command(CMD_GET_TAX_CODES)[1:]
+        taxes = []
+        for i in range(14):
+            reg = tax_codes[i*5+1:i*5+5]
+            if reg == '////':
+                continue
+
+            if tax_codes[i*5] in 'ABCDEFGHIJKLMNOP':
+                type = 'ICMS'
+            else:
+                type = 'ISS'
+
+            sold = fiscal_registries[88+(i*14):102+(i*14)]
+            taxes.append(('%02d' % (i+1), Decimal(sold)/100, type))
+
+        taxes.append(('ACRESC', Decimal(fiscal_registries[313:326]) / 100, 'ICMS'))
+        taxes.append(('DESC', Decimal(fiscal_registries[19:32]) / 100, 'ICMS'))
+        taxes.append(('CANC', Decimal(fiscal_registries[33:46]) / 100, 'ICMS'))
+        taxes.append(('I', Decimal(fiscal_registries[47:60]) / 100, 'ICMS'))
+        taxes.append(('N', Decimal(fiscal_registries[61:74]) / 100, 'ICMS'))
+        taxes.append(('F', Decimal(fiscal_registries[75:88]) / 100, 'ICMS'))
+        return taxes
+
+
     def get_sintegra(self):
         registries = self._get_registers()
         fiscal_registries = self._get_fiscal_registers()
@@ -704,13 +824,19 @@
             sold = fiscal_registries[88+(i*14):102+(i*14)]
             taxes.append((reg, Decimal(sold)/100, type))
 
+        taxes.append(('ACRESC', Decimal(fiscal_registries[313:326]) / 100, 'ICMS'))
         taxes.append(('DESC', Decimal(fiscal_registries[19:32]) / 100, 'ICMS'))
         taxes.append(('CANC', Decimal(fiscal_registries[33:46]) / 100, 'ICMS'))
         taxes.append(('I', Decimal(fiscal_registries[47:60]) / 100, 'ICMS'))
         taxes.append(('N', Decimal(fiscal_registries[61:74]) / 100, 'ICMS'))
         taxes.append(('F', Decimal(fiscal_registries[75:88]) / 100, 'ICMS'))
 
-        total_sold = sum(value for _, value, _ in taxes)
+        total_sold = 0 # total Bruto
+        for tax in taxes:
+            if tax[0] == 'ACRES': # o acresmico ja está computado nas tributacoes
+                continue
+
+            total_sold += tax[1]
 
         old_total = Decimal(fiscal_registries[:18]) / 100
         cancelled = Decimal(fiscal_registries[33:46]) / 100
@@ -734,6 +860,15 @@
              crz=int(registries[39:42]), # FIXME: this is being fetched before the actual
                                          # reduction, so it will offset by one.
              coo=int(registries[6:12]),
-             period_total=period_total,
+             period_total=period_total, # total Bruto
              total=period_total + old_total,
              taxes=taxes)
+
+    def get_coo_inicial_final(self):
+        #Implementar a funcção para daruma
+        coo_inicial = '0'
+        return coo_inicial
+
+    def get_read_ie(self):
+        #Implementar função para daruma
+        return 'Isento'
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/dataregis/EP375.py trunk/stoqdrivers/printers/dataregis/EP375.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/dataregis/EP375.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/dataregis/EP375.py	2016-10-13 16:22:19.376311895 -0300
@@ -609,7 +609,7 @@
     def summarize(self):
         self._send_command(self.CMD_READ_X)
 
-    def till_add_cash(self, value):
+    def till_add_cash(self, value, totalizador=0, meio=0):
         value = format_value(value, 32)
         self._send_command(self.CMD_GERENCIAL_REPORT, "01", "Valor = " + value)
         self._send_command(self.CMD_CLOSE_GERENCIAL_REPORT)
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/elgin/ELGINK.py trunk/stoqdrivers/printers/elgin/ELGINK.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/elgin/ELGINK.py	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/printers/elgin/ELGINK.py	2016-10-13 16:22:19.348312401 -0300
@@ -0,0 +1,42 @@
+# -*- Mode: Python; coding: utf-8 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Stoqdrivers
+## Copyright (C) 2005-2007 Async Open Source <http://www.async.com.br>
+## All rights reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 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 General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+## USA.
+##
+## Author(s):   Henrique Romano <henrique@async.com.br>
+##              Johan Dahlin <henrique@async.com.br>
+##
+"""
+PertoPay 2023 driver implementation.
+"""
+
+from stoqdrivers.printers.fiscnet.FiscNetECF import FiscNetChequePrinter
+from stoqdrivers.translation import stoqdrivers_gettext
+
+_ = stoqdrivers_gettext
+
+
+class ELGINK(FiscNetChequePrinter):
+    log_domain = 'PertoPay2023'
+    supported = True
+
+    model_name = "Elgin K"
+
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/fiscal.py trunk/stoqdrivers/printers/fiscal.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/fiscal.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/fiscal.py	2017-01-23 16:54:44.689415054 -0200
@@ -1,37 +1,35 @@
 # -*- Mode: Python; coding: utf-8 -*-
 # vi:si:et:sw=4:sts=4:ts=4
 
-##
-## Stoqdrivers
-## Copyright (C) 2005-2007 Async Open Source <http://www.async.com.br>
-## All rights reserved
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 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 General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-## USA.
-##
-## Author(s):   Johan Dahlin     <jdahlin@async.com.br>
-##              Henrique Romano  <henrique@async.com.br>
-##
+#
+# Stoqdrivers
+# Copyright (C) 2005-2007 Async Open Source <http://www.async.com.br>
+# All rights reserved
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+# USA.
+#
+# Author(s):   Johan Dahlin     <jdahlin@async.com.br>
+#              Henrique Romano  <henrique@async.com.br>
+#
 
 import datetime
 from decimal import Decimal
-
 from kiwi.datatypes import currency
-from kiwi.argcheck import number, percent
-from kiwi.log import Logger
-
+from kiwi.argcheck import number
+from stoqdrivers.logs.log import traced as log
 from stoqdrivers.exceptions import (CloseCouponError, PaymentAdditionError,
                                     AlreadyTotalized, InvalidValue)
 from stoqdrivers.enum import PaymentMethodType, TaxType, UnitType
@@ -42,11 +40,11 @@
 
 _ = stoqdrivers_gettext
 
-log = Logger('stoqdrivers.fiscalprinter')
-
 #
 # Extra data types to argcheck
 #
+TWOPLACES = Decimal(10) ** -2
+
 
 class taxcode(number):
     @classmethod
@@ -55,6 +53,7 @@
                          TaxType.EXEMPTION):
             raise ValueError("%s must be one of TaxType.* constants" % name)
 
+
 class unit(number):
     @classmethod
     def value_check(cls, name, value):
@@ -62,6 +61,7 @@
                          UnitType.EMPTY, UnitType.CUSTOM):
             raise ValueError("%s must be one of UNIT_* constants" % name)
 
+
 class payment_method(number):
     @classmethod
     def value_check(cls, name, value):
@@ -69,6 +69,7 @@
                          PaymentMethodType.CUSTOM):
             raise ValueError("%s must be one of *_PM constants" % name)
 
+
 #
 # FiscalPrinter interface
 #
@@ -76,6 +77,7 @@
 class FiscalPrinter(BasePrinter):
     def __init__(self, brand=None, model=None, device=None, config_file=None,
                  *args, **kwargs):
+
         BasePrinter.__init__(self, brand, model, device, config_file, *args,
                              **kwargs)
         self._has_been_totalized = False
@@ -83,6 +85,11 @@
         self.totalized_value = Decimal("0.0")
         self._capabilities = self._driver.get_capabilities()
         self._charset = self._driver.coupon_printer_charset
+        self._serial = None
+
+    def refresh(self):
+        print dir(self)
+        # self.refresh()
 
     def get_capabilities(self):
         return self._capabilities
@@ -92,222 +99,426 @@
 
     @capcheck(basestring, basestring, basestring)
     def identify_customer(self, customer_name, customer_address, customer_id):
-        log.info('identify_customer(customer_name=%r, '
-                  'customer_address=%r, customer_id=%r)' % (
-            customer_name, customer_address, customer_id))
-
-        self._driver.coupon_identify_customer(
-            self._format_text(customer_name),
-            self._format_text(customer_address),
-            self._format_text(customer_id))
+        log.info('> CMD: identify_customer(customer_name=%r, customer_address=%r, customer_id=%r)'
+                 % (customer_name, customer_address, customer_id))
+
+        res = self._driver.coupon_identify_customer(self._format_text(customer_name),
+                                                    self._format_text(customer_address),
+                                                    self._format_text(customer_id))
+        log.info('< RET: %s' % str(res))
+        return res
 
     def coupon_is_customer_identified(self):
-        return self._driver.coupon_is_customer_identified()
+        log.info('> CMD: coupon_is_customer_identified()')
+        res = self._driver.coupon_is_customer_identified()
+        log.info('< RET: %s' % str(res))
+        return res
 
     def open(self):
-        log.info('coupon_open()')
-
-        return self._driver.coupon_open()
+        log.info('> CMD: coupon_open()')
+        res = self._driver.coupon_open()
+        log.info('< RET: ' + str(res))
+        return res
 
-    @capcheck(basestring, basestring, Decimal, str, Decimal, unit,
-              Decimal, Decimal, basestring)
+    @capcheck(basestring, basestring, Decimal, str, Decimal, unit, Decimal, Decimal, basestring)
     def add_item(self, item_code, item_description, item_price, taxcode,
-                 items_quantity=Decimal("1.0"), unit=UnitType.EMPTY,
-                 discount=Decimal("0.0"), surcharge=Decimal("0.0"),
-                 unit_desc=""):
-        log.info("add_item(code=%r, description=%r, price=%r, "
-                 "taxcode=%r, quantity=%r, unit=%r, discount=%r, "
-                 "surcharge=%r, unit_desc=%r)" % (
-            item_code, item_description, item_price, taxcode,
-            items_quantity, unit, discount, surcharge, unit_desc))
+                 items_quantity=Decimal("1.0"), unit=UnitType.EMPTY, discount=Decimal("0.0"),
+                 surcharge=Decimal("0.0"), unit_desc=""):
+
+        log.info("> CMD: add_item(code=%r, description=%r, price=%r, taxcode=%r, quantity=%r, "
+                 "unit=%r, discount=%r, surcharge=%r, unit_desc=%r)" %
+                 (item_code, item_description, item_price, taxcode, items_quantity, unit, discount,
+                  surcharge, unit_desc))
 
         if self._has_been_totalized:
-            raise AlreadyTotalized("the coupon is already totalized, you "
-                                   "can't add more items")
+            err = "O Cupom já foi totalizado, você não pode mais adicionar itens"
+            log.error('< ERR: ' + err)
+            raise AlreadyTotalized(err)
+
         if discount and surcharge:
-            raise TypeError("discount and surcharge can not be used together")
+            err = "discount and surcharge can not be used together"
+            log.error('< ERR: ' + err)
+            raise TypeError(err)
+
         elif unit != UnitType.CUSTOM and unit_desc:
-            raise ValueError("You can't specify the unit description if "
-                             "you aren't using UnitType.CUSTOM constant.")
+            err = "You can't specify the unit description if you aren't using UnitType.CUSTOM constant."
+            log.error('< ERR: ' + err)
+            raise ValueError(err)
+
         elif unit == UnitType.CUSTOM and not unit_desc:
-            raise ValueError("You must specify the unit description when "
-                             "using UnitType.CUSTOM constant.")
+            err = "You must specify the unit description when using UnitType.CUSTOM constant."
+            log.error('< ERR: ' + err)
+            raise ValueError(err)
+
         elif unit == UnitType.CUSTOM and len(unit_desc) != 2:
-            raise ValueError("unit description must be 2-byte sized string")
+            err = "unit description must be 2-byte sized string"
+            log.error('< ERR: ' + err)
+            raise ValueError(err)
+
         if not item_price:
-            raise InvalidValue("The item value must be greater than zero")
+            err = "The item value must be greater than zero"
+            log.error('< ERR: ' + err)
+            raise InvalidValue(err)
+
+        retval = self._driver.coupon_add_item(self._format_text(item_code),
+                                              self._format_text(item_description),
+                                              item_price, taxcode, items_quantity,
+                                              unit, discount, surcharge,
+                                              unit_desc=self._format_text(unit_desc))
+        log.info('< RET: ' + str(retval))
+        return retval
 
-        return self._driver.coupon_add_item(
-            self._format_text(item_code), self._format_text(item_description),
-            item_price, taxcode, items_quantity, unit, discount, surcharge,
-            unit_desc=self._format_text(unit_desc))
+    def desc_acres_item(self, desc_acres, item, valor):
+        log.info('> CMD: desc_acres_item(desc_acres=%s, item=%s, valor=%s)' % (desc_acres, item, valor))
+        res = self._driver.desc_acres_item(desc_acres, item, valor)
+        log.info('< RET: %s' % str(res))
+        return res
 
     @capcheck(Decimal, Decimal, taxcode)
     def totalize(self, discount=currency(0), surcharge=currency(0),
                  taxcode=TaxType.NONE):
-        log.info('totalize(discount=%r, surcharge=%r, taxcode=%r)' % (
+        log.info('> CMD: totalize(discount=%r, surcharge=%r, taxcode=%r)' % (
             discount, surcharge, taxcode))
 
         if discount and surcharge:
-            raise TypeError("discount and surcharge can not be used together")
+            err = "discount and surcharge can not be used together"
+            log.error('< ERR: ' + err)
+            raise TypeError(err)
+
+        if discount > self.get_coupon_subtotal():
+            err = "Valor do Desconto/Acrescimo nao pode ser maior que o Total do Cupom"
+            log.error('< ERR: ' + err)
+            raise ValueError(err)
+
         if surcharge and taxcode == TaxType.NONE:
-            raise ValueError("to specify a surcharge you need specify its "
-                             "tax code")
-        result = self._driver.coupon_totalize(discount, surcharge, taxcode)
+            err = "to specify a surcharge you need specify its tax code"
+            log.error('< ERR: ' + err)
+            raise ValueError(err)
+
+        res = self._driver.coupon_totalize(discount, surcharge, taxcode)
         self._has_been_totalized = True
-        self.totalized_value = result
-        return result
+        self.totalized_value = res
+
+        log.info("< RET: %s\n%s" % (str(res), "FIM DO TOTALIZE ================================================================"))
+
+        return res
+
+    def configure_payment_methods(self, descricao=u''):
+        log.info('> CMD: configure_payment_methods(descricao=%r)' % descricao)
+        res = self._driver._configure_payment_methods(descricao)
+        log.info('< RET: %s' % str(res))
+        return res
 
     @capcheck(basestring, Decimal, basestring)
     def add_payment(self, payment_method, payment_value, description=''):
-        log.info("add_payment(method=%r, value=%r, description=%r)" % (
-            payment_method, payment_value, description))
+        log.info("> CMD: add_payment(method=%r, value=%r, description=%r)" % (payment_method,
+                                                                              payment_value,
+                                                                              description))
 
         if not self._has_been_totalized:
-            raise PaymentAdditionError(_("You must totalize the coupon "
-                                         "before add payments."))
-        result = self._driver.coupon_add_payment(
-            payment_method, payment_value,
-            self._format_text(description))
+            err = "Você deve totalizar o cupom antes de fecha-lo."
+            log.error('< ERR: %s' % err)
+            raise PaymentAdditionError(_(err))
+
+        res = self._driver.coupon_add_payment(payment_method, payment_value,
+                                              self._format_text(description))
+
+        log.info('< RET: %s' % str(res))
+
         self.payments_total_value += payment_value
-        return result
+        return res
+
+    def open_cash_drawer(self, sec=0):
+        log.info('> CMD: open_cash_drawer(sec=%r)' % sec)
+        res = self._driver.open_cash_drawer(sec)
+        log.info('< RET: %s' % str(res))
+        return res
+
+    def get_status_cash_drawer(self):
+        log.info('> CMD: get_status_cash_drawer()')
+        res = self._driver.get_status_cash_drawer()
+        log.info('< RET: %s' % str(res))
+        return res
 
     def cancel(self):
-        log.info('coupon_cancel()')
-        retval = self._driver.coupon_cancel()
+        log.info('> CMD: coupon_cancel()')
+        res = self._driver.coupon_cancel()
+        log.info('< RET: %s' % str(res))
         self._has_been_totalized = False
         self.payments_total_value = Decimal("0.0")
         self.totalized_value = Decimal("0.0")
-        return retval
+        return res
 
     def cancel_last_coupon(self):
         """Cancel the last non fiscal coupon or the last sale."""
-        self._driver.cancel_last_coupon()
+        log.info('> CMD: cancel_last_coupon()')
+        res = self._driver.cancel_last_coupon()
+        log.info('< RET: %s' % str(res))
+        return res
 
-    @capcheck(int)
-    def cancel_item(self, item_id):
-        log.info('coupon_cancel_item(item_id=%r)' % (item_id,))
+    def _get_last_item_id(self):
+        log.info('> CMD: _get_last_item_id()')
+        res = self._driver._get_last_item_id()
+        log.info('< RET: %s' % str(res))
+        return res
 
-        return self._driver.coupon_cancel_item(item_id)
+
+    @capcheck(int)
+    def cancel_item(self, item_id=None):
+        log.info('> CMD: coupon_cancel_item(item_id=%r)' % (item_id,))
+        res = self._driver.coupon_cancel_item(item_id)
+        log.info('< RET: %s' % str(res))
+        return res
 
     @capcheck(basestring)
     def close(self, promotional_message=''):
-        log.info('coupon_close(promotional_message=%r)' % (
-            promotional_message))
+        log.info('> CMD: coupon_close(promotional_message=%r)' % promotional_message)
 
         if not self._has_been_totalized:
-            raise CloseCouponError(_("You must totalize the coupon before "
-                                     "closing it"))
+            err = "Você deve totalizar o cupom antes de fecha-lo."
+            log.error('< ERR: %s' % err)
+            raise CloseCouponError(_(err))
+
         if not self.payments_total_value:
-            raise CloseCouponError(_("It is not possible close the coupon "
-                                     "since there are no payments defined."))
+            err = "Não é possível fechar o cupom, pois não existem pagamentos definidos."
+            log.error('< ERR: %s' % err)
+            raise CloseCouponError(_(err))
+
         if self.totalized_value > self.payments_total_value:
-            raise CloseCouponError(_("Isn't possible close the coupon since "
-                                     "the payments total (%.2f) doesn't "
-                                     "match the totalized value (%.2f).")
-                                   % (self.payments_total_value,
-                                      self.totalized_value))
-        res = self._driver.coupon_close(
-            self._format_text(promotional_message))
+            err = "Não é possível fechar o cupom porque os pagamentos totais (%.2f) " \
+                  "não coicide com o valor totalizado (%.2f)."
+            log.error('< ERR: %s' % err)
+            raise CloseCouponError(_(err) % (self.payments_total_value, self.totalized_value))
+
+        res = self._driver.coupon_close(self._format_text(promotional_message))
+        log.info('< RET: %s' % str(res))
+
+        self.get_status().check_error_two()  # Verifica se teve algum erro na transacao com ECF
         self._has_been_totalized = False
         self.payments_total_value = Decimal("0.0")
         self.totalized_value = Decimal("0.0")
+
         return res
 
     def summarize(self):
-        log.info('summarize()')
+        log.info('> CMD: summarize()')
+        res = self._driver.summarize()
+        log.info('< RET: %s' % str(res))
+        return res
 
-        return self._driver.summarize()
+    def get_totalizadores(self, total=False):
+        log.info('> CMD: get_totalizadores(total=%s)' % total)
+        res = self._driver.get_totalizadores(total=total)
+        log.info('< RET: %s' % str(res))
+        return res
 
     def close_till(self, previous_day=False):
-        log.info('close_till(previous_day=%r)' % (previous_day,))
-
-        return self._driver.close_till(previous_day)
+        log.info('> CMD: close_till(previous_day=%r)' % (previous_day,))
+        res = self._driver.close_till(previous_day)
+        log.info('< RET: %s' % str(res))
+        return res
 
-    @capcheck(Decimal)
-    def till_add_cash(self, add_cash_value):
-        log.info('till_add_cash(add_cash_value=%r)' % (add_cash_value,))
+    def _add_voucher(self, type, add_voucher_value, text=''):
+        log.info('> CMD: _add_voucher(type=%r, add_voucher_value=%r, text=%r)'
+                 % (type, add_voucher_value, text))
+        res = self._driver._add_voucher(type, add_voucher_value, text)
+        log.info('< RET: %s' % str(res))
+        return res
 
-        return self._driver.till_add_cash(add_cash_value)
+    @capcheck(Decimal, int, int)
+    def till_add_cash(self, add_cash_value, totalizador=0, meio=0):
+        log.info('> CMD: till_add_cash(add_cash_value=%r, totalizador=%r, meio=%r)'
+                 % (add_cash_value, totalizador, meio))
+        res = self._driver.till_add_cash(add_cash_value, totalizador=totalizador, meio=meio)
+        log.info('< RET: %s' % str(res))
+        return res
 
-    @capcheck(Decimal)
-    def till_remove_cash(self, remove_cash_value):
-        log.info('till_remove_cash(remove_cash_value=%r)' % (
-            remove_cash_value,))
+    @capcheck(Decimal, int, str)
+    def till_remove_cash(self, remove_cash_value, cod=0, text=''):
+        log.info('> CMD: till_remove_cash(remove_cash_value=%r, cod=%r, text=%r)'
+                 % (remove_cash_value, cod, text))
 
-        return self._driver.till_remove_cash(remove_cash_value)
+        res = self._driver.till_remove_cash(remove_cash_value, cod=cod, text=text)
+        log.info('< RET: %s' % str(res))
+        return res
 
-    @capcheck(datetime.date, datetime.date)
-    def till_read_memory(self, start, end):
+    @capcheck(datetime.date, datetime.date, bool)
+    def till_read_memory(self, start, end, full=False):
         assert start <= end <= datetime.date.today(), (
             "start must be less then end and both must be less today")
-        log.info('till_read_memory(start=%r, end=%r)' % (
-            start, end))
 
-        return self._driver.till_read_memory(start, end)
+        log.info('> CMD: till_read_memory(start=%r, end=%r)' % (start, end))
+
+        res = self._driver.till_read_memory(start, end, full)
+        log.info('< RET: %s' % str(res))
+        return res
 
-    @capcheck(datetime.date, datetime.date)
-    def till_read_memory_to_serial(self, start, end):
+    @capcheck(datetime.date, datetime.date, bool)
+    def till_read_memory_to_serial(self, start, end, full=False):
         assert start <= end <= datetime.date.today(), (
             "start must be less then end and both must be less today")
-        log.info('till_read_memory(start=%r, end=%r)' % (
-            start, end))
 
-        return self._driver.till_read_memory_to_serial(start, end)
+        log.info('> CMD: till_read_memory(start=%r, end=%r)' % (start, end))
+
+        res = self._driver.till_read_memory_to_serial(start, end, full)
+        log.info('< RET: %s' % res)
+        return res
+
+    @capcheck(int, int, bool)
+    def till_read_memory_by_reductions(self, start, end, full=False):
+        assert end >= start > 0, ("start must be less then end and both must be positive")
+        log.info('> CMD: till_read_memory_by_reductions(start=%r, end=%r)' % (start, end))
+
+        res = self._driver.till_read_memory_by_reductions(start, end, full)
+        log.info('< RET: %s' % str(res))
+        return res
+
+    @capcheck(int, int, bool)
+    def till_read_memory_by_reductions_to_serial(self, start, end, full=False):
+        assert end >= start > 0, ("start must be less then end and both must be positive")
+        log.info('> CMD: till_read_memory_by_reductions(start=%r, end=%r)' % (start, end))
+        res = self._driver.till_read_memory_by_reductions_to_serial(start, end, full)
+        log.info('< RET: %s' % res)
+        return res
 
-    @capcheck(int, int)
-    def till_read_memory_by_reductions(self, start, end):
-        assert end >= start > 0, ("start must be less then end "
-                                  "and both must be positive")
-        log.info('till_read_memory_by_reductions(start=%r, end=%r)' % (
-            start, end))
+    def gerencial_report_open(self, id=0):
+        log.info('> CMD: gerencial_report_open(id=%r)' % id)
+        res = self._driver.gerencial_report_open(id=id)
+        log.info('< RET: %s' % str(res))
+        return res
+
+    def gerencial_report_print(self, text):
+        log.info('> CMD: gerencial_report_print(text=%s)' % text)
+        res = self._driver.gerencial_report_print(text)
+        log.info('< RET: %s' % str(res))
+        return res
 
-        self._driver.till_read_memory_by_reductions(start, end)
+    def gerencial_report_close(self):
+        log.info('> CMD: gerencial_report_open()')
+        res = self._driver.gerencial_report_close()
+        log.info('< RET: %s' % str(res))
+        return res
 
     def payment_receipt_open(self, identifier, coo, method, value):
-        log.info('payment_receipt_open(identifier=%s, coo=%s, method=%s, value=%s)'
-                  % (identifier, coo, method, value))
-        return self._driver.payment_receipt_open(identifier, coo, method, value)
+        log.info('> CMD: payment_receipt_open(identifier=%s, coo=%s, method=%s, value=%s)'
+                 % (identifier, coo, method, value))
+        res = self._driver.payment_receipt_open(identifier=identifier, coo=coo, method=method,
+                                                value=value)
+        log.info('< RET: %s' % str(res))
+        return res
 
     def payment_receipt_print(self, text):
-        log.info('payment_receipt_print(text=%s)' % text)
-        return self._driver.payment_receipt_print(text)
+        log.info('> CMD: payment_receipt_print(text=%s)' % text)
+        res = self._driver.payment_receipt_print(text)
+        log.info('< RET: %s' % str(res))
+        return res
 
     def payment_receipt_close(self):
-        log.info('payment_receipt_close()')
-        return self._driver.payment_receipt_close()
+        log.info('> CMD: payment_receipt_close()')
+        res = self._driver.payment_receipt_close()
+        log.info('< RET: %s' % str(res))
+        return res
 
     def payment_receipt_print_duplicate(self):
-        log.info('payment_receipt_print_duplicate()')
-        return self._driver.payment_receipt_print_duplicate()
+        log.info('> CMD: payment_receipt_print_duplicate()')
+        res = self._driver.payment_receipt_print_duplicate()
+        log.info('< RET: %s' % str(res))
+        return res
 
     def get_serial(self):
-        log.info('get_serial()')
+        log.info('> CMD: get_serial()')
+        self._serial = self._driver.get_serial() if not self._serial else self._serial
+        res = self._serial
+        log.info('< RET: %s' % res)
+        return res
 
-        return self._driver.get_serial()
+    def get_serial_mfd(self):
+        log.info('> CMD: get_serial()')
+        res = self._driver.get_serial_mfd()
+        log.info('< RET: %s' % str(res))
+        return res
 
     def query_status(self):
-        log.info('query_status()')
+        log.info('> CMD: query_status()')
+        res = self._driver.query_status()
+        log.info('< RET: %s' % str(res))
+        return res
+
+    def send_command(self, command, *args, **kwargs):
+        res = self._driver._send_command(command, *args, **kwargs)
+        return res
+
+    def get_status(self, val=None, cmd_status=None):
+        log.info('> CMD: get_status(val=%r, cmd_status=%r)' % (val, cmd_status))
+        res = self._driver.get_status(val, cmd_status)
+        log.info('< RET: %s' % str(res))
+        return res
+
+    def get_ecf_date(self):
+        log.info('> CMD: get_ecf_date()')
+        res = self._driver.get_ecf_date()
+        log.info('< RET: %s' % str(res))
+        return res
+
+    def get_coupon_subtotal(self):
+        log.info('> CMD: get_coupon_subtotal()')
+        res = self._driver._get_coupon_subtotal()
+        log.info('< RET: %s' % str(res))
+        return res
+
+    def emite_leitura_fita_detalhe(self, *args, **kwargs):
+        log.info('> CMD: emite_leitura_fita_detalhe(%s,%s)' % (args, kwargs))
+        res = self._driver._emite_leitura_fita_detalhe(args, kwargs)
+        log.info('< RET: %s' % str(res))
+        return res
+
+    def le_impressao(self):
+        log.info('> CMD: le_impressao()')
+        res = self._driver._le_impressao()
+        log.info('< RET: %s' % str(res))
+        return res
 
-        return self._driver.query_status()
+    def set_summertime(self, value=True):
+        log.info('> CMD: set_summertime(value=%r)' % value)
+        res = self._driver.set_summertime(value)
+        log.info('< RET: %s' % str(res))
+        return res
+
+    def get_summertime(self):
+        log.info('> CMD: get_summertime()')
+        res = self._driver.get_summertime()
+        log.info('< RET: %s' % str(res))
+        return res
 
     def status_reply_complete(self, reply):
-        log.info('status_reply_complete(%s)' % (reply,))
-        return self._driver.status_reply_complete(reply)
+        log.info('> CMD: status_reply_complete(%s)' % (reply,))
+        res = self._driver.status_reply_complete(reply)
+        log.info('< RET: %s' % str(res))
+        return res
 
     def get_tax_constants(self):
-        log.info('get_tax_constants()')
+        log.info('> CMD: get_tax_constants()')
+        res = self._driver.get_tax_constants()
+        log.info('< RET: %s' % str(res))
+        return res
 
-        return self._driver.get_tax_constants()
+    def get_tax(self):
+        log.info('> CMD: get_tax()')
+        res = self._driver.get_tax()
+        log.info('< RET: %s' % res)
+        return res
 
     def get_payment_constants(self):
-        log.info('get_payment_constants()')
-
-        return self._driver.get_payment_constants()
+        log.info('> CMD: get_payment_constants()')
+        res = self._driver.get_payment_constants()
+        log.info('< RET: %s' % res)
+        return res
 
     def get_payment_receipt_identifier(self, method):
-        log.info('get_payment_receipt_identifier(method=%s)' % method)
-        return self._driver.get_payment_receipt_identifier(method)
+        log.info('> CMD: get_payment_receipt_identifier(method=%s)' % method)
+        res = self._driver.get_payment_receipt_identifier(method)
+        log.info('< RET: %s' % res)
+        return res
 
     def get_ccf(self):
         """Fiscal Coupon Counter
@@ -315,9 +526,10 @@
         @returns: the last document ccf
         @rtype: integer
         """
-        log.info('get_ccf()')
-
-        return self._driver.get_ccf()
+        log.info('> CMD: get_ccf()')
+        res = self._driver.get_ccf()
+        log.info('< RET: %s' % res)
+        return res
 
     def get_coo(self):
         """Operation Order Counter
@@ -325,9 +537,30 @@
         @returns: the last document coo
         @rtype: integer
         """
-        log.info('get_coo()')
+        log.info('> CMD: get_coo()')
+        res = self._driver.get_coo()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_coo_inicial_final(self):
+        """Operation Order Counter
+            @returns: the last document coo
+            @rtype: integer
+            """
+        log.info('> CMD: get_coo_inicial_final()')
+        res = self._driver.get_coo_inicial_final()
+        log.info('< RET: %s' % res)
+        return res
 
-        return self._driver.get_coo()
+    def get_read_ie(self):
+        """
+        @returns: Iscrição estadual
+        @return:
+        """
+        log.info('> CMD: get_read_ie()')
+        res = self._driver.get_read_ie()
+        log.info('< RET: %s' % res)
+        return res
 
     def get_gnf(self):
         """Nonfiscal Operation General Counter
@@ -335,9 +568,10 @@
         @returns: gnf
         @rtype: integer
         """
-        log.info('get_gnf()')
-
-        return self._driver.get_gnf()
+        log.info('> CMD: get_gnf()')
+        res = self._driver.get_gnf()
+        log.info('< RET: %s' % res)
+        return res
 
     def get_crz(self):
         """Z Reduction Counter
@@ -345,15 +579,136 @@
         @returns: the last document crz
         @rtype: integer
         """
-        log.info('get_crz()')
+        log.info('> CMD: get_crz()')
+        res = self._driver.get_crz()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_cfc(self):
+        """Cupom Fiscal Cancelado
 
-        return self._driver.get_crz()
+        @returns: Total de Cupoms Cancelados
+        @rtype: integer
+        """
+        log.info('> CMD: get_cfc()')
+        res = self._driver.get_cfc()
+        log.info('< RET: %s' % res)
+        return res
 
+    def get_cro(self):
+        """Cupom de Operacao nao Fiscal
+
+        @returns: Total de operacoes nao fiscais
+        @rtype: integer
+        """
+        log.info('> CMD: get_cro()')
+        res = self._driver.get_cro()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_grg(self):
+        """Contador Geral de Relatório Gerencial
+
+        @returns: Total de Relatório Gerencial
+        @rtype: integer
+        """
+        log.info('> CMD: get_grg()')
+        res = self._driver.get_grg()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_cdc(self):
+        u"""Contador de Comprovantes de Débito ou Crédito
+
+        @returns: Total de Comprovantes de Débito ou Crédito
+        @rtype: integer
+        """
+        log.info('> CMD: get_cdc()')
+        res = self._driver.get_cdc()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_grande_total(self):
+        log.info('> CMD: get_grande_total()')
+        res = self._driver.get_grande_total()
+        log.info('< RET: %s' % res)
+        return res
+
+    def retorno_de_variaveis(self, var, tam):
+        log.info('> CMD: retorno_de_variaveis(var%r, tam%r)' % (var, tam))
+        res = self._driver.retorno_de_variaveis(var, tam)
+        log.info('< RET: %s' % res)
+        return res
+
+    def ler_ultima_reducao(self):
+        log.info('> CMD: ler_ultima_reducao()')
+        res = self._driver.ler_ultima_reducao()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_date_last_mov_rz(self):
+        log.info('> CMD: get_date_last_mov_rz()')
+        res = self._driver.get_date_last_mov_rz()
+        log.info('< RET: %s' % res)
+        return res
 
     def get_sintegra(self):
-        log.info('get_sintegra()')
+        log.info('> CMD: get_sintegra()')
+        res = self._driver.get_sintegra()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_data_mov(self):
+        log.info('> CMD: get_data_mov()')
+        res = self._driver.get_data_mov()
+        log.info('< RET: %s' % res)
+        return res
 
-        return self._driver.get_sintegra()
+    def get_date_sb(self):
+        log.info('> CMD: get_date_sb()')
+        res = self._driver.get_date_sb()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_counters(self):
+        log.info('> CMD: get_counters()')
+        if self.brand != 'sweda':
+            contadores = {
+                'coo': self.get_coo(),
+                'ccf': self.get_ccf(),
+                'gnf': self.get_gnf(),
+                'crz': self.get_crz(),
+                'grg': self.get_grg(),
+                'cdc': self.get_cdc()
+            }
+        else:
+            contadores = self._driver.get_counters()
+        log.info('< RET: %s' % contadores)
+        return contadores
+
+    def identifier_pafecf(self, text):
+        log.info('> CMD: identifier_pafecf()')
+        res = self._driver.identifier_pafecf(text)
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_last_reduce(self):
+        log.info('> CMD: get_last_reduce()')
+        res = self._driver.get_last_reduce()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_num_loja(self):
+        log.info('> CMD: get_num_loja()')
+        res = self._driver.get_num_loja()
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_num_ecf(self):
+        log.info('> CMD: get_num_ecf()')
+        res = self._driver.get_num_ecf()
+        log.info('< RET: %s' % res)
+        return res
 
     @property
     def supports_duplicate_receipt(self):
@@ -361,4 +716,19 @@
 
     @property
     def identify_customer_at_end(self):
-        return self._driver.identify_customer_at_end
+        log.info('> CMD: identify_customer_at_end()')
+        res = self._driver.identify_customer_at_end
+        log.info('< RET: %s' % res)
+        return res
+
+    def get_emitir_mfd(self, relatorio, inicio, fim, dir_arq, ecf, tipo='1', dir_bin=''):
+        """
+        Função especifica para ECF Sweda.
+        """
+        return self._driver.get_emitir_mfd(relatorio, inicio, fim, dir_arq, ecf, tipo, dir_bin)
+
+    def get_pagamento_existe(self):
+        log.info('> CMD: get_pagamento_existe()')
+        res = self._driver.get_pagamento_existe()
+        log.info('< RET: %s' % res)
+        return res
\ Falta o caracter nova linha no final do arquivo
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/fiscnet/FiscNetECF.py trunk/stoqdrivers/printers/fiscnet/FiscNetECF.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/fiscnet/FiscNetECF.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/fiscnet/FiscNetECF.py	2017-01-23 16:54:44.641415359 -0200
@@ -34,16 +34,18 @@
 
 from serial import PARITY_EVEN
 from zope.interface import implements
+from sigpdv.tools.config import config
 
 from kiwi.datatypes import currency
 from kiwi.python import Settable
 
+from stoqdrivers.logs.log import traced as log
 from stoqdrivers.enum import PaymentMethodType, TaxType, UnitType
 from stoqdrivers.exceptions import (DriverError, PendingReduceZ,
     CommandParametersError, CommandError, ReadXError, OutofPaperError,
-    CouponTotalizeError, PaymentAdditionError, CancelItemError,
+    CouponTotalizeError, PaymentAdditionError, CancelItemError, SummerTime,
     CouponOpenError, InvalidState, PendingReadX, CloseCouponError,
-    CouponNotOpenError)
+    CouponNotOpenError, PrinterOfflineError, BlockZ)
 from stoqdrivers.interfaces import ICouponPrinter, IChequePrinter
 from stoqdrivers.printers.capabilities import Capability
 from stoqdrivers.printers.cheque import BaseChequePrinter, BankConfiguration
@@ -88,6 +90,9 @@
     FLAG_MFD_ESGOTADA: 'FLAG_MFD_ESGOTADA',
     }
 
+TWOPLACES = Decimal(10) ** -2
+TREPLACES = Decimal(10) ** -3
+
 
 class FiscNetConstants(BaseDriverConstants):
     _constants = {
@@ -126,7 +131,7 @@
     identify_customer_at_end = False
 
 
-    CHEQUE_CONFIGFILE = 'perto.ini'
+    CHEQUE_CONFIGFILE = 'elgin.ini'
 
     CMD_PREFIX = '{'
     CMD_SUFFIX = '}'
@@ -140,10 +145,13 @@
         8013: CouponTotalizeError,
         8014: PaymentAdditionError,
         8017: CloseCouponError,
+        8023: SummerTime,
+        8024: SummerTime,
         8044: CancelItemError,
         8045: CancelItemError,
         8068: PaymentAdditionError,
         8086: CancelItemError,
+
         15009: PendingReduceZ,
         11002: CommandParametersError,
         11006: CommandError,
@@ -154,7 +162,8 @@
         }
 
     def __init__(self, port, consts=None):
-        port.set_options(baudrate=115200, parity=PARITY_EVEN)
+        port.set_options(baudrate=115200, parity=PARITY_EVEN,
+                         write_timeout=3, read_timeout=3)
         SerialBase.__init__(self, port)
         self._consts = consts or FiscNetConstants
         self._command_id = 0
@@ -186,7 +195,8 @@
                 if m:  # quoted value
                     text = unmatched(m)
                     value = m.group(1)
-                    value = _RETVAL_ESCAPE_RE.sub(r"\1", value)
+                    t = r"\1"
+                    value = _RETVAL_ESCAPE_RE.sub(t, value)
                 else:
                     m = _RETVAL_VALUE_RE.search(text)
                     if m:  # unquoted value
@@ -204,6 +214,8 @@
 
     def _send_command(self, command, **params):
         # Page 38-39
+        if command == 1:
+            return self.get_status(1)
         parameters = []
         for param, value in params.items():
             if isinstance(value, Decimal):
@@ -220,18 +232,40 @@
 
             parameters.append('%s=%s' % (param, value))
 
-        reply = self.writeline("%d;%s;%s;" % (self._command_id,
-                                              command,
-                                              ' '.join(parameters)))
+        try:
+            reply = self.writeline("%d;%s;%s;" % (self._command_id,
+                                                  command,
+                                                  ' '.join(parameters)))
+        except Exception as e:
+            if e.message == 'Timeout' and command == 'EmiteReducaoZ':
+                limite = 1
+                # tratamento para o caso da redução Z demorar muito a emitir o cupom na ECF
+                while True:
+                    try:
+                        reply = self._get_status()
+                        if reply or limite > 25:
+                            return True
+
+                    except Exception as e:
+                        log.error("Lendo reducao Z: %s" % str(e))
+                        if limite > 10:
+                            return False
+
+                    limite += 1
+
+            else:
+                raise e
+
         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
+            raise AssertionError('Erro ao obter resposta do ECF')
 
         retdict = self._parse_return_value(sections[2])
         errorcode = int(sections[1])
@@ -259,6 +293,11 @@
             cmd = 'LeData'
             argname = 'NomeData'
             retname = 'ValorData'
+        elif regtype == datetime.time:
+            cmd = 'LeHora'
+            argname = 'NomeHora'
+            retname = 'ValorHora'
+            
         elif regtype == str:
             cmd = 'LeTexto'
             argname = 'NomeTexto'
@@ -279,27 +318,245 @@
         elif regtype == datetime.date:
             # This happens the first time we send a ReducaoZ after
             # opening the printer and removing the jumper.
-            if retval == '#00/00/0000#':
-                return datetime.date.today()
-            else:
-                # "29/03/2007" -> datetime.date(2007, 3, 29)
-                d, m, y = map(int, retval[1:-1].split('/'))
-                return datetime.date(y, m, d)
+#            if retval == '#00/00/0000#':
+#                return datetime.date.today()
+#            else:
+            # "29/03/2007" -> datetime.date(2007, 3, 29)
+            d, m, y = map(int, retval[1:-1].split('/'))
+            return datetime.date(y, m, d)
+        elif regtype == datetime.time:
+#            if retval == '#00:00:00#':
+#                return datetime.datetime.date.today().time()
+#            else:
+            h, m, s = map(int, retval[1:-1].split(':'))
+            return datetime.time(h, m, s)
         elif regtype == str:
             # '"string"' -> 'string'
             return retval.strip('"')
         else:
             raise AssertionError
+    class FiscNetStatus(object):
+        codes = {
+            128: (OutofPaperError(_("Impressora sem papel"))),
+            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")))}
+
+        def __init__(self, reply, parent):
+            self.reply = reply
+            self.codes = reply
+            self.cls = parent
+
+        @property
+        def open(self):
+            st = bool(self.reply & FLAG_DOCUMENTO_ABERTO)
+            return st
+        @property
+        def pagamento_existe(self):
+            return bool(self.reply & FLAG_Z_PENDENTE)
+        @property
+        def out_paper(self):
+            retcmd = self.cls._send_command('LeIndicador', NomeIndicador="SensorPoucoPapel")
+            retcmd = retcmd.get('ValorNumericoIndicador')
+            return bool(int(retcmd))
+        @property
+        def block_z(self):
+            return bool(self.reply & FLAG_Z_PENDENTE)
+
+        @property
+        def off_line(self):
+            st = bool(self.reply & FLAG_EM_LINHA)
+            if st:
+                st = False
+            else:
+                st = True
+            return st
+
+        def check_error(self):
+#            errors_dict = {
+#                7003: OutofPaperError,
+#                7004: OutofPaperError,
+#                8007: CouponTotalizeError,
+#                8011: PaymentAdditionError,
+#                8013: CouponTotalizeError,
+#                8014: PaymentAdditionError,
+#                8017: CloseCouponError,
+#                8044: CancelItemError,
+#                8045: CancelItemError,
+#                8068: PaymentAdditionError,
+#                8086: CancelItemError,
+#                15009: PendingReduceZ,
+#                11002: CommandParametersError,
+#                11006: CommandError,
+#                11007: InvalidState,
+#                15007: PendingReadX,
+#                15008: ReadXError,
+#                15011: OutofPaperError
+#                }
+            flags_check = { FLAG_INTERVENCAO_TECNICA: InvalidState('Equipamento em Intervenção Técnica.'),
+                            FLAG_SEM_MFD: DriverError('MFD não encontrada.'),
+                            FLAG_RAM_NOK: DriverError('RAM não está consistente.'),
+                            FLAG_RELOGIO_NOK: DriverError('Relógio inconsistente.'),
+                            FLAG_SEM_MF: DriverError('Memória fiscal não encontrada.'),
+                            FLAG_DIA_FECHADO: BlockZ('Bloqueio por Redução Z.'),
+                            FLAG_Z_PENDENTE: PendingReduceZ('Redução Z pendente.'),
+                            FLAG_SEM_PAPEL: OutofPaperError('Sem papel na estação de cupom fiscal.'),
+                            FLAG_MECANISM_NOK: DriverError('Mecanismo impressor não configurado.'),
+                            FLAG_MFD_ESGOTADA: DriverError('MFD Esgotada.')}
+
+            for i in sorted(flags_check.keys()):
+                if i & self.reply:
+                    
+                    raise flags_check[i]
+
+        def check_error_two(self):
+            return True
+
+
+    def get_status(self, val=None, cmd_status=None):
+        status = self._get_status()
+        return self.FiscNetStatus(status, parent=self)
+        
+        lst_status = []
+        #return status 
+        if status & FLAG_INTERVENCAO_TECNICA:
+            lst_status.append(FLAG_INTERVENCAO_TECNICA)
+
+        if status & FLAG_RAM_NOK:
+            lst_status.append(FLAG_RAM_NOK)
+
+        if status & FLAG_RELOGIO_NOK:
+            lst_status.append(FLAG_RELOGIO_NOK)
+
+        if status & FLAG_SEM_MF:
+            lst_status.append(FLAG_SEM_MF)
+
+        if status & FLAG_DIA_FECHADO:
+            lst_status.append(FLAG_DIA_FECHADO)
+
+        if status & FLAG_DIA_ABERTO:
+            lst_status.append(FLAG_DIA_ABERTO)
+
+        if status & FLAG_Z_PENDENTE:
+            lst_status.append(FLAG_Z_PENDENTE)
+
+        if status & FLAG_SEM_PAPEL:
+            lst_status.append(FLAG_SEM_PAPEL)
+        
+        if status & FLAG_MECANISM_NOK:
+            lst_status.append(FLAG_MECANISM_NOK)
+        
+        if status & FLAG_DOCUMENTO_ABERTO:
+            lst_status.append(FLAG_DOCUMENTO_ABERTO)
+        
+        if status & FLAG_INSCRICOES_OK:
+            lst_status.append(FLAG_INSCRICOES_OK)
+        
+        if status & FLAG_EM_LINHA:
+            lst_status.append(FLAG_EM_LINHA)
+        else:
+            raise PrinterOfflineError(u'Impressora Off-Line')
+
+        if status & FLAG_MFD_ESGOTADA:
+            print 'FLAG_MFD_ESGOTADA', FLAG_MFD_ESGOTADA        
+
+        return lst_status
+
+    def set_summertime(self, value=None):
+        return self._send_command('AcertaHorarioVerao')
+
+    def get_summertime(self):
+        retval = self._send_command('LeIndicador', NomeIndicador="HorarioVerao")
+        status = retval.get('ValorNumericoIndicador', '0')
+        return bool(int(status))
+
+    
+
+    def get_ecf_date(self):
+        d = self._read_register('Data', datetime.date)
+        h = self._read_register('Hora', datetime.time)
+        return datetime.datetime(d.year, d.month, d.day, h.hour, h.minute, h.second)
+
+    def get_data_mov(self):
+        return self._read_register('DataAbertura', datetime.date)
+    
+    def open_cash_drawer(self, sec=0):
+        return self._send_command('AbreGaveta', TempoAcionamentoGaveta=sec)
+
+    def get_corta_papel(self, corte=1):
+        if config['cortapapel']:
+            try:
+                self._send_command('CortaPapel', TipoCorte=corte)
+                return True
+            except Exception as e:
+                log.error("Erro ao cortar papel: %s" % str(e))
+
+        return False
+
+    def get_status_cash_drawer(self):
+        retval = self._send_command('LeIndicador', NomeIndicador="SensorGaveta")
+        status = retval.get('ValorNumericoIndicador', False)
+        return bool(int(status))
 
     def _get_status(self):
         return self._read_register('Indicadores', int)
 
     def _get_last_item_id(self):
-        return self._read_register('ContadorDocUltimoItemVendido', int)
+        try:
+            res = self._read_register('ContadorDocUltimoItemVendido', int)
+        except Exception as e:
+            log.error(str(e))
+            res = 999
+
+        return res
 
+    def _get_coupon_subtotal(self):
+        return self._get_coupon_total_value()
+    
     def _get_coupon_total_value(self):
         return self._read_register('TotalDocLiquido', Decimal)
 
+    def _emite_leitura_fita_detalhe(self, *args, **kwargs):
+        COOFinal = args[1].get('COOFinal')
+        COOInicial = args[1].get('COOInicial')
+        DataFinal = False
+        DataInicial = False
+        if args[1].get('yf') and args[1].get('mf') and args[1].get('df'):
+            DataFinal = datetime.date(args[1].get('yf'), args[1].get('mf'), args[1].get('df'))
+            DataInicial = datetime.date(args[1].get('yi'), args[1].get('mi'), args[1].get('di'))
+        Destino = args[1].get('Destino')
+        Proprietario = args[1].get('Proprietario')
+        TipoDocumento = args[1].get('TipoDocumento')
+        if COOFinal: # consulta por coo
+            if Destino == 'I': # impressora
+                res = self._send_command('EmiteLeituraFitaDetalhe', COOFinal=COOFinal, COOInicial=COOInicial,
+                                         Destino=Destino)
+            else: # Recepcao Serial
+                res = self._send_command('EmiteLeituraFitaDetalhe', COOFinal=COOFinal, COOInicial=COOInicial,
+                                         Destino=Destino, TipoDocumento=TipoDocumento)
+        else: # consulta por data
+            if Destino == 'I': # impressora
+                res = self._send_command('EmiteLeituraFitaDetalhe', DataFinal=DataFinal, DataInicial=DataInicial,
+                                         Destino=Destino)
+            else: # Recepcao Serial
+                res = self._send_command('EmiteLeituraFitaDetalhe', DataFinal=DataFinal, DataInicial=DataInicial,
+                                         Destino=Destino, TipoDocumento=TipoDocumento)
+        return self._le_impressao()
+
+    def _le_impressao(self):
+        '''
+        (TextoImpressao) Le os ultimos 4000 caracters nao lidos impressos da impressora
+        :return: dicionario de chave 'TextoImpressao' e o valor a string da leitura
+        '''
+        res = texto = self._send_command('LeImpressao').get('TextoImpressao')
+        # enquanto houver texto a ser lido, chama a funcao le_impressao()
+        while texto:
+            texto = self._send_command('LeImpressao').get('TextoImpressao')
+            res += texto
+
+        return res.decode('cp850')
+
     def _get_coupon_remainder_value(self):
         value = self._read_register('TotalDocValorPago', Decimal)
         result = self._get_coupon_total_value() - value
@@ -340,7 +597,7 @@
             if e.code != 8057: # Not configured
                 raise
 
-    def _define_payment_method(self, code, name):
+    def _define_payment_method(self, code, name, vinculated=False):
         try:
             retdict = self._send_command(
                 'LeMeioPagamento', CodMeioPagamentoProgram=code)
@@ -361,7 +618,7 @@
             self._send_command(
                 'DefineMeioPagamento',
                 CodMeioPagamentoProgram=code, DescricaoMeioPagamento=name,
-                NomeMeioPagamento=name, PermiteVinculado=False)
+                NomeMeioPagamento=name, PermiteVinculado=vinculated)
         except DriverError, e:
             raise
 
@@ -407,6 +664,8 @@
         except DriverError, e:
             if e.code != 8005: # Not configured
                 raise
+    def get_tax(self):
+        return self._get_taxes()
 
     def _get_taxes(self):
         taxes = []
@@ -418,6 +677,7 @@
 
         read_r = lambda x: self._read_register(x, Decimal)
 
+
         taxes = [
             ('I', read_r('TotalDiaIsencaoICMS'), 'ICMS'),
             ('F', read_r('TotalDiaSubstituicaoTributariaICMS'), 'ICMS'),
@@ -428,6 +688,7 @@
 
         for reg in range(16):
             value = read_r('TotalDiaValorAliquota[%d]' % reg)
+
             if value:
                 retdict = self._send_command(
                     'LeAliquota', CodAliquotaProgramavel=reg)
@@ -441,6 +702,44 @@
                 taxes.append(('%04d' % int(desc), value, type))
         return taxes
 
+    def get_totalizadores(self, total=False):
+        '''
+        Esta funcao busca cada totalizador da ecf e seus valores
+        '''
+        taxes = []
+        taxes.append(('CANC', Decimal(0), 'ICMS'))
+        taxes.append(('DESC', Decimal(0), 'ICMS'))
+        taxes.append(('I', Decimal(0), 'ICMS'))
+        taxes.append(('N', Decimal(0), 'ICMS'))
+        taxes.append(('F', Decimal(0), 'ICMS'))
+
+        read_r = lambda x: self._read_register(x, Decimal)
+
+        taxes = [
+            ('I', read_r('TotalDiaIsencaoICMS'), 'ICMS'),
+            ('F', read_r('TotalDiaSubstituicaoTributariaICMS'), 'ICMS'),
+            ('N', read_r('TotalDiaNaoTributadoICMS'), 'ICMS'),
+            ('DESC', read_r('TotalDiaDescontos'), 'ICMS'),
+            ('ACRESC', read_r('TotalDiaAcrescimos'), 'ICMS'),
+            ('CANC', read_r('TotalDiaCancelamentosICMS'), 'ICMS'),
+            ('CANC', read_r('TotalDiaCancelamentosISSQN'), 'ISS'),
+            ]
+
+        for reg in range(16):
+            value = read_r('TotalDiaValorAliquota[%d]' % (reg+1))
+            try:
+                retdict = self._send_command('LeAliquota', CodAliquotaProgramavel=reg+1)
+
+                if retdict['AliquotaICMS'] == 'Y':
+                    type = "ICMS"
+                else:
+                    type = "ISS"
+
+                taxes.append(('%02d' % (reg+1), value, type))
+            except: # para um numero de CodAliquotaProgramavel que nao esta cadastrado na ecf encerra a repeticao
+                break
+        return taxes
+
     def setup(self):
         self._define_tax_name(0, "Suprimento".encode('cp850'))
         self._define_tax_name(1, "Sangria".encode('cp850'))
@@ -449,8 +748,10 @@
 
         self._define_payment_method(0, u'Cheque'.encode('cp850'))
         self._define_payment_method(1, u'Boleto'.encode('cp850'))
-        self._define_payment_method(2, u'Cartão credito'.encode('cp850'))
-        self._define_payment_method(3, u'Cartão debito'.encode('cp850'))
+        self._define_payment_method(2, u'Cartão credito'.encode('cp850'),
+                                    vinculated=True)
+        self._define_payment_method(3, u'Cartão debito'.encode('cp850'),
+                                    vinculated=True)
         self._define_payment_method(4, u'Financeira'.encode('cp850'))
         self._define_payment_method(5, u'Vale compra'.encode('cp850'))
         for code in range(6, 15):
@@ -467,15 +768,13 @@
 
     def print_status(self):
         status = self._get_status()
-        print 'Flags'
         for flag in reversed(_status_flags):
             if status & flag:
                 print flag, _flagnames[flag]
 
-        print 'non-fiscal registers'
         for i in range(15):
             try:
-                print self._send_command(
+                self._send_command(
                     'LeNaoFiscal', CodNaoFiscal=i)
             except DriverError, e:
                 if e.code != 8057:
@@ -501,10 +800,15 @@
         customer = self._customer_name
         document = self._customer_document
         address = self._customer_address
-        self._send_command('AbreCupomFiscal',
-                           EnderecoConsumidor=address[:80],
-                           IdConsumidor=document[:29],
-                           NomeConsumidor=customer[:30])
+        if self._customer_name:
+            ret = self._send_command('AbreCupomFiscal', EnderecoConsumidor=address[:80],
+                                     IdConsumidor=document[:29], NomeConsumidor=customer[:30])
+        else:
+            ret = self._send_command('AbreCupomFiscal')
+
+        if not ret:
+            return True
+        return False
 
     def coupon_add_item(self, code, description, price, taxcode,
                         quantity=Decimal("1.0"), unit=UnitType.EMPTY,
@@ -519,7 +823,19 @@
         else:
             unit = self._consts.get_value(unit)
 
-        taxcode = int(taxcode)
+        try:
+            taxcode = int(taxcode)
+        except:
+            #print self._get_taxes()
+            taxcode = 1
+#            for x in self._get_taxes():
+#                print '*', taxcode, '*'
+#                if taxcode == x[0]:
+#                    taxcode = int(x[1])
+#                    if taxcode > 9:
+#                        taxcode = 1
+#                    break 
+            
         self._send_command('VendeItem',
                            CodAliquota=taxcode,
                            CodProduto=code[:48],
@@ -527,17 +843,45 @@
                            Unidade=unit,
                            PrecoUnitario=price,
                            Quantidade=quantity)
-        return self._get_last_item_id()
+        retval = self._get_last_item_id()
+        if discount > 0:
+            self._send_command('AcresceItemFiscal',
+                               Cancelar=False, 
+                               NumItem=retval,
+                               ValorAcrescimo=discount * -1)
+        if surcharge > 0:
+            self._send_command('AcresceItemFiscal',
+                               Cancelar=False, 
+                               NumItem=retval,
+                               ValorAcrescimo=surcharge)
+        
+        return retval
 
-    def coupon_cancel_item(self, item_id):
-        self._send_command('CancelaItemFiscal', NumItem=item_id)
+    def desc_acres_item(self, desc_acres, item, valor):
+        if desc_acres == 'D':
+            valor *= -1
+        valor = Decimal(valor).quantize(TWOPLACES)
+        self._send_command('AcresceItemFiscal',
+                           Cancelar=False,
+                           NumItem=item,
+                           ValorAcrescimo=valor)
+        return True
+
+
+    def coupon_cancel_item(self, item_id=None):
+        if item_id == None: # cancela o ultimo item
+            self._send_command('CancelaItemFiscal')
+        else:
+            self._send_command('CancelaItemFiscal', NumItem=item_id)
 
     def coupon_cancel(self):
         self._send_command('CancelaCupom')
+        self.get_corta_papel()
 
     def cancel_last_coupon(self):
         """Cancel the last non fiscal coupon or the last sale."""
         self._send_command('CancelaCupom')
+        self.get_corta_papel()
 
     def coupon_totalize(self, discount=currency(0),
                         surcharge=currency(0), taxcode=TaxType.NONE):
@@ -553,20 +897,67 @@
         return self._get_coupon_total_value()
 
     def coupon_add_payment(self, payment_method, value, description=u""):
+        #pm = int(payment_method)
         pm = int(payment_method)
         self._send_command('PagaCupom',
                            CodMeioPagamento=pm, Valor=value,
                            TextoAdicional=description[:80])
         return self._get_coupon_remainder_value()
 
+    def _add_voucher(self, type, value, cod_finalizadora=-2):
+        '''
+        Recarga de celular
+        cod_finalizadora = -2(Dinheiro por defalt)
+        :type = codigo do tot. nao fiscal
+        '''
+        status = self._get_status()
+        if status & FLAG_DOCUMENTO_ABERTO:
+            self.coupon_cancel()
+        self._send_command('AbreCupomNaoFiscal')
+        self._send_command('EmiteItemNaoFiscal',
+                           CodNaoFiscal=type,
+                           Valor=value)
+        self._send_command('PagaCupom',
+                           CodMeioPagamento=cod_finalizadora, Valor=value)
+        self._send_command('EncerraDocumento')
+        self.get_corta_papel()
+
+    def _configure_payment_methods(self, descricao):
+        vlr = self.get_payment_constants()
+        try:
+            return vlr[descricao[:80]]
+        except KeyError:
+            return False
+
     def coupon_close(self, message=''):
+        max = 8
+        inc = 1
+        new_message = ''
+        opr=''
+        for m in message.split('\n'):
+            if inc <= max:
+                if m.startswith('OP'):
+                    opr = m[4:]
+                else:
+                    new_message += '%s\n' % m
+                    inc += 1
+        new_message = re.sub(r'NOTA LEGAL:', r'\x1BE1NOTA LEGAL:\x1BE0', new_message)
+
         self._send_command('EncerraDocumento',
-                           TextoPromocional=message[:492])
+                           Operador=opr,
+                           TextoPromocional=new_message[:492])
         self._reset()
+        self.get_corta_papel()
         return self.get_coo()
 
     def summarize(self):
         self._send_command('EmiteLeituraX')
+        coo = False
+        while True:
+            coo = self.get_coo()
+            if coo:
+                break
+        self.get_corta_papel()
 
     def close_till(self, previous_day=False):
         status = self._get_status()
@@ -574,44 +965,69 @@
             self.coupon_cancel()
 
         self._send_command('EmiteReducaoZ')
+        self.get_corta_papel()
 
-    def till_add_cash(self, value):
+    def till_add_cash(self, value, totalizador=0, meio=0):
         status = self._get_status()
         if status & FLAG_DOCUMENTO_ABERTO:
             self.coupon_cancel()
         self._send_command('AbreCupomNaoFiscal')
         self._send_command('EmiteItemNaoFiscal',
-                           NomeNaoFiscal="Suprimento",
+                           CodNaoFiscal=totalizador,
                            Valor=value)
         self._send_command('PagaCupom',
                            CodMeioPagamento=-2, Valor=value)
         self._send_command('EncerraDocumento')
+        self.get_corta_papel()
 
-    def till_remove_cash(self, value):
+    def till_remove_cash(self, value, cod=0, text=''):
         status = self._get_status()
         if status & FLAG_DOCUMENTO_ABERTO:
             self.coupon_cancel()
+
         self._send_command('AbreCupomNaoFiscal')
         self._send_command('EmiteItemNaoFiscal',
-                           NomeNaoFiscal="Sangria",
+                           CodNaoFiscal=cod,
                            Valor=value)
-        self._send_command('EncerraDocumento')
+        self._send_command('EncerraDocumento', TextoPromocional=text)
+        self.get_corta_papel()
 
-    def till_read_memory(self, start=None, end=None):
+    def till_read_memory(self, start=None, end=None, full=False, destino='I'):
         try:
             self._send_command('EmiteLeituraMF',
-                               LeituraSimplificada=True,
+                               Destino=destino,
+                               LeituraSimplificada=not full,
                                DataInicial=start,
                                DataFinal=end)
+            if destino == 'I': # impressora
+                self.get_corta_papel()
+            else: # serial
+                return self._le_impressao()
+
         except DriverError, e:
             if e.code == 8089:
                 return
 
-    def till_read_memory_by_reductions(self, start=None, end=None):
+
+    def till_read_memory_by_reductions(self, start=None, end=None, full=False, destino='I'):
+        '''
+        :param destino: I = impressora, S = serial
+        '''
         self._send_command('EmiteLeituraMF',
-                           LeituraSimplificada=True,
+                           Destino=destino,
+                           LeituraSimplificada=not full,
                            ReducaoInicial=start,
                            ReducaoFinal=end)
+        if destino == 'I':
+            self.get_corta_papel()
+        else:
+            return self._le_impressao()
+
+    def till_read_memory_to_serial(self, start=None, end=None, full=False):
+        return self.till_read_memory(start, end, full, 'S')
+
+    def till_read_memory_by_reductions_to_serial(self, start=None, end=None, full=False):
+        return self.till_read_memory_by_reductions(start, end, full, 'S')
 
     def get_capabilities(self):
         return dict(item_code=Capability(max_len=48),
@@ -672,9 +1088,9 @@
         return constants
 
     def get_payment_constants(self):
-        constants = [('-2', 'Dinheiro')]
+        constants = [('Dinheiro', '-2')]
 
-        for reg in range(16):
+        for reg in range(15):
             try:
                 retdict = self._send_command(
                     'LeMeioPagamento', CodMeioPagamentoProgram=reg)
@@ -685,9 +1101,9 @@
 
             code = retdict['CodMeioPagamentoProgram']
             name = retdict['NomeMeioPagamento']
-            constants.append((code, name))
+            constants.append((name.decode(self.coupon_printer_charset), code))
 
-        return constants
+        return dict(constants)
 
     def get_ccf(self):
         return self._read_register('CCF', int)
@@ -703,10 +1119,53 @@
 
     def get_crz(self):
         return self._read_register('CRZ', int)
+    ###
+    def get_gnf(self):
+        return self._read_register('GNF', int)
+
+    def get_cfc(self):
+        return self._read_register('CFC', int)
+
+    def get_grg(self):
+        return self._read_register('GRG', int)
+
+    def get_cdc(self):
+        return self._read_register('CDC', int)
+
 
     def get_firmware_version(self):
         return self._read_register('VersaoSW', str)
 
+    def get_date_last_mov_rz(self):
+        '''
+        :return: Retorna a data do movimento da ultima reducao Z emitida
+        '''
+        ultimo_z = self.get_ultamo_z()
+        data = ''
+        if ultimo_z:
+            data = ultimo_z[582:588] # data do movimento do ultimo Z emitido
+        try:
+            if len(data) == 5:
+                data = '0' + data
+
+            data = datetime.datetime(year=2000 + int(data[4:6]),
+                            month=int(data[2:4]),
+                            day=int(data[:2])).date()
+        except Exception as e:
+            log.error('get_date_last_mov_rz: %s' % str(e))
+            data = 0
+
+        return data
+
+    def get_ultamo_z(self):
+        '''
+        Pega os dados da ultima reducao Z emiditda
+        :return:
+        '''
+        dados_z = self._read_register('DadosUltimaReducaoZ', str)
+
+        return dados_z
+
     def get_sintegra(self):
         data = Settable(
             opening_date=self._read_register('DataAbertura', datetime.date),
@@ -717,29 +1176,81 @@
             cro=self.get_cro(),
             crz=self.get_crz(),
             coo=self.get_coo(),
+            ccf=self.get_ccf(),
+            gnf=self.get_gnf(),
+            grg=self.get_grg(),
+            cdc=self.get_cdc(),
             period_total=self._read_register('TotalDiaVendaBruta', Decimal),
             total=self._read_register('GT', Decimal),
             taxes=self._get_taxes())
-
         return data
 
     def get_payment_receipt_identifier(self, method_name):
         # We don't need an identifier. Just return None
         return None
 
-    def payment_receipt_open(self, identifier, coo, method_id, value):
-        self._send_command('AbreCreditoDebito', CodMeioPagamento=int(method_id),
+    def payment_receipt_open(self, identifier, coo, method, value):
+        self._send_command('AbreCreditoDebito', CodMeioPagamento=int(method),
                            COO=coo, Valor=value)
 
     def payment_receipt_print(self, text):
-        self._send_command('ImprimeTexto', TextoLivre=text)
+        text = text.encode('cp850')
+        max_line = 7
+        inc = 1
+        imprime_linha = ''
+        for line in text.split('\n'):
+            if inc <= max_line:
+                line = line.replace('\\', '\\\\') # Vespague sucks
+
+            if len(imprime_linha + '\n' + line) > 492: # limite de caracteres do comando ImprimeTexto
+                self._send_command('ImprimeTexto', TextoLivre=imprime_linha)
+                imprime_linha = ''
+
+            inc += 1
+            if line == 'corta_papel':
+                if imprime_linha:
+                    self._send_command('ImprimeTexto', TextoLivre=imprime_linha)
+                    imprime_linha = ''
+
+                if not self.get_corta_papel():
+                    imprime_linha = '\n------------------------------------------------\n\n'
+
+                continue
+
+            imprime_linha += '\n' + line
+
+        self._send_command('ImprimeTexto', TextoLivre=imprime_linha) # imprime a o restante do texto
 
     def payment_receipt_close(self):
         self._send_command('EncerraDocumento')
+        self.get_corta_papel()
 
     def payment_receipt_print_duplicate(self):
         self._send_command('EmiteViaCreditoDebito')
 
+    def gerencial_report_open(self, id=0):
+        self._send_command('AbreGerencial', CodGerencial=id)
+
+    def gerencial_report_print(self, text):
+        text = text.encode('cp850')
+        imprime_linha = ''
+        for line in text.split('\n'):
+            line = line.replace('\\', '\\\\') # Vespague sucks
+            if len(imprime_linha + '\n' + line) > 492: # limite de caracteres do comando ImprimeTexto
+                self._send_command('ImprimeTexto', TextoLivre=imprime_linha)
+                imprime_linha = ''
+
+            imprime_linha += '\n' + line
+
+        self._send_command('ImprimeTexto', TextoLivre=imprime_linha)
+
+    def gerencial_report_close(self):
+        self._send_command('EncerraDocumento')
+        self.get_corta_papel()
+
+    # FIX ME
+    def get_grande_total(self):
+        return self._read_register('GT', Decimal)
 
 class FiscNetChequePrinter(FiscNetECF, BaseChequePrinter):
     implements(IChequePrinter)
@@ -782,6 +1293,25 @@
                     cheque_value=Capability(digits=14, decimals=4),
                     cheque_city=Capability(max_len=27))
 
+    def get_last_reduce(self):
+        dec = Decimal('0')
+        retval = {'CRO': dec,
+                  'CRZ': dec,
+                  'COO': dec,
+                  'GNF': dec,
+                  'CCF': dec,
+                  'GRG': dec,
+                  'CFD': dec,
+                  'NFC': dec,
+                  'CFC': dec,
+                  'COM': dec,
+                  'CER': dec,
+                  'CDC': dec,
+                  'NCN': dec,
+                  'CCDC': dec,
+                  'GT': dec,
+                  'VBD': dec}
+        return retval
     def get_capabilities(self):
         caps = super(FiscNetChequePrinter, self).get_capabilities()
         caps.update(self._get_cheque_capabilities())
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/perto/Pay2023.py trunk/stoqdrivers/printers/perto/Pay2023.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/perto/Pay2023.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/perto/Pay2023.py	1969-12-31 21:00:00.000000000 -0300
@@ -1,42 +0,0 @@
-# -*- Mode: Python; coding: utf-8 -*-
-# vi:si:et:sw=4:sts=4:ts=4
-
-##
-## Stoqdrivers
-## Copyright (C) 2005-2007 Async Open Source <http://www.async.com.br>
-## All rights reserved
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2 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 General Public License for more details.
-##
-## You should have received a copy of the GNU General Public License
-## along with this program; if not, write to the Free Software
-## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-## USA.
-##
-## Author(s):   Henrique Romano <henrique@async.com.br>
-##              Johan Dahlin <henrique@async.com.br>
-##
-"""
-PertoPay 2023 driver implementation.
-"""
-
-from stoqdrivers.printers.fiscnet.FiscNetECF import FiscNetChequePrinter
-from stoqdrivers.translation import stoqdrivers_gettext
-
-_ = stoqdrivers_gettext
-
-
-class Pay2023(FiscNetChequePrinter):
-    log_domain = 'PertoPay2023'
-    supported = True
-
-    model_name = "Pertopay Fiscal 2023"
-
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/sweda/IFS9000I.bak.py trunk/stoqdrivers/printers/sweda/IFS9000I.bak.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/sweda/IFS9000I.bak.py	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/printers/sweda/IFS9000I.bak.py	2016-10-13 16:22:19.420311101 -0300
@@ -0,0 +1,1172 @@
+# -*- Mode: Python; coding: utf-8 -*-
+# vi:si:et:sw=4:sts=4:ts=4
+
+##
+## Stoqdrivers
+## Copyright (C) 2005 Async Open Source <http://www.async.com.br>
+## All rights reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 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 General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+## USA.
+##
+## Author(s):   Evandro Vale Miquelito      <evandro@async.com.br>
+##
+"""
+Sweda IFS9000I driver
+"""
+
+import string
+import datetime
+from decimal import Decimal
+
+from kiwi.python import Settable
+
+from stoqdrivers.logs.log import traced as log
+from zope.interface import implements
+
+from stoqdrivers.enum import PaymentMethodType, TaxType, UnitType
+from stoqdrivers.exceptions import (PrinterError, CloseCouponError,
+                                    PendingReadX, CommandError,
+                                    CouponOpenError, CommandParametersError,
+                                    CouponNotOpenError, ReduceZError,
+                                    HardwareFailure, DriverError,
+                                    OutofPaperError, PendingReduceZ,
+                                    InvalidState, CancelItemError,
+                                    AlreadyTotalized)
+from stoqdrivers.interfaces import ICouponPrinter
+from stoqdrivers.serialbase import SerialBase
+from stoqdrivers.printers.capabilities import Capability
+from stoqdrivers.printers.base import BaseDriverConstants
+from stoqdrivers.translation import stoqdrivers_gettext
+
+_ = stoqdrivers_gettext
+
+
+class IFS9000IConstants(BaseDriverConstants):
+    _constants = {
+        # Page 4-12, two decimals
+        UnitType.WEIGHT:      '!',
+        UnitType.METERS:      '@',
+        UnitType.LITERS:      ')',
+        UnitType.EMPTY:       '^',
+        PaymentMethodType.MONEY:         '01',
+        PaymentMethodType.CHECK:        '01'
+        }
+
+    _tax_constants = [
+        # Page 4-8
+        (TaxType.SUBSTITUTION, 'F', None),
+        (TaxType.EXEMPTION,    'I', None),
+        (TaxType.NONE,         'N', None),
+        # Custom, see Page 4-78
+        ]
+
+
+class IFS9000I(SerialBase):
+
+    implements(ICouponPrinter)
+
+    model_name = "Sweda IFS 9000 I"
+    coupon_printer_charset = "ascii"
+
+    CMD_PREFIX = "."
+    CMD_SUFFIX = EOL_DELIMIT = '}'
+
+    #
+    # Printer command set
+    #
+
+    CMD_COUPON_OPEN = '17'
+    CMD_COUPON_ADD_ITEM = '01'
+    CMD_ITEM_ADD_DISCOUNT = '02'
+    CMD_ITEM_ADD_SURCHARGE = '60'
+    CMD_ITEM_CANCEL = '04'
+    CMD_COUPON_ADD_DISCOUNT = '03'
+    CMD_COUPON_ADD_SURCHARGE = '11'
+    CMD_COUPON_CANCEL = '05'
+    CMD_COUPON_TOTALIZE = '10'
+    CMD_COUPON_CLOSE = '12'
+    CMD_READ_X = '13'
+    CMD_REDUCE_Z = '14'
+    CMD_PRINTER_STATUS = '23'
+    CMD_PRINT_PARAMETERS = '18'
+    CMD_SET_SALE_PARAMETERS = '30'
+    CMD_SET_HEADER_PARAMETERS = '31'
+    CMD_SAVE_USER_SETTINGS = '41'
+    CMD_ADD_USER_SETTINGS = '34'
+    CMD_SETUP_CLOCK = '35'
+    CMD_SETUP_PAYMENT_METHOD = '39'
+    CMD_TRANSACTION_STATUS = '28'
+    CMD_GET_PRINTER_TOTALIZERS = '27'
+    CMD_OPEN_VOUCHER = '19'
+    CMD_INCREMENT_NONFISCAL_COUNTER = '07'
+
+    #
+    # Settings for printer command parameters
+    #
+
+    CUSTOMER_CHAR_LEN = 20
+    CUSTOMER_CNPJ_LEN = 22
+    CUSTOMER_STATE_REGISTER_LEN = 21
+    # We have actually 24 characters for product descriptions but the first
+    # argument define the label for product units
+    DESCRIPTION_CHAR_LEN = 23
+    # A second description allowed when adding items
+    SECONDDESC_CHAR_LEN = 40
+    PRODUCT_CODE_CHAR_LEN = 13
+    TAXCODE_CHAR_LEN = 3
+    PRINT_ONE_LINE_CHAR_LEN = 2
+    PERCENTAGE_CHAR_LEN = 4
+    ITEM_NUMBER_CHAR_LEN = 3
+    PAYMENTMETHOD_CHAR_LEN = 2
+
+    SURCHARGE_CHAR_LEN = 11
+    SURCHARGE_DEC_SEPARATOR = 2
+    SURCHARGE_ZERO_DIGITS = 0
+
+    PRICE_CHAR_LEN = 8
+    PRICE_DEC_SEPARATOR = 2
+    PRICE_ZERO_DIGITS = 1
+
+    TOTAL_CHAR_LEN = 8
+    TOTAL_DEC_SEPARATOR = 2
+    TOTAL_ZERO_DIGITS = 4
+
+    QTY_CHAR_LEN = 7
+    QTY_DEC_SEPARATOR= 3
+    QTY_ZERO_DIGITS = 0
+
+    # This code must be added in command CMD_COUPON_ADD_SURCHARGE and
+    # represents a label 'ACRESCIMO' in the printed coupon
+    STANDARD_SURCHARGE_CODE ='51'
+
+
+    errors_dict = {'DIA ENCERRADO': (PendingReadX, _("A Read X is pending")),
+                   'ERRO-COMAND INVALIDO': (CommandError, _("The command is "
+                                                            "invalid")),
+                   'ERRO-OPERACAO NAO ENCERRADA': (CouponOpenError,
+                                                   _("A coupon already exist")),
+                   'ERRO-PARAMETROS DO COMAND INVALIDOS': (CommandParametersError,
+                                                           _("Parameters invalid.")),
+                   'ERRO-OPERACAO NAO ABERTA': (CouponNotOpenError,
+                                                _("There is no coupon opened")),
+                   'JA  FEZ REDU??O': (ReduceZError, _("Reduce Z already done.")),
+                   '^RELOGIO ZERADO-PROGRAMAR': (PrinterError,
+                                                 _("Printer clock isn't "
+                                                   "programmed yet.")),
+                   'RELOGIO:ERRO-PROGRAMAR DATA/HORA': (PrinterError,
+                                                        _("Printer clock isn't "
+                                                          "programmed yet.")),
+                   '^CHAMAR ASSISTENCIA TECNICA': (HardwareFailure,
+                                                   _("Problem in fiscal printer")),
+                   '^CMOS: ERRO-MEM?RIA TAXAS/PARAMET': (HardwareFailure,
+                                                         _("Problem in fiscal "
+                                                           "printer")),
+                   'ERRO-TOTAL: NAO HOUVE LANCAMENTO': (CloseCouponError,
+                                                        _("There is no items "
+                                                          "added to coupon")),
+                   'ERRO - VERIFICAR PAPEL': (OutofPaperError, _("Out of paper")),
+                   'ENCERRAR O DIA !': (PendingReduceZ, _("Redução Z Pendente")),
+                   'ERRO-COMANDO NAO PERMITIDO': (InvalidState,
+                                                  _("Invalid state for command "
+                                                    "execution")),
+                   'ERRO-SEM OS DADOS DO ITEM NA MEMORIA': (CancelItemError,
+                                                            _("It is not "
+                                                              "possible cancel "
+                                                              "the item since "
+                                                              "it wasn't added "
+                                                              "yet")),
+                   'ERRO-CUPOM TOTALIZADO': (AlreadyTotalized,
+                                             _("The coupon is already "
+                                               "totalized")),
+                   'ERRO-ITEM INEXISTE': (CancelItemError,
+                                          _("There is no such item to cancel "
+                                            "in the coupon")),
+                   'ERRO-CANC: NAO HOUVE LANCAMENTO' : (CouponNotOpenError,
+                                                        (_("There's no open document to cancel"))),
+                   }
+
+    def __init__(self, port, consts=None):
+        self._consts = consts or IFS9000IConstants
+        SerialBase.__init__(self, port)
+        self._customer_document = ''
+
+    #
+    # Initializing Fiscal Printer
+    #
+
+    def setup_no_fiscal_legends(self):
+        self.send_command("38S&%-14s-%-14s+%-14s", "Caixa", "Sangria",
+                          "Suprimento")
+
+    def setup_coupon_header(self, jump_lines_number, header_data):
+        """ Call this method only in non-fiscal mode.
+        header_data is a list of 5 strings of 40 characters which will be
+        added in the header of all the printed coupons.
+        """
+        command = self.CMD_SET_HEADER_PARAMETERS
+        # This is standard. The first argument must be always 'S'
+        command += 'S'
+        jump_lines_number = str(jump_lines_number)
+        if not jump_lines_number.isdigit():
+            raise TypeError('Argument jump_lines_number must be numeric')
+        if not jump_lines_number.isdigit() in range(10):
+            raise TypeError('Argument jump_lines_number must be between '
+                            '0 and 9')
+        command += jump_lines_number
+        # Here we are setting the header lines, five groups where the first
+        # element is printer attribute and the second one is a the line
+        # format
+        if not isinstance(header_data, list):
+            raise TypeError('header_data argument must be a list')
+        if not len(header_data) == 5:
+            raise ValueError('header_data argument must have 5 items')
+        for line in header_data:
+            # The first element(zero) is a default printer attribute
+            command += '0%s' % line
+        self.writeline(command)
+
+    def setup_clock(self):
+        t = datetime.datetime.now()
+
+        hour = self._format_datetime(t.hour)
+        minute = self._format_datetime(t.minute)
+        second = self._format_datetime(t.second)
+        day = self._format_datetime(t.day)
+        month = self._format_datetime(t.month)
+        year = str(t.year)[2:]
+
+        time = '%s%s%s' % (hour, minute, second)
+        date = '%s%s%s' % (day, month, year)
+
+        command = self.CMD_SETUP_CLOCK + time +  date
+        self.writeline(command)
+
+    def setup_sale_parameters(self, use_cents, has_title):
+        command = self.CMD_SET_SALE_PARAMETERS
+        if use_cents:
+            use_cents = 'S'
+        else:
+            use_cents = 'N'
+        if has_title:
+            has_title = 'S'
+        else:
+            has_title = 'N'
+        # standard argument for a decimal number.
+        # XXX I still don't understand what is it for
+        command = (self.CMD_SET_SALE_PARAMETERS + use_cents + '245' +
+                   has_title)
+        self.writeline(command)
+
+    def setup_printer_user(self, user_cnpj, user_state_reg):
+        # XXX Warning: never call this command more than 5 times, otherwise
+        # the fiscal print will not work anymore
+        command = self.CMD_ADD_USER_SETTINGS
+        cnpj = self._format_string(user_cnpj, self.CUSTOMER_CNPJ_LEN,
+                                   'user_cnpj')
+        state_reg = self._format_string(user_state_reg,
+                                        self.CUSTOMER_STATE_REGISTER_LEN,
+                                        'user_state_reg')
+        command += cnpj + state_reg
+        self.writeline(command)
+
+        # Save in memory user information
+        command = self.CMD_SAVE_USER_SETTINGS
+        # This is a confirm argument. 'S' = confirm, 'N' = cancel
+        command += 'S'
+        self.writeline(command)
+
+    def setup_sale_data(self):
+        """ Call this method only in non-fiscal mode.
+        """
+
+        # Set taxes table
+        self.writeline('33T010500')
+
+        # Set non-fiscal operation label
+        label = '&NAO FISCAL    '
+        assert len(label) == 15
+        command = '38S%s' % label
+        self.writeline(command)
+
+        #
+        # Setting up the payment methods
+        #
+        for method in (PaymentMethodType.MONEY,
+                       PaymentMethodType.CHECK):
+            label = ("%-15s" % method.get_description())[:15]
+            self.send_command(self.CMD_SETUP_PAYMENT_METHOD, 'S', label)
+
+    #
+    # Formatting data
+    #
+
+    def _check_integer(self, value, arg_name):
+        value_str = str(value)
+        if not value_str.isdigit():
+            raise ValueError('Argument %s must be integer' % arg_name)
+
+    def _format_datetime(self, value):
+        value = str(value)
+        if len(value) == 1:
+            return '0' + value
+        return value
+
+    def _format_string(self, value, arg_len, arg_name='', left_justify=False):
+        value = str(value)
+        if len(value) > arg_len:
+            raise ValueError('Argument %s can not have more than %d '
+                             'characters' % (arg_name, arg_len))
+        if left_justify:
+            return string.ljust(value, arg_len)
+        return string.rjust(value, arg_len)
+
+    def _format_float(self, value, arg_name, char_len, dec_separator,
+                      zero_digits):
+        """ - char_len is the maximum amount of character allowed for value
+              argument.
+            - dec_separator is the number of decimal separator digits
+            - zero_digits is the number of zeros we should add in the
+              beggining of the argument value.
+        """
+        value = '%.*f' % (dec_separator, value)
+
+        # As we are going do remove the period character we can here allow
+        # char_len + 1 as the maximum number of chars per argument
+        value = self._format_string(value, char_len + 1, arg_name)
+        value = string.replace(value, ' ', '0')
+        value = string.replace(value, '.', '')
+
+        # The first character for total and price arguments must
+        # be always zero
+        value = '0' * zero_digits + value
+        return value
+
+    def _format_price(self, price, arg_name):
+        return self._format_float(price, arg_name, self.PRICE_CHAR_LEN,
+                                  self.PRICE_DEC_SEPARATOR,
+                                  self.PRICE_ZERO_DIGITS)
+
+    def _format_quantity(self, quantity, arg_name):
+        return self._format_float(quantity, arg_name, self.QTY_CHAR_LEN,
+                                  self.QTY_DEC_SEPARATOR,
+                                  self.QTY_ZERO_DIGITS)
+
+    def _format_total(self, total, arg_name):
+        return self._format_float(total, arg_name, self.TOTAL_CHAR_LEN,
+                                  self.TOTAL_DEC_SEPARATOR,
+                                  self.TOTAL_ZERO_DIGITS)
+
+    #
+    # Helper methods
+    #
+
+    def get_transaction_status(self):
+        """28 - Status de Transa��o (pag. 75)
+        
+        Este comando informa a situa��o do ECF, com respeito a:
+        
+         1. Seq�encial do �ltimo comando fiscal executado.;
+         2. N�mero de itens impressos na venda atual.
+         3. Existe transa��o pendente?
+         4. �ltima transa��o efetuada.
+         5. �ltimo comando recebido.
+         6. Se houve redu��o no dia.
+         7. Total l�quido da venda.
+         8. Total bruto da venda.
+         9. Indicador de erro no ECF.
+        10. Data atual.
+        11. Hora atual.
+        12. Hor�rio de ver�o.
+        13. Mensagem de erro interno.
+        14. N�mero do ECF.
+        15. Total que falta receber.
+        16. Soma dos valores lan�ados pelo comando <ESC>.07.
+        17. Permiss�o para abrir o caixa.
+        18. Est� no modo INTERVEN��O?
+        19. Indica se est� recebendo texto de cupom vinculado.
+        20. Situa��o do vinculado.
+        21. Impress�o de cheques/ autentica��o"""
+        
+        datas = ((3, 4, 'SEQ'), (7, 3, 'ITEM'), (10, 1, 'STATUS'), 
+                 (11, 8, 'TRANSACAO'), (19, 2, 'ESCAPE'), (21, 1, 'REDUCAO'),
+                 (22, 12, 'LIQ'), (34, 12, 'BRUTO'), (46, 1, 'ERRO'),
+                 (47, 6, 'DATA'), (53, 4, 'HORA'), (57, 1, 'VERAO'), 
+                 (58, 35, 'MENSERRO'), (93, 3, 'ECF'), (96, 1, 'PAPEL'),
+                 (97, 1, 'ABREV'), (98, 12, 'FALTA'), (110, 12, 'REC'),
+                 (122, 1, 'FAZX'), (123, 1, 'ABRIR'), (124, 1, 'PROG'),
+                 (125, 1, 'VINC'), (126, 1, 'VINC2'), (127, 1, 'SLIP'))
+             
+        reply = self.writeline(self.CMD_TRANSACTION_STATUS)
+        print reply
+        if reply[1] == '+':
+            ret_val = {}
+            for d in datas:
+                idx_ini = d[0] -1 
+                idx_end = idx_ini + d[1] 
+                data_name = d[2]
+                
+                ret_val[data_name] = reply[idx_ini:idx_end]
+            return ret_val
+        return None
+
+    def _get_totalizers(self, type):
+        reply = self.send_command(self.CMD_GET_PRINTER_TOTALIZERS, type)
+        return reply
+    
+     
+    def get_totalizers(self, type="1"):
+        reply = self.writeline(self.CMD_GET_PRINTER_TOTALIZERS + type)
+        if reply[1] != '+':
+            raise ValueError("Inconsistent package received while "
+                             "reading printer totalizers")
+        return reply[5:]
+    def _send_command(self, command, *args, **kwargs):
+        return None
+    
+    def send_command(self, command, *params):
+        """ Send a command to printer.
+
+        command: Is the command in string format
+        params: a list of parameter to this command (all parameters
+        must be string)
+        """
+        cmd = command + ''.join(params)
+        print '>>>', cmd
+        reply = self.writeline(cmd)
+        print '<<<', reply
+        complete = False
+        if command == self.CMD_PRINTER_STATUS:
+            return reply
+        elif command == '59':
+            complete = True
+        return self.handle_error(reply, complete=complete)
+
+    def handle_error(self, reply, complete=False):
+        # Page 4-2
+        # format: |.-NNNNERROR_MESSAGE}|
+        if reply[1] != '-' or reply[2] == 'P':
+            return reply[2:] if complete else reply
+        errmsg = reply[6:]
+        try:
+            exception, reason = self.errors_dict[errmsg]
+            
+            raise exception(reason)
+        except KeyError:
+            raise DriverError(errmsg)
+
+    def get_last_item_id(self):
+        # Man page 4-62
+        rv = self.get_transaction_status()
+        if rv is not None:
+            data = rv['ITEM']
+            try:
+                return int(data)
+            except ValueError:
+                # This seems to happen the first time of the day we
+                # print something.
+                log.warn("Could not parse data status item: %r" % data)
+        return rv
+
+    def get_totalized_value(self):
+        # Man page 4-63
+        return self._get_coupon_subtotal()
+
+    def get_remainder_value(self):
+        # Man page 4-63
+        rv = self.get_transaction_status()
+        if rv is not None:
+            value = float(rv['BRUTO']) / 1e2
+            if value < 0.00:
+                return 0.00
+        return rv
+
+    def _get_coupon_number(self):
+        return int(self.get_totalizers()[115:119])
+
+    def _open_voucher(self, index):
+        self.send_command(self.CMD_OPEN_VOUCHER, "%02d" % index)
+
+    def _add_value_to_counter(self, value, counter):
+        self.send_command(self.CMD_INCREMENT_NONFISCAL_COUNTER, counter,
+                          "%012d" % int(float(value) * 1e2), ' ' * 40)
+
+    #
+    # ICouponPrinter implementation
+    #
+
+    def coupon_identify_customer(self, customer, address, document):
+        # The arguments customer and address are here only for API
+        # compatibility, they are not supported by the printer.
+        self._customer_document = document
+
+    def coupon_is_customer_identified(self):
+        return len(self._customer_document) > 0
+
+    def coupon_open(self):
+        self.get_status().check_error()
+        if self._customer_document:
+            customer = self._format_string(self._customer_document,
+                                           self.CUSTOMER_CHAR_LEN,
+                                           'customer_document')
+        else:
+            customer = ''
+        try:
+            self.send_command(self.CMD_COUPON_OPEN, customer)
+        except InvalidState:
+            # if we catch InvalidState here, probably the printer is with
+            # a read X pending, so..
+            raise PendingReadX(_("A read X is pending."))
+
+
+    def coupon_add_item(self, code, description, price, taxcode,
+                        quantity=Decimal("1.0"), unit=UnitType.EMPTY,
+                        discount=Decimal("0.0"),
+                        surcharge=Decimal("0.0"), unit_desc=""):
+        if unit == UnitType.CUSTOM:
+            unit = UnitType.EMPTY
+        unit_code = self._consts.get_value(unit)
+        code = self._format_string(code[-13:], self.PRODUCT_CODE_CHAR_LEN, 'code')
+        orig_qty = quantity
+        quantity = self._format_quantity(quantity, 'quantity')
+        orig_price = price
+        price = self._format_price(price, 'price')
+
+        total = orig_qty * orig_price
+        total = self._format_total(total, 'total')
+        if taxcode in ['I', 'F', 'N']:
+            taxcode = self._format_string(taxcode, 3, "taxcode", 
+                                          left_justify=True)
+        else:
+            taxcode = 'T%s' % taxcode.rjust(2, '0')
+         
+        description = str(description)
+        if len(description) > self.DESCRIPTION_CHAR_LEN:
+            second_desc = description[self.DESCRIPTION_CHAR_LEN:]
+            description = description[:self.DESCRIPTION_CHAR_LEN]
+        else:
+            second_desc = ''
+        description = self._format_string(description,
+                                          self.DESCRIPTION_CHAR_LEN,
+                                          'description')
+        if second_desc:
+            second_desc = self._format_string(second_desc,
+                                              self.SECONDDESC_CHAR_LEN,
+                                              'second_desc')
+        self.send_command(self.CMD_COUPON_ADD_ITEM, code, quantity,
+                          price, total, unit_code, description, taxcode,
+                          second_desc)
+        if discount:
+            self.discount_item(discount)
+        elif surcharge:
+            self.surcharge_item(surcharge)
+            
+        return self.get_last_item_id()
+
+    def coupon_cancel_item(self, item_id):
+        self._check_integer(item_id, 'item_id')
+        item_id = self._format_string(item_id, self.ITEM_NUMBER_CHAR_LEN,
+                                      'item_id')
+        item_id = string.replace(item_id, ' ', '0')
+
+        self.send_command(self.CMD_ITEM_CANCEL, item_id)
+
+    def coupon_add_surcharge(self, item_id, value, description):
+        """
+        Valid surcharge types are::
+          "51": "surcharge"
+        The arguments item_id and descriptions only exit for API
+        compatibility
+        """
+        percentage = "0" * self.PERCENTAGE_CHAR_LEN
+        value = '%012d' % (value * Decimal('1e2'))
+        self.send_command(self.CMD_COUPON_ADD_SURCHARGE, '51', percentage, 
+                          value, 'S')
+
+    def coupon_cancel(self):
+        self.send_command(self.CMD_COUPON_CANCEL)
+
+    def cancel_last_coupon(self):
+        """Cancel the last non fiscal coupon or the last sale."""
+        self.send_command(self.CMD_COUPON_CANCEL)
+
+    def coupon_add_payment(self, payment_method, value, description='',
+                           custom_pm=''):
+
+        pm = payment_method.rjust(2, '0')
+        
+        if description:
+            description = '{' + description
+            
+        self.send_command(self.CMD_COUPON_TOTALIZE, pm,
+                          '%012d' % int(float(value) * 1e2), description)
+        return self.get_remainder_value()
+
+    def coupon_totalize(self, discount=Decimal("0.0"),
+                        surcharge=Decimal("0.0"), taxcode=TaxType.NONE):
+        """ Print the total value of the coupon.
+        The taxcode argument is useless here and exists only for API
+        compatibility
+        """
+
+        # FIXME: API changed: discount/surcharge was percentage,
+        # now is currency.
+        if discount:
+            self.discount_coupon(discount)
+        elif surcharge:
+            self.coupon_add_surcharge(None, surcharge, None)
+        self.send_command(self.CMD_COUPON_TOTALIZE)
+        return self.get_totalized_value()
+
+    def coupon_close(self, message=''):
+        self.send_command(self.CMD_COUPON_CLOSE)
+        self._customer_document = None
+        return self._get_coupon_number()
+
+    def close_till(self, previous_day=False):
+        # TODO Add a date optional argument here
+        """This is 'reduce Z' in Brazil"""
+        t = datetime.datetime.now()
+        day = self._format_datetime(t.day)
+        month = self._format_datetime(t.month)
+        year = str(t.year)[2:]
+        date = '%s%s%s' % (day, month, year)
+
+        # This will print a sales report after summarizing
+        print_report = 'N'
+
+        self.send_command(self.CMD_REDUCE_Z, print_report, date)
+
+    def summarize(self):
+        """This is 'read X' in Brazil"""
+        # This will print a sales report after summarizing
+        print_report = 'N'
+
+        self.send_command(self.CMD_READ_X, print_report)
+
+    def till_add_cash(self, value):
+        # XXX: The manual says that all the printer counters are defined by
+        # the user (i.e, can be programmed on the printer memory), so we don't
+        # have any counter related to "CASH IN" or "CASH OUT". This part must
+        # be considered when fixing bug #2246.  Right now i just consider
+        # the counter used by the printer at Async for cash in ("03")
+        self._open_voucher(1)
+        self._add_value_to_counter(value, "03")
+        self.coupon_add_payment(PaymentMethodType.MONEY, value)
+        self.coupon_close()
+
+    def till_remove_cash(self, value):
+        # XXX: The manual says that all the printer counters are defined by
+        # the user (i.e, can be programmed on the printer memory), so we don't
+        # have any counter related to "CASH IN" or "CASH OUT". This part must
+        # be considered when fixing bug #2246.  Right now i just consider
+        # the counter used by the printer at Async for cash out ("02")
+        self._open_voucher(1)
+        self._add_value_to_counter(value, "02")
+        self.coupon_close()
+
+    def till_read_memory(self, start, end):
+        raise NotImplementedError
+
+    def till_read_memory_by_reductions(self, start, end):
+        raise NotImplementedError
+
+    def _get_status(self):
+        """retornar status de impress�o com string de interpreta��o."""
+        return self.send_command(self.CMD_PRINTER_STATUS)
+
+    def get_capabilities(self):
+        return dict(item_code=Capability(max_len=14),
+                    item_id=Capability(digits=3),
+                    items_quantity=Capability(digits=4, decimals=3),
+                    item_price=Capability(digits=6, decimals=2),
+                    item_description=Capability(max_len=64),
+                    payment_value=Capability(digits=10, decimals=2),
+                    promotional_message=Capability(max_len=492),
+                    payment_description=Capability(max_len=80),
+                    customer_name=Capability(max_len=30),
+                    customer_id=Capability(max_len=28),
+                    customer_address=Capability(max_len=80),
+                    add_cash_value=Capability(digits=10, decimals=2),
+                    remove_cash_value=Capability(digits=10,
+                                                 decimals=2))
+
+    def get_constants(self):
+        return self._consts
+
+    #
+    # Auxiliar methods
+    #
+
+    def discount_coupon(self, value):
+        # We are using a only a discount by value for API compatibility
+        percentage = '0' * self.PERCENTAGE_CHAR_LEN
+        value = '%012d' % int(float(value) * 1e2)
+
+        # This will print a total right after add the discount
+        print_total = 'S'
+
+        self.send_command(self.CMD_COUPON_ADD_DISCOUNT, percentage, value,
+                          print_total)
+
+    def discount_item(self, value):
+        # We are using a only a discount by value for API compatibility
+        percentage = '0' * self.PERCENTAGE_CHAR_LEN
+        value = '%012d' % (value * Decimal('1e2'))
+        self.send_command(self.CMD_ITEM_ADD_DISCOUNT, percentage, value)
+
+    def surcharge_item(self, value):
+        # We are using a only a discount by value for API compatibility
+        percentage = '0' * self.PERCENTAGE_CHAR_LEN
+        value = '%012d' % (value * Decimal('1e2'))
+        self.send_command(self.CMD_ITEM_ADD_SURCHARGE, percentage, value)
+
+    def print_printer_parameters(self):
+        self.send_command(self.CMD_PRINT_PARAMETERS)
+
+    #### CHRISTIAN #############################################################
+    def get_data_movimento(self):
+        pass
+    def _get_totalizers_g(self):
+        return self._get_totalizers_h()
+        reply = self._get_totalizers('G')
+        print '--', reply
+
+        datas = ((5, 9, 'COO_I'), (9, 13, 'COO'), (13, 17, 'CRO'), 
+                 (17, 21, 'CRO_I'), (21, 25, 'CONT_X'))
+
+        if reply:
+            ret_val = {}
+            for d in datas:
+                idx_ini = d[0] 
+                idx_end = d[1]  
+                data_name = d[2]
+                
+                ret_val[data_name] = reply[idx_ini:idx_end]
+            return ret_val
+        return None
+
+    
+    def _get_totalizers_h(self):
+        reply = self._get_totalizers('H')
+        reply =  reply[2:]
+        datas = ((5, 26, 'SERIAL'), (26, 38, 'SB'), (38, 42, 'CRO'), 
+                 (42, 46, 'CRZ'), (46, 52, 'GNF'), (52, 58, 'GRG'), 
+                 (58, 64, 'CCF'), (64, 70, 'CFD'), (70, 76, 'COO'),
+                 (76, 80, 'CDC'), (80, 84, 'NCN'), (84, 88, 'NFC'),
+                 (88, 92, 'CFC'), (92, 110, 'GT'), (110, 124, 'VB'))
+
+        if reply:
+            ret_val = {}
+            for d in datas:
+                idx_ini = d[0] 
+                idx_end = d[1]  
+                data_name = d[2]
+                
+                ret_val[data_name] = reply[idx_ini:idx_end]
+            return ret_val
+        return None
+    
+    def _get_coupon_subtotal(self):
+        rv = self.get_transaction_status()
+        if rv is not None:
+            return Decimal(rv['LIQ']) / Decimal("1e2")
+
+
+#****************************
+        
+    def get_coo(self):
+        retval = self._get_totalizers_h()
+        return int(retval['COO'])
+
+    def get_cro(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CRO'])
+    
+    def get_crz(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CRZ'])
+    
+    def get_gnf(self):
+        retval = self._get_totalizers_h()
+        return int(retval['GNF'])
+    
+    def get_grg(self):
+        retval = self._get_totalizers_h()
+        return int(retval['GRG'])
+    
+    def get_ccf(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CCF'])
+    
+    def get_cfd(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CFD'])
+    
+    def get_cdc(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CDC'])
+    
+    def get_ncn(self):
+        retval = self._get_totalizers_h()
+        return int(retval['NCN'])
+    
+    def get_nfc(self):
+        retval = self._get_totalizers_h()
+        return int(retval['GNF'])
+
+    def get_cfc(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CFC'])
+   
+    def get_serial(self):
+        retval = self._get_totalizers_h()
+        return retval['SERIAL'][:-1]
+
+    
+    def get_grande_total(self):
+        retval = self._get_totalizers_h()
+        return Decimal(int(retval['GT'])) / Decimal("1e2")
+
+    def get_venda_bruta(self):
+        retval = self._get_totalizers_h()
+        return Decimal(int(retval['VB'])) / Decimal("1e2")
+
+    
+    def get_ecf_date(self):
+        retval = self.get_transaction_status()
+        
+        data = retval['DATA']
+        horas = retval['HORA']
+        
+        dia = data[:2]
+        mes = data[2:4]
+        ano = data [4:]
+        hora = horas[:2]
+        minutos = horas[2:]
+        retval = datetime.datetime(2000 + int(ano), int(mes), int(dia), int(hora), int(minutos), 0)
+        return retval
+
+    def get_status(self, val=None, cmd_status=None):
+            status = self._get_status()
+
+            sinal = status[1]
+            st1 =  int(status[3])
+            st2 =  int(status[4])
+            st3 =  int(status[5])
+            
+            trans = self.get_transaction_status()
+             
+            class Status(object):
+                def __init__(self, parent=None):
+                    self.status = status
+                    self.parent = parent
+                @property
+                def open(self):
+                    return trans['STATUS'] == 'P'
+                @property
+                def pagamento_existe(self):
+#                    total_pago = (Decimal(trans['LIQ']) / Decimal("1e2")) - (Decimal(trans['FALTA']) / Decimal("1e2"))
+                    total_pago = Decimal(trans['FALTA']) / Decimal("1e2")
+                    print trans
+                    return total_pago > 0
+                @property
+                def out_paper(self):
+                    return sinal == '-' and st3 == 5
+                @property
+                def block_z(self):
+                    return trans['REDUCAO'] == 'F'
+    
+                @property
+                def off_line(self):
+                    return sinal == '-' and st3 == 1
+                    
+                
+                def check_error(self):
+                    if self.block_z:
+                        raise PendingReduceZ('Bloqueio por Redu��o Z')
+                    if self.open:
+                        raise CouponOpenError('Cupom aberto')
+                    if st1 and st2 and st3 == 5:
+                        raise OutofPaperError('Impressora sem papel.')
+            return Status(self)
+
+    def _apura_totais(self):
+        """
+        Coment�rios:
+        Este comando captura informa��es do equipamento e soma dos
+        totais armazenados na Mem�ria Fiscal, conforme per�odo selecionado, 
+        aceito nos modelos IF ST200 e IF ST120.
+        
+        As seguintes informa��es s�o recebidas:
+        Modelo, n�mero de fabrica��o, tipo do ECF, n�mero de ordem do ECF,
+        n�mero de ordem do usu�rio, CNPJ, Inscri��o Estadual, s�mbolo da
+        moeda, n�mero de s�rie da MFD, Grande Total.
+        """
+        
+        reply = self.send_command('59')
+        ret = reply[4:] 
+        while True:
+            reply = self.send_command('++}')
+            if reply[:2] == '.]':
+                break
+            ret += reply[6:]
+
+        data_mov_z_str = ret[171:191].split('/') # data do movimento do ultimo Z
+        data_fiscal_str = ret[198:208].split('/')
+
+        data_mov_z = datetime.date(int(data_mov_z_str[2]),
+                                    int(data_mov_z_str[1]),
+                                    int(data_mov_z_str[0]))
+        data_fiscal = datetime.date(int(data_fiscal_str[2]),
+                                    int(data_fiscal_str[1]),
+                                    int(data_fiscal_str[0]))
+        ret_dict = {'DATA_MOV_Z': data_mov_z,
+                    'DATA_FISCAL': data_fiscal,
+                    'COO_INI': int(ret[209:215])  
+                    
+                    }
+        return ret_dict
+
+    def get_totalizadores(self):
+        retval = []
+
+        ret_cmd = self.send_command('27', '1' )
+        if ret_cmd[:3] == '.+C':
+            retval.append(
+                ('CANC', Decimal(ret_cmd[76:88]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('DESC', Decimal(ret_cmd[92:104]) / Decimal('1e2'), 'ICMS')
+            )
+
+
+        ret_cmd = self.send_command('27', '3' )
+        if ret_cmd[:3] == '.+C':
+            retval.append(
+                ('I', Decimal(ret_cmd[21:33]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('N', Decimal(ret_cmd[33:45]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('F', Decimal(ret_cmd[45:57]) / Decimal('1e2'), 'ICMS')
+            )
+        ret_cmd = ''
+
+        ret_cmd_4 = self.send_command('27', '4' )
+        if ret_cmd_4[:3] == '.+C':
+            ret_cmd += ret_cmd_4[7:]
+
+        ret_cmd_5 = self.send_command('27', '5' )
+        if ret_cmd_5[:3] == '.+C':
+            ret_cmd += ret_cmd_5[7:]
+
+        if ret_cmd:
+            tipo = {}
+            i = 0
+            for r in range(14):
+                reg = ret_cmd[i:i+15]
+                i += 15
+                if reg[:3] == '   ':
+                    continue
+                tipo[reg[:3]] = Decimal(reg[3:]) / Decimal('1e2')
+
+            ret_cmd_29 = ''
+            ret_cmd_29_3 = self.send_command('29', '3' )
+            if ret_cmd_29_3[:3] == '.+T':
+                ret_cmd_29 += ret_cmd_29_3[48:48+28]
+
+            ret_cmd_29_4 = self.send_command('29', '4' )
+            if ret_cmd_29_4[:3] == '.+T':
+                ret_cmd_29 += ret_cmd_29_4[7:]
+
+            ret_cmd_29_5 = self.send_command('29', '5' )
+            if ret_cmd_29_5[:3] == '.+T':
+                ret_cmd_29 += ret_cmd_29_5[7:28]
+
+            i = 0
+            if ret_cmd_29:
+                for r in range(14):
+                    reg = ret_cmd_29[i:i+7]
+                    i += 7
+                    it = reg[0] # Tipo Tributa��o, T=ICMS
+                    ii = reg[1:3] # Indice da tributa��o
+                    iv = reg[3:] # Valor da tributa��o
+
+                    if it == ' ':
+                        continue
+                    getval = tipo.get(it+ii, False)
+                    if getval:
+                        if it == 'T':
+                            tipo_t = 'ICMS'
+                        else:
+                            tipo_t = 'ISQN'
+                        retval.append(
+                            ('%02d' % (r+1), iv, tipo_t)
+                        )
+        return retval
+
+    def _get_taxes(self):
+        
+        retval = []
+        
+        ret_cmd = self.send_command('27', '1' )
+        if ret_cmd[:3] == '.+C':
+            retval.append(
+                ('CANC', Decimal(ret_cmd[76:88]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('DESC', Decimal(ret_cmd[92:104]) / Decimal('1e2'), 'ICMS')
+            )
+            
+        
+        ret_cmd = self.send_command('27', '3' )
+        if ret_cmd[:3] == '.+C':
+            retval.append(
+                ('I', Decimal(ret_cmd[21:33]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('N', Decimal(ret_cmd[33:45]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('F', Decimal(ret_cmd[45:57]) / Decimal('1e2'), 'ICMS')
+            )
+        ret_cmd = ''    
+
+        ret_cmd_4 = self.send_command('27', '4' )
+        if ret_cmd_4[:3] == '.+C':
+            ret_cmd += ret_cmd_4[7:] 
+        
+        ret_cmd_5 = self.send_command('27', '5' )
+        if ret_cmd_5[:3] == '.+C':
+            ret_cmd += ret_cmd_5[7:] 
+
+        if ret_cmd:
+            tipo = {}
+            i = 0
+            for r in range(14):
+                reg = ret_cmd[i:i+15]
+                i += 15    
+                if reg[:3] == '   ':
+                    continue
+                tipo[reg[:3]] = Decimal(reg[3:]) / Decimal('1e2')
+            
+            ret_cmd_29 = ''            
+            ret_cmd_29_3 = self.send_command('29', '3' )
+            if ret_cmd_29_3[:3] == '.+T':
+                ret_cmd_29 += ret_cmd_29_3[48:48+28]
+
+            ret_cmd_29_4 = self.send_command('29', '4' )
+            if ret_cmd_29_4[:3] == '.+T':
+                ret_cmd_29 += ret_cmd_29_4[7:]
+
+            ret_cmd_29_5 = self.send_command('29', '5' )
+            if ret_cmd_29_5[:3] == '.+T':
+                ret_cmd_29 += ret_cmd_29_5[7:28]
+
+            i = 0
+            if ret_cmd_29:
+                for r in range(14):
+                    reg = ret_cmd_29[i:i+7]
+                    i += 7
+                    it = reg[0] # Tipo Tributa��o, T=ICMS
+                    ii = reg[1:3] # Indice da tributa��o
+                    iv = reg[3:] # Valor da tributa��o
+       
+                    if it == ' ':
+                        continue
+                    getval = tipo.get(it+ii, False)
+                    if getval:
+                        if it == 'T':
+                            tipo_t = 'ICMS'
+                        else:
+                            tipo_t = 'ISQN'
+                        retval.append(
+                            (iv, getval, tipo_t)
+                        )
+        return retval
+        
+    def _get_aliquotas(self, tipo='ICMS'):
+        AliquotasStr = '';
+        Posicao      = 1;
+    
+        RetCmd = self.send_command('29', '3' )
+        if RetCmd[:3] == '.+T':
+            AliquotasStr += '%s|' % RetCmd
+        
+        RetCmd = self.send_command('29', '4' )
+        if RetCmd[:3] == '.+T':
+            AliquotasStr += '%s|' % RetCmd
+
+        RetCmd = self.send_command('29', '5' )
+        if RetCmd[:3] == '.+T':
+            AliquotasStr += RetCmd[7:] 
+  
+        return AliquotasStr
+
+    def get_date_last_mov_rz(self):
+        '''
+        :return: Retorna a data do movimento da ultima reducao Z emitida
+        '''
+        data = 0
+        try:
+            ultimo_z = self.get_last_reduce()
+            if ultimo_z:
+                data = ultimo_z.get('DATA_MOV_Z') # data do movimento do ultimo Z emitido
+
+        except Exception as e:
+            log.error('get_date_last_mov_rz: %s' % str(e))
+
+        return data
+     
+    def get_sintegra(self):
+        regs = self._get_totalizers_h()
+        totais=self._apura_totais()
+        data = Settable(
+            opening_date=totais['DATA_FISCAL'],
+            serial=regs['SERIAL'],
+            serial_id=0,
+            coupon_start=int(totais['COO_INI']),
+            coupon_end=int(regs['COO']),
+            cro=int(regs['CRO']),
+            crz=int(regs['CRZ']),
+            coo=int(regs['COO']),
+            ccf=int(regs['CCF']),
+            gnf=int(regs['GNF']),
+            grg=int(regs['GRG']),
+            cdc=int(regs['CDC']),
+            period_total=self.get_venda_bruta(),
+            total=self.get_grande_total(),
+            taxes=self._get_taxes()
+        )
+        return data        
+    def get_gerencial_reports(self):
+        retval = {}
+        reply = self.send_command('29', 'M')
+        i = 7
+        for x in range(8):
+            s = reply[i:i+15]
+            i += 15
+            if s == ' ' * 15:
+                continue
+            retval[x] = s
+        return retval
+        
+    def gerencial_report_open(self, id=0):    
+        reports = self.get_gerencial_reports()
+        if not reports.get(id, False):
+            raise Exception('Relatorio gerencial n�o cadastrado') 
+        return reports 
\ Falta o caracter nova linha no final do arquivo
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/sweda/IFS9000I.py trunk/stoqdrivers/printers/sweda/IFS9000I.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/sweda/IFS9000I.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/printers/sweda/IFS9000I.py	2017-01-23 16:54:44.657415257 -0200
@@ -1,4 +1,4 @@
-# -*- Mode: Python; coding: iso-8859-1 -*-
+# -*- Mode: Python; coding: utf-8 -*-
 # vi:si:et:sw=4:sts=4:ts=4
 
 ##
@@ -26,32 +26,36 @@
 """
 Sweda IFS9000I driver
 """
-
+import math
+import operator
 import string
 import datetime
+import os
+from ctypes import CDLL
 from decimal import Decimal
-
-from kiwi.log import Logger
+import re
+from kiwi.python import Settable
+from stoqdrivers.logs.log import traced as log
 from zope.interface import implements
 
 from stoqdrivers.enum import PaymentMethodType, TaxType, UnitType
-from stoqdrivers.exceptions import (PrinterError, CloseCouponError,
-                                    PendingReadX, CommandError,
-                                    CouponOpenError, CommandParametersError,
-                                    CouponNotOpenError, ReduceZError,
-                                    HardwareFailure, DriverError,
-                                    OutofPaperError, PendingReduceZ,
-                                    InvalidState, CancelItemError,
-                                    AlreadyTotalized)
+from stoqdrivers.exceptions import (PrinterError, CloseCouponError,  PendingReadX, CommandError, CouponOpenError,
+                                    CommandParametersError, CouponNotOpenError, ReduceZError, HardwareFailure,
+                                    DriverError,  OutofPaperError, PendingReduceZ, InvalidState, CancelItemError,
+                                    AlreadyTotalized, PaymentAdditionError, CodigoInvalido )
 from stoqdrivers.interfaces import ICouponPrinter
 from stoqdrivers.serialbase import SerialBase
 from stoqdrivers.printers.capabilities import Capability
 from stoqdrivers.printers.base import BaseDriverConstants
 from stoqdrivers.translation import stoqdrivers_gettext
+import time
 
 _ = stoqdrivers_gettext
 
-log = Logger('stoqdrivers.sweda')
+root_path = os.path.dirname(os.path.realpath(__file__))
+
+TWOPLACES = Decimal(10) ** -2
+TREPLACES = Decimal(10) ** -3
 
 class IFS9000IConstants(BaseDriverConstants):
     _constants = {
@@ -90,6 +94,7 @@
     CMD_COUPON_OPEN = '17'
     CMD_COUPON_ADD_ITEM = '01'
     CMD_ITEM_ADD_DISCOUNT = '02'
+    CMD_ITEM_ADD_SURCHARGE = '60'
     CMD_ITEM_CANCEL = '04'
     CMD_COUPON_ADD_DISCOUNT = '03'
     CMD_COUPON_ADD_SURCHARGE = '11'
@@ -107,10 +112,11 @@
     CMD_SETUP_CLOCK = '35'
     CMD_SETUP_PAYMENT_METHOD = '39'
     CMD_TRANSACTION_STATUS = '28'
+    CMD_LEITURA_DE_TABELAS = '29'
     CMD_GET_PRINTER_TOTALIZERS = '27'
     CMD_OPEN_VOUCHER = '19'
     CMD_INCREMENT_NONFISCAL_COUNTER = '07'
-
+    CMD_IDENTIFIER_PAFECF = '57'
     #
     # Settings for printer command parameters
     #
@@ -122,7 +128,7 @@
     # argument define the label for product units
     DESCRIPTION_CHAR_LEN = 23
     # A second description allowed when adding items
-    SECONDDESC_CHAR_LEN = 40
+    SECONDDESC_CHAR_LEN = 200
     PRODUCT_CODE_CHAR_LEN = 13
     TAXCODE_CHAR_LEN = 3
     PRINT_ONE_LINE_CHAR_LEN = 2
@@ -151,16 +157,17 @@
     STANDARD_SURCHARGE_CODE ='51'
 
 
-    errors_dict = {'DIA ENCERRADO': (PendingReadX, _("A Read X is pending")),
-                   'ERRO-COMAND INVALIDO': (CommandError, _("The command is "
-                                                            "invalid")),
+    errors_dict = {'DIA ENCERRADO': (PendingReadX, _("Redução X Pendente")),
+                   'ERRO-COO NAO EXISTE': (CodigoInvalido, _("Não existe CDC pendente no ECF que "
+                                                             "corresponda aos argumentos informados")),
+                   'ERRO-COMAND INVALIDO': (CommandError, _("Comando Inválido")),
                    'ERRO-OPERACAO NAO ENCERRADA': (CouponOpenError,
-                                                   _("A coupon already exist")),
+                                                   _("Cupom Aberto")),
                    'ERRO-PARAMETROS DO COMAND INVALIDOS': (CommandParametersError,
-                                                           _("Parameters invalid.")),
-                   'ERRO-OPERACAO NAO ABERTA': (CouponNotOpenError,
-                                                _("There is no coupon opened")),
-                   'JA  FEZ REDU��O': (ReduceZError, _("Reduce Z already done.")),
+                                                           _("Paramêtro Inválido.")),
+                   'ERRO-OPERACAO NAO ABERTA': (CouponNotOpenError, 
+                                                "Cupom não foi aberto"),
+                   'JA  FEZ REDU??O': (ReduceZError, _("Redução Z já efetuada.")),
                    '^RELOGIO ZERADO-PROGRAMAR': (PrinterError,
                                                  _("Printer clock isn't "
                                                    "programmed yet.")),
@@ -169,18 +176,15 @@
                                                           "programmed yet.")),
                    '^CHAMAR ASSISTENCIA TECNICA': (HardwareFailure,
                                                    _("Problem in fiscal printer")),
-                   '^CMOS: ERRO-MEM�RIA TAXAS/PARAMET': (HardwareFailure,
+                   '^CMOS: ERRO-MEM?RIA TAXAS/PARAMET': (HardwareFailure,
                                                          _("Problem in fiscal "
                                                            "printer")),
                    'ERRO-TOTAL: NAO HOUVE LANCAMENTO': (CloseCouponError,
-                                                        _("There is no items "
-                                                          "added to coupon")),
+                                                        _("Não há itens adicionado ao cupom")),
                    'ERRO - VERIFICAR PAPEL': (OutofPaperError, _("Out of paper")),
-                   'ENCERRAR O DIA !': (PendingReduceZ, _("A Reduce Z is "
-                                                          "pending")),
+                   'ENCERRAR O DIA !': (PendingReduceZ, _("Redução Z Pendente")),
                    'ERRO-COMANDO NAO PERMITIDO': (InvalidState,
-                                                  _("Invalid state for command "
-                                                    "execution")),
+                                                  "Commado não permitido"),
                    'ERRO-SEM OS DADOS DO ITEM NA MEMORIA': (CancelItemError,
                                                             _("It is not "
                                                               "possible cancel "
@@ -188,19 +192,29 @@
                                                               "it wasn't added "
                                                               "yet")),
                    'ERRO-CUPOM TOTALIZADO': (AlreadyTotalized,
-                                             _("The coupon is already "
-                                               "totalized")),
+                                             _("O Cupom já foi totalizado")),
                    'ERRO-ITEM INEXISTE': (CancelItemError,
                                           _("There is no such item to cancel "
                                             "in the coupon")),
                    'ERRO-CANC: NAO HOUVE LANCAMENTO' : (CouponNotOpenError,
                                                         (_("There's no open document to cancel"))),
-                   }
+                   'ERRO-CODIGO DA MODALIDADE INCORRETO' : (PaymentAdditionError,
+                                                            "Finalizadora não cadastrada na ECF."),
+                   'ERRO-CODIGO INVALIDO' : (CodigoInvalido, """1. Código do produto igual a zeros.
+2. Código do produto igual a espaços não é ISSQN.
+3. Código do acumulador não-fiscal não-numérico ou inexistente.
+4. Código do acumulador de acréscimo inválido.
+5. Código da mensagem informado no comando '<ESC>.09' inexistente.
+6. Formato do comando '<ESC>.09' incorreto.""")
+    }
 
     def __init__(self, port, consts=None):
         self._consts = consts or IFS9000IConstants
         SerialBase.__init__(self, port)
-        self._customer_document = None
+        self._customer_name = ''
+        self._customer_document = ''
+        self._customer_address = ''
+        self._customer = ''
 
     #
     # Initializing Fiscal Printer
@@ -373,18 +387,49 @@
     #
 
     def get_transaction_status(self):
+        """28 - Status de Transaçao (pag. 75)"""
+        datas = ((3, 4, 'SEQ'), (7, 3, 'ITEM'), (10, 1, 'STATUS'), 
+                 (11, 8, 'TRANSACAO'), (19, 2, 'ESCAPE'), (21, 1, 'REDUCAO'),
+                 (22, 12, 'LIQ'), (34, 12, 'BRUTO'), (46, 1, 'ERRO'),
+                 (47, 6, 'DATA'), (53, 4, 'HORA'), (57, 1, 'VERAO'), 
+                 (58, 35, 'MENSERRO'), (93, 3, 'ECF'), (96, 1, 'PAPEL'),
+                 (97, 1, 'ABREV'), (98, 12, 'FALTA'), (110, 12, 'REC'),
+                 (122, 1, 'FAZX'), (123, 1, 'ABRIR'), (124, 1, 'PROG'),
+                 (125, 1, 'VINC'), (126, 1, 'VINC2'), (127, 1, 'SLIP'))
+             
         reply = self.writeline(self.CMD_TRANSACTION_STATUS)
+
         if reply[1] == '+':
-            return reply[2:]
+            ret_val = {}
+            for d in datas:
+                idx_ini = d[0] -1 
+                idx_end = idx_ini + d[1] 
+                data_name = d[2]
+                
+                ret_val[data_name] = reply[idx_ini:idx_end]
+            return ret_val
         return None
 
+    def _reset(self):
+        self._customer_name = ''
+        self._customer_document = ''
+        self._customer_address = ''
+        self._customer = ''
+
+    def _get_totalizers(self, type):
+        reply = self.send_command(self.CMD_GET_PRINTER_TOTALIZERS, type)
+        return reply
+    
+     
     def get_totalizers(self, type="1"):
         reply = self.writeline(self.CMD_GET_PRINTER_TOTALIZERS + type)
         if reply[1] != '+':
             raise ValueError("Inconsistent package received while "
                              "reading printer totalizers")
         return reply[5:]
-
+    def _send_command(self, command, *args, **kwargs):
+        return None
+    
     def send_command(self, command, *params):
         """ Send a command to printer.
 
@@ -392,17 +437,26 @@
         params: a list of parameter to this command (all parameters
         must be string)
         """
-        reply = self.writeline(command + ''.join(params))
-        return self.handle_error(reply)
+        cmd = command + ''.join(params)
+
+        reply = self.writeline(cmd)
+
+        complete = False
+        if command == self.CMD_PRINTER_STATUS:
+            return reply
+        elif command == '59':
+            complete = True
+        return self.handle_error(reply, complete=complete)
 
-    def handle_error(self, reply):
+    def handle_error(self, reply, complete=False):
         # Page 4-2
         # format: |.-NNNNERROR_MESSAGE}|
-        if reply[1] != '-':
-            return reply[2:]
+        if reply[1] != '-' or reply[2] == 'P':
+            return reply[2:] if complete else reply
         errmsg = reply[6:]
         try:
             exception, reason = self.errors_dict[errmsg]
+            
             raise exception(reason)
         except KeyError:
             raise DriverError(errmsg)
@@ -411,7 +465,7 @@
         # Man page 4-62
         rv = self.get_transaction_status()
         if rv is not None:
-            data = rv[4:7]
+            data = rv['ITEM']
             try:
                 return int(data)
             except ValueError:
@@ -422,15 +476,13 @@
 
     def get_totalized_value(self):
         # Man page 4-63
-        rv = self.get_transaction_status()
-        if rv is not None:
-            return Decimal(rv[19:31]) / Decimal("1e2")
+        return self._get_coupon_subtotal()
 
     def get_remainder_value(self):
         # Man page 4-63
         rv = self.get_transaction_status()
         if rv is not None:
-            value = float(rv[31:43]) / 1e2
+            value = float(rv['BRUTO']) / 1e2
             if value < 0.00:
                 return 0.00
         return rv
@@ -442,7 +494,8 @@
         self.send_command(self.CMD_OPEN_VOUCHER, "%02d" % index)
 
     def _add_value_to_counter(self, value, counter):
-        self.send_command(self.CMD_INCREMENT_NONFISCAL_COUNTER, counter,
+
+        self.send_command(self.CMD_INCREMENT_NONFISCAL_COUNTER, "%02d" % counter,
                           "%012d" % int(float(value) * 1e2), ' ' * 40)
 
     #
@@ -452,26 +505,54 @@
     def coupon_identify_customer(self, customer, address, document):
         # The arguments customer and address are here only for API
         # compatibility, they are not supported by the printer.
+        self._customer_name = customer
         self._customer_document = document
+        self._customer_address = address
 
     def coupon_is_customer_identified(self):
-        return len(self._customer_document) > 0
+        ret = False
+        if self._customer_document:
+            ret = True 
+        return ret 
 
     def coupon_open(self):
+        self.get_status().check_error()
         if self._customer_document:
-            customer = self._format_string(self._customer_document,
+            self._customer = self._format_string(self._customer_document,
                                            self.CUSTOMER_CHAR_LEN,
                                            'customer_document')
         else:
-            customer = ''
+            self._reset()
         try:
-            self.send_command(self.CMD_COUPON_OPEN, customer)
+            return self.send_command(self.CMD_COUPON_OPEN, self._customer)
+            self._reset()
         except InvalidState:
             # if we catch InvalidState here, probably the printer is with
             # a read X pending, so..
             raise PendingReadX(_("A read X is pending."))
 
 
+    def arredonda(self, v, d=2):
+        ''' O arredondamento segue de acordo com a Norma NBR 5891/77 da ABNT de dezembro de 1977
+            está no manual da Sweda
+        '''
+        parte_1, parte_2 = str(Decimal(v) * Decimal(100.0)).split('.')
+        if parte_2[:1] == '5':
+            # testa se o proximo algarismo depois do 5 eh 0 ou nao tem nada(que eh zero)
+            if len(parte_2) < 2 or parte_2[1:2] == '0':
+                # testa se o ultimo numero considerado eh impar, se for soma 1
+                if int(parte_1[-1]) % 2 == 1:
+                    parte_1 = str(int(parte_1) + 1)
+            else:
+                parte_1 = str(int(parte_1) + 1)
+
+        elif int(parte_2[:1]) > 5:
+            parte_1 = str(int(parte_1) + 1)
+
+        parte_1 = parte_1[:-2] + '.' + parte_1[-2:]
+        c = Decimal(10) ** -d
+        return Decimal(parte_1).quantize(c)
+
     def coupon_add_item(self, code, description, price, taxcode,
                         quantity=Decimal("1.0"), unit=UnitType.EMPTY,
                         discount=Decimal("0.0"),
@@ -479,17 +560,21 @@
         if unit == UnitType.CUSTOM:
             unit = UnitType.EMPTY
         unit_code = self._consts.get_value(unit)
-        code = self._format_string(code, self.PRODUCT_CODE_CHAR_LEN, 'code')
+        code = self._format_string(code[-13:], self.PRODUCT_CODE_CHAR_LEN, 'code')
         orig_qty = quantity
         quantity = self._format_quantity(quantity, 'quantity')
         orig_price = price
         price = self._format_price(price, 'price')
 
-        total = orig_qty * orig_price
-        total = self._format_total(total, 'total')
+        total = self.arredonda(orig_qty * orig_price)
 
-        taxcode = self._format_string(taxcode,
-                                      3, "taxcode", left_justify=True)
+        total = self._format_total(total, 'total')
+        if taxcode in ['I', 'F', 'N']:
+            taxcode = self._format_string(taxcode, 3, "taxcode", 
+                                          left_justify=True)
+        else:
+            taxcode = 'T%s' % taxcode.rjust(2, '0')
+         
         description = str(description)
         if len(description) > self.DESCRIPTION_CHAR_LEN:
             second_desc = description[self.DESCRIPTION_CHAR_LEN:]
@@ -503,10 +588,18 @@
             second_desc = self._format_string(second_desc,
                                               self.SECONDDESC_CHAR_LEN,
                                               'second_desc')
-        self.send_command(self.CMD_COUPON_ADD_ITEM, code, quantity,
-                          price, total, unit_code, description, taxcode,
-                          second_desc)
 
+        reply = self.send_command(self.CMD_COUPON_ADD_ITEM, code, quantity,
+                                  price, total, unit_code, description, 
+                                  taxcode, second_desc)
+
+        if reply[1] == '-' and reply[3:6] == '555':
+            raise OutofPaperError('Impressora sem papel!') 
+        if discount:
+            self.discount_item(discount)
+        elif surcharge:
+            self.surcharge_item(surcharge)
+            
         return self.get_last_item_id()
 
     def coupon_cancel_item(self, item_id):
@@ -524,17 +617,12 @@
         The arguments item_id and descriptions only exit for API
         compatibility
         """
-        percentage = "%0*d" % (self.PERCENTAGE_CHAR_LEN,
-                               int(value * Decimal("1e2")))
-        coupon_total = self.get_totalized_value()
-        surcharge_value = coupon_total * (value / Decimal("1e2"))
-        surcharge_total = self._format_float(surcharge_value, 'value',
-                                             self.SURCHARGE_CHAR_LEN,
-                                             self.SURCHARGE_DEC_SEPARATOR,
-                                             self.SURCHARGE_ZERO_DIGITS)
-        surcharge_type = self.STANDARD_SURCHARGE_CODE
-        self.send_command(self.CMD_COUPON_ADD_SURCHARGE, surcharge_type,
-                          percentage, surcharge_total, 'S')
+        percentage = "0000"
+        value *= 10
+        value = '%012d' % (Decimal(value) * Decimal('1e2'))
+
+        self.send_command(self.CMD_COUPON_ADD_SURCHARGE, '50', percentage, 
+                          value, 'S')
 
     def coupon_cancel(self):
         self.send_command(self.CMD_COUPON_CANCEL)
@@ -545,16 +633,43 @@
 
     def coupon_add_payment(self, payment_method, value, description='',
                            custom_pm=''):
-        if not custom_pm:
-            pm = self._consts.get_value(payment_method)
-        else:
-            pm = custom_pm
+
+        pm = payment_method.rjust(2, '0')
+        
         if description:
             description = '{' + description
-        self.send_command(self.CMD_COUPON_TOTALIZE, pm,
-                          '%012d' % int(float(value) * 1e2), description)
+            
+        self.send_command(self.CMD_COUPON_TOTALIZE, pm, '%012d' % int(value * Decimal('1e2')), description)
         return self.get_remainder_value()
 
+    def _configure_payment_methods(self, descricao=u''):
+        ind = 0
+        finalizadoraNaEcf = {}
+        for tipo in ('5', '6', 'Q', 'R'):
+            reply = self.send_command(self.CMD_LEITURA_DE_TABELAS, tipo)[7:]
+            if  tipo == '5':
+                for ret in (reply[29:44], reply[45:60], reply[61:76]):
+                    ind += 1
+                    finalizadoraNaEcf[ret] = str(ind).rjust(2, '0')
+            else:
+                i = 16
+                x = 1
+                while True:
+                    print ind
+                    if ind == 20:
+                        break
+                    ret = reply[x:i]
+                    x = i+1
+                    i += 16
+                    ind += 1
+                    if len(ret) == 15 and  ret != '               ':
+                        finalizadoraNaEcf[ret] = str(ind).rjust(2, '0')
+        try:
+            result = finalizadoraNaEcf[descricao.ljust(15, ' ')]
+        except:
+            result =  False
+        return result
+
     def coupon_totalize(self, discount=Decimal("0.0"),
                         surcharge=Decimal("0.0"), taxcode=TaxType.NONE):
         """ Print the total value of the coupon.
@@ -568,17 +683,70 @@
             self.discount_coupon(discount)
         elif surcharge:
             self.coupon_add_surcharge(None, surcharge, None)
-        self.send_command(self.CMD_COUPON_TOTALIZE)
+
+        # tenta totalizar o cupom de qualquer jeito para não repetir o comando por completo e
+        # tentar dar o desconto/acrescimo novamente
+        while True:
+            try:
+                self.send_command(self.CMD_COUPON_TOTALIZE)
+                break
+            except Exception as e:
+                log.error("Erro ao totalizar o cupom: %s" % str(e))
+                time.sleep(1)
+
         return self.get_totalized_value()
 
     def coupon_close(self, message=''):
+        new_message = ''
+        if message:
+            new_cmd = False
+            new_message = ''
+            stx_msg = ''
+            lines = message.split('\n')
+            idx = 0
+            for x in range(8):
+                if x == 3 and self._customer_name:
+                    line = 'CLIENTE: %s' % self._customer_name
+                    idx -= 1
+                elif x == 4 and self._customer_address:
+                    line = 'ENDERECO: %s' % self._customer_address
+                    idx -= 1
+                else:
+                    if idx == len(lines):
+                        break
+                    else:
+                        line = lines[idx]
+                        if not line:
+                            idx += 1
+                            continue
+                idx += 1
+
+                if len(line) > 40:
+                    new_cmd = True
+
+                stx_msg += line + '\n'
+                line = line[:40]
+
+                new_message += '1' + line.ljust(40, ' ')
+
+            if new_message:
+                stx_msg = re.sub(r'NOTA LEGAL:', chr(22) + chr(73) + r'NOTA LEGAL:' + chr(22) + chr(65) , stx_msg)
+                self.send_command_stx('*07|', stx_msg)
+            else:
+                self.send_command(self.CMD_COUPON_CLOSE, 'NN', new_message, '}')
+            print stx_msg
+            print '*' * 60
+            print new_message
         self.send_command(self.CMD_COUPON_CLOSE)
         self._customer_document = None
-        return self._get_coupon_number()
+        self._customer_name = ''
+        self._customer_address = ''
+        return True
 
     def close_till(self, previous_day=False):
         # TODO Add a date optional argument here
         """This is 'reduce Z' in Brazil"""
+
         t = datetime.datetime.now()
         day = self._format_datetime(t.day)
         month = self._format_datetime(t.month)
@@ -588,7 +756,15 @@
         # This will print a sales report after summarizing
         print_report = 'N'
 
-        self.send_command(self.CMD_REDUCE_Z, print_report, date)
+        ret_cmd =  self.send_command(self.CMD_REDUCE_Z, print_report, date)
+        st_dict = {
+            '1': Exception('Impressora off-line'),
+            '2': Exception('TIME-OUT de transmissão'),
+            '5': Exception('Sem/Papel'),
+            }
+        if ret_cmd[1] == '-' and ret_cmd[5] in st_dict.keys():
+            raise st_dict[ret_cmd[5]]
+        return ret_cmd
 
     def summarize(self):
         """This is 'read X' in Brazil"""
@@ -597,43 +773,201 @@
 
         self.send_command(self.CMD_READ_X, print_report)
 
-    def till_add_cash(self, value):
+    def till_add_cash(self, value, totalizador, meio):
         # XXX: The manual says that all the printer counters are defined by
         # the user (i.e, can be programmed on the printer memory), so we don't
         # have any counter related to "CASH IN" or "CASH OUT". This part must
         # be considered when fixing bug #2246.  Right now i just consider
         # the counter used by the printer at Async for cash in ("03")
         self._open_voucher(1)
-        self._add_value_to_counter(value, "03")
-        self.coupon_add_payment(PaymentMethodType.MONEY, value)
+        self._add_value_to_counter(value, totalizador)
+        self.coupon_add_payment('%02d' % meio, value)
         self.coupon_close()
 
-    def till_remove_cash(self, value):
+    def till_remove_cash(self, value, cod=0, text=''):
         # XXX: The manual says that all the printer counters are defined by
         # the user (i.e, can be programmed on the printer memory), so we don't
         # have any counter related to "CASH IN" or "CASH OUT". This part must
         # be considered when fixing bug #2246.  Right now i just consider
         # the counter used by the printer at Async for cash out ("02")
-        self._open_voucher(1)
-        self._add_value_to_counter(value, "02")
-        self.coupon_close()
-
-    def till_read_memory(self, start, end):
-        raise NotImplementedError
-
-    def till_read_memory_by_reductions(self, start, end):
-        raise NotImplementedError
-
-    def get_status(self):
-        # TODO retornar status de impress�o com string de interpreta��o.
-        self.send_command(self.CMD_PRINTER_STATUS)
+        self.send_command('19', '%02d' % cod)
+        value = '%012d' % (value * Decimal('1e2'))
+        self.send_command('07', '%02d' % cod, value)
+        self.send_command('12', 'NN2%s' % text)
+   
+    def calc_checksum(self, data):
+        retval = operator.imod(sum([ord(x) for x in data]), 256)
+        return chr(retval)
+    
+    def readline_stx(self):
+        # Inicio
+        str_ = ''
+        buffer = ''
+        ler_buffer = 1
+        aguardando_checksum = False
+        receber_registro = False
+        res = ()
+        porta = self._port
+        while ler_buffer:
+            c = porta.readline(1)
+            if c: # H� byte lido na porta de comunica��o?
+                if aguardando_checksum: # Aguardando o byte de verifica��o (checksum)
+                    if self.calc_checksum(buffer) == c: # O byte corresponde ao checksum esperado?
+                        porta.write(chr(6)) # Transmitir o caracter ACK (6). Efetivar o registro no buffer
+                        res += str_,
+                        loop = False
+                    else: # Descartar os Dados do registro e transmitir o caracter NAK (21)
+                        porta.write(chr(21))
+                        buffer = ''
+                        str_ = ''
+                    aguardando_checksum = False
+                else:
+                    if c == chr(2): # Trata-se do caracter STX (2)
+                        receber_registro = True
+                if receber_registro: # H� registro em recep��o?
+                    buffer += c
+                    if c == chr(0):
+                        str_ += ' '
+                    else:
+                        str_ += c
+                    if c == chr(3): # Trata-se do caracter ETX (3)?
+                        aguardando_checksum = True
+                        receber_registro = False
+                else:
+                    if c == chr(6):
+                        res += str_,
+                        ler_buffer = False
+                    elif c == chr(21):
+                        porta.write(chr(21))
+                        aguardando_checksum = False
+                        str_ = ''
+                        buffer = ''
+                        res = ()
+                        ler_buffer = False
+                    
+            else:
+                ler_buffer = 0
+        return res
+    def clear_ecf_buffer(self):
+        self._port.flushOutput()
+        return 1
+        size = 0
+        c = '0'
+        while c:
+            c = self._port.readline(1)
+            size += 1
+        return size    
+
+    def write_stx(self, data):
+        """ Escrever na porta serial """
+        print 'Limpar Buffer %d' % self.clear_ecf_buffer() # Limpar o buffer
+        self.write(data) # Escreve na porta
+        ret_ecf = self._port.readline(1)
+        if ret_ecf == chr(6):
+            reply = self.readline_stx()
+        elif ret_ecf == chr(21):
+            reply = ret_ecf
+        else:
+            print 'Limpar Buffer ERRO %d' % self.clear_ecf_buffer() # Limpar o buffer
+            raise Exception("Write data error")
+        print 'Limpar Buffer FIM %d' % self.clear_ecf_buffer() # Limpar o buffer
+        return reply
+    
+    def _decompress_data(self, string):
+        """ Descomprimi os registros enviados pela impressora 
+        
+        Formato da compress�o: c ESC n
+              onde:
+                  c     C�digo asASCII do caracter comprimido
+                  ESC   caracter ESC (27), indicador de compress�o
+                  n     valor entre 34 e 255 que indica, respectivamente, 3 a 244
+                        repeti��es do caracter c
+        """
+        ret_str = ''
+        ignore = False
+        for x in range(len(string)):
+
+            c = string[x]
+            
+            if ignore:
+                ignore = False
+                print 'ignore', c
+                continue
+            
+            elif ord(c) == 27: 
+                n = ord(string[x +1])
+                n = n - 34 + 3
+                c = string[x -1] * n
+                ignore = True
+         
+                
+            ret_str += c
+        return ret_str         
+    def send_command_stx(self, cmd, *params):
+        cmd = chr(2) + cmd + '|'.join(params) + chr(3)
+        cmd += self.calc_checksum(cmd)
+        
+        self.CMD_PREFIX = chr(2)
+        self.CMD_SUFFIX = self.EOL_DELIMIT = chr(3)
+        print '>>>', cmd
+        reply = self.write_stx(cmd)
+        
+#        print reply
+#        read1 = self.read(1)
+#        if not read1 == chr(6):
+#            raise DriverError('Comando não executado!')
+#         
+#        reply = self.readline()
+        self.CMD_PREFIX = "."
+        self.CMD_SUFFIX = self.EOL_DELIMIT = '}'
+#        print '>>>', cmd
+#        print 'REPLY', reply
+#        if reply[4] not in ['+']:
+#            print 'err', self.readline()
+#            raise DriverError('Comando não executado! ERR[%s]' % reply[5:9])
+        return reply
+        
+        
+    def till_read_memory(self, start, end, full=False, serial=False):
+        full = 'S' if not full else 'C'
+        serial = 'TXT|CP860' if serial else ''
+        start = start.strftime('%d%m%Y')
+        end = end.strftime('%d%m%Y')
+        return self.send_command_stx('*18|', start, end, full, serial)
+                
+    def till_read_memory_by_reductions(self, start, end, full=False, serial=False):
+        full = 'S' if not full else 'C'
+        start = '%04d' % start
+        end = '%04d' % end
+        serial = 'TXT|CP860' if serial else ''
+        return self.send_command_stx('*17|', start, end, full, serial)
+
+    def till_read_memory_by_reductions_to_serial(self, start, end, full):   
+        reply = self.till_read_memory_by_reductions(start, end, full, True)
+        retval = ''
+        for x in reply:
+            if x[4] == '>':
+                retval += unicode('%s\n' % self._decompress_data(x[6:-1]), 'cp860)')
+        return retval
+
+    def till_read_memory_to_serial(self, start, end, full):
+        reply = self.till_read_memory(start, end, full, True)
+        retval = ''
+        for x in reply:
+            if x[4] == '>':
+                retval += unicode('%s\n' % self._decompress_data(x[6:-1]), 'cp860)')
+        return retval
+    
+    def _get_status(self):
+        """retornar status de impress�o com string de interpreta��o."""
+        return self.send_command(self.CMD_PRINTER_STATUS)
 
     def get_capabilities(self):
-        return dict(item_code=Capability(max_len=13),
+        return dict(item_code=Capability(max_len=14),
                     item_id=Capability(digits=3),
                     items_quantity=Capability(digits=4, decimals=3),
                     item_price=Capability(digits=6, decimals=2),
-                    item_description=Capability(max_len=64),
+                    item_description=Capability(max_len=200),
                     payment_value=Capability(digits=10, decimals=2),
                     promotional_message=Capability(max_len=492),
                     payment_description=Capability(max_len=80),
@@ -653,22 +987,648 @@
 
     def discount_coupon(self, value):
         # We are using a only a discount by value for API compatibility
-        percentage = '0' * self.PERCENTAGE_CHAR_LEN
-        value = '%012d' % int(float(value) * 1e2)
-
+        percentage = '0000'
+        value = '%012d' % int(value * Decimal('1e2'))
         # This will print a total right after add the discount
         print_total = 'S'
 
         self.send_command(self.CMD_COUPON_ADD_DISCOUNT, percentage, value,
                           print_total)
 
-    def discount_item(self, value):
+    def desc_acres_item(self, desc_acres, item, valor):
+        valor = Decimal(valor)
+        if desc_acres == 'D':
+            res = self.discount_item(valor, item)
+        else:
+            res = self.surcharge_item(valor, item)
+        return res
+
+    def discount_item(self, value, item=False):
         # We are using a only a discount by value for API compatibility
-        percentage = '0' * self.PERCENTAGE_CHAR_LEN
-        value = self._format_price(value, 'value')
+        percentage = '0000'
+        value = '%012d' % (Decimal(value).quantize(TWOPLACES) * Decimal('1e2'))
+        parametros = percentage + value
+        if item:
+            parametros += '%03d' % item
+        self.send_command(self.CMD_ITEM_ADD_DISCOUNT, parametros)
 
-        self.send_command(self.CMD_ITEM_ADD_DISCOUNT, percentage, value)
+    def surcharge_item(self, value, item=False):
+        # We are using a only a discount by value for API compatibility
+        percentage = '0000' # * self.PERCENTAGE_CHAR_LEN
+        value = '%012d' % (value * Decimal('1e2'))
+        parametros = percentage + value
+        if item:
+            parametros += '%03d' % item
+        self.send_command(self.CMD_ITEM_ADD_SURCHARGE, parametros)
 
     def print_printer_parameters(self):
         self.send_command(self.CMD_PRINT_PARAMETERS)
 
+    #### CHRISTIAN #############################################################
+    def get_data_movimento(self):
+        pass
+    def _get_totalizers_g(self):
+        return self._get_totalizers_h()
+        reply = self._get_totalizers('G')
+        print '--', reply
+
+        datas = ((5, 9, 'COO_I'), (9, 13, 'COO'), (13, 17, 'CRO'), 
+                 (17, 21, 'CRO_I'), (21, 25, 'CONT_X'))
+
+        if reply:
+            ret_val = {}
+            for d in datas:
+                idx_ini = d[0] 
+                idx_end = d[1]  
+                data_name = d[2]
+                
+                ret_val[data_name] = reply[idx_ini:idx_end]
+            return ret_val
+        return None
+
+    
+    def _get_totalizers_h(self):
+        reply = self._get_totalizers('H')
+        reply = reply[2:]
+
+        datas = ((5, 26, 'SERIAL'), (26, 38, 'SB'), (38, 42, 'CRO'), 
+                 (42, 46, 'CRZ'), (46, 52, 'GNF'), (52, 58, 'GRG'), 
+                 (58, 64, 'CCF'), (64, 70, 'CFD'), (70, 76, 'COO'),
+                 (76, 80, 'CDC'), (80, 84, 'NCN'), (84, 88, 'NFC'),
+                 (88, 92, 'CFC'), (92, 110, 'GT'), (110, 124, 'VB'))
+
+        if reply:
+            ret_val = {}
+            for d in datas:
+                idx_ini = d[0] 
+                idx_end = d[1]  
+                data_name = d[2]
+                
+                ret_val[data_name] = reply[idx_ini:idx_end]
+            return ret_val
+        return None
+    def _get_coupon_subtotal(self):
+        rv = self.get_transaction_status()
+        if rv is not None:
+            return Decimal(rv['LIQ']) / Decimal("1e2")
+
+    def get_counters(self):
+        retval = self._get_totalizers_h()
+        ret = {
+            'cro': int(retval['CRO']),
+            'crz': int(retval['CRZ']),
+            'gnf': int(retval['GNF']),
+            'grg': int(retval['GRG']),
+            'ccf': int(retval['CCF']),
+            'cfd': int(retval['CFD']),
+            'coo': int(retval['COO']),
+            'cdc': int(retval['CDC']),
+            'ncn': int(retval['NCN']),
+            'nfc': int(retval['NFC']),
+            'cfc': int(retval['CFC'])
+        }
+        return ret
+    #****************************
+        
+    def get_coo(self):
+        retval = self._get_totalizers_h()
+        return int(retval['COO'])
+
+    def get_cro(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CRO'])
+    
+    def get_crz(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CRZ'])
+    
+    def get_gnf(self):
+        retval = self._get_totalizers_h()
+        return int(retval['GNF'])
+    
+    def get_grg(self):
+        retval = self._get_totalizers_h()
+        return int(retval['GRG'])
+    
+    def get_ccf(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CCF'])
+    
+    def get_cfd(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CFD'])
+    
+    def get_cdc(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CDC'])
+    
+    def get_ncn(self):
+        retval = self._get_totalizers_h()
+        return int(retval['NCN'])
+    
+    def get_nfc(self):
+        retval = self._get_totalizers_h()
+        return int(retval['GNF'])
+
+    def get_cfc(self):
+        retval = self._get_totalizers_h()
+        return int(retval['CFC'])
+   
+    def get_serial(self):
+        retval = self._get_totalizers_h()
+        return retval['SERIAL'][:-1]
+
+    
+    def get_grande_total(self):
+        retval = self._get_totalizers_h()
+        return Decimal(int(retval['GT'])) / Decimal("1e2")
+
+    def get_venda_bruta(self):
+        retval = self._get_totalizers_h()
+        return Decimal(int(retval['VB'])) / Decimal("1e2")
+
+    
+    def get_ecf_date(self):
+        retval = self.get_transaction_status()
+        
+        data = retval['DATA']
+        horas = retval['HORA']
+        
+        dia = data[:2]
+        mes = data[2:4]
+        ano = data [4:]
+        hora = horas[:2]
+        minutos = horas[2:]
+        retval = datetime.datetime(2000 + int(ano), int(mes), int(dia), int(hora), int(minutos), 0)
+        return retval
+
+    def get_status(self, val=None, cmd_status=None):
+            status = self._get_status()
+
+            sinal = status[1]
+            st1 = int(status[3])
+            st2 = int(status[4])
+            st3 = int(status[5])
+
+            trans = self.get_transaction_status()
+            class Status(object):
+                def __init__(self, parent=None):
+                    self.status = status
+                    self.parent = parent
+                @property
+                def open(self):
+                    return trans['STATUS'] == 'P'
+                @property
+                def pagamento_existe(self):
+                    total_pago = ((Decimal(trans['LIQ'])
+                                   if trans['LIQ'] else 0 / Decimal("1e2") - Decimal(trans['FALTA'])
+                    if trans['FALTA'] else 0 / Decimal("1e2")))
+                    return total_pago > 0 and self.open
+                @property
+                def out_paper(self):
+                    return sinal == '-' and st3 == 5
+                @property
+                def block_z(self):
+                    return trans['REDUCAO'] == 'F'
+    
+                @property
+                def off_line(self):
+                    return sinal == '-' and st3 == 1
+
+                def check_error(self):
+                    if self.block_z:
+                        raise PendingReduceZ('Bloqueio por Reduçao Z')
+                    if self.open:
+                        raise CouponOpenError('Cupom aberto')
+                    if st1 and st2 and st3 == 5:
+                        raise OutofPaperError('Impressora sem papel.')
+
+                def check_error_two(self):
+                    if st1 != 0:
+                        self.check_error()
+
+            return Status(self)
+
+    def _apura_totais(self, **kwargs):
+        """
+        Coment�rios:
+        Este comando captura informa��es do equipamento e soma dos
+        totais armazenados na Mem�ria Fiscal, conforme per�odo selecionado, 
+        aceito nos modelos IF ST200 e IF ST120.
+        
+        As seguintes informa��es s�o recebidas:
+        Modelo, n�mero de fabrica��o, tipo do ECF, n�mero de ordem do ECF,
+        n�mero de ordem do usu�rio, CNPJ, Inscri��o Estadual, s�mbolo da
+        moeda, n�mero de s�rie da MFD, Grande Total.
+        """
+        crz_i = crz_f = space = ''
+        if kwargs.get('crz_i'):
+            if not kwargs.get('crz_f'):
+                raise DriverError('Parâmetros do comando inválidos: CRZ final requerido.')
+            crz_i = '%04d' % kwargs.get('crz_i')
+            crz_f = '%04d' % kwargs.get('crz_f')
+            space = '      '
+
+        reply = self.send_command('59', crz_i, space, crz_f, space)
+        ret = reply[4:]
+
+        while True:
+            reply = self.send_command('++}')
+            if reply[:2] == '.]':
+                break
+            ret += reply[6:]
+        try:
+            data_mov_rz_str = ret[171:191].split('/') # data do movimento do ultimo Z
+            data_fiscal_str = ret[198:208].split('/')
+            if data_fiscal_str:
+                data_fiscal = datetime.date(int(data_fiscal_str[2]),
+                                            int(data_fiscal_str[1]),
+                                            int(data_fiscal_str[0]))
+            else:
+                data_fiscal = False
+            if data_mov_rz_str:
+                data_mov_z = datetime.date(int(data_fiscal_str[2]),
+                                           int(data_fiscal_str[1]),
+                                           int(data_fiscal_str[0]))
+        except Exception as e:
+            raise DriverError('Erro ao apurar totais: %s' % e.message)
+        ret_dict = {'DATA_MOV_Z': data_mov_z,
+                    'DATA_FISCAL': data_fiscal,
+                    'CRZ': int(ret[167:167+4]),
+                    'COO_INI': int(ret[209:215]),
+                    'CRO_INI': int(ret[215:215+6]),
+                    'VBD': Decimal(ret[389:389+18]) / Decimal('100')}
+        return ret_dict
+
+    def get_tax(self):
+        return self._get_taxes()
+
+    def get_totalizadores(self, total=False):
+        retval = []
+
+        ret_cmd = self.send_command('27', '1' )
+        if ret_cmd[:3] == '.+C':
+            retval.append(
+                ('CANC', Decimal(ret_cmd[76:88]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('DESC', Decimal(ret_cmd[92:104]) / Decimal('1e2'), 'ICMS')
+            )
+
+
+        ret_cmd = self.send_command('27', '3' )
+        if ret_cmd[:3] == '.+C':
+            retval.append(
+                ('I', Decimal(ret_cmd[21:33]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('N', Decimal(ret_cmd[33:45]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('F', Decimal(ret_cmd[45:57]) / Decimal('1e2'), 'ICMS')
+            )
+            it = ret_cmd[93:94] # Tipo Tributa��o, T=ICMS
+            ii = ret_cmd[94:96] # Indice da tributa��o
+            iv = ret_cmd[96:108] # Valor da tributa��o
+            if it == 'T':
+                tipo_t = 'ICMS'
+            else:
+                tipo_t = 'ISS'
+            retval.append(
+                            (ii, Decimal(iv) / Decimal('1e2'), tipo_t)
+                        )
+        ret_cmd = ''
+
+        ret_cmd_4 = self.send_command('27', '4' )
+        if ret_cmd_4[:3] == '.+C':
+            ret_cmd += ret_cmd_4[7:]
+
+        ret_cmd_5 = self.send_command('27', '5' )
+        if ret_cmd_5[:3] == '.+C':
+            ret_cmd += ret_cmd_5[7:]
+
+        ret_cmd_f = self.send_command('27', 'I' )
+        if ret_cmd_f[:3] == '.+C':
+            ret_cmd += ret_cmd_f[7:]
+        print '*' * 50
+        print ret_cmd_f
+        print '*' * 50
+
+        if ret_cmd:
+            tipo = {}
+            i = 0
+            for r in range(14):
+                reg = ret_cmd[i:i+15]
+                i += 15
+                if reg[:3] == '   ':
+                    continue
+
+                if reg[:1] == 'T':
+                    tipo_t = 'ICMS'
+                else:
+                    tipo_t = 'ISS'
+
+                retval.append((reg[1:3],  Decimal(reg[3:]) / Decimal('1e2'), tipo_t))
+
+        return retval
+
+    def _get_taxes(self):
+        
+        retval = []
+        
+        ret_cmd = self.send_command('27', '1' )
+        if ret_cmd[:3] == '.+C':
+            retval.append(
+                ('CANC', Decimal(ret_cmd[76:88]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('DESC', Decimal(ret_cmd[92:104]) / Decimal('1e2'), 'ICMS')
+            )
+            
+        
+        ret_cmd = self.send_command('27', '3' )
+        if ret_cmd[:3] == '.+C':
+            retval.append(
+                ('I', Decimal(ret_cmd[21:33]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('N', Decimal(ret_cmd[33:45]) / Decimal('1e2'), 'ICMS')
+            )
+            retval.append(
+                ('F', Decimal(ret_cmd[45:57]) / Decimal('1e2'), 'ICMS')
+            )
+        ret_cmd = ''    
+
+        ret_cmd_4 = self.send_command('27', '4' )
+        if ret_cmd_4[:3] == '.+C':
+            ret_cmd += ret_cmd_4[7:] 
+        
+        ret_cmd_5 = self.send_command('27', '5' )
+        if ret_cmd_5[:3] == '.+C':
+            ret_cmd += ret_cmd_5[7:]
+
+        ret_cmd_f = self.send_command('27', 'I' )
+        if ret_cmd_f[:3] == '.+C':
+            ret_cmd += ret_cmd_f[7:]
+        print '*' * 50
+        print ret_cmd_f
+        print '*' * 50
+
+        if ret_cmd:
+            tipo = {}
+            i = 0
+            for r in range(14):
+                reg = ret_cmd[i:i+15]
+                i += 15    
+                if reg[:3] == '   ':
+                    continue
+                try:
+                    tipo[reg[:3]] = Decimal(reg[3:]) / Decimal('1e2')
+                except:
+                    tipo[reg[:3]] = Decimal('000000000000') / Decimal('1e2')
+                    pass
+            
+            ret_cmd_29 = ''            
+            ret_cmd_29_3 = self.send_command('29', '3' )
+            if ret_cmd_29_3[:3] == '.+T':
+                ret_cmd_29 += ret_cmd_29_3[48:48+28]
+
+            ret_cmd_29_4 = self.send_command('29', '4' )
+            if ret_cmd_29_4[:3] == '.+T':
+                ret_cmd_29 += ret_cmd_29_4[7:]
+
+            ret_cmd_29_5 = self.send_command('29', '5' )
+            if ret_cmd_29_5[:3] == '.+T':
+                ret_cmd_29 += ret_cmd_29_5[7:28]
+
+            i = 0
+            if ret_cmd_29:
+                for r in range(14):
+                    reg = ret_cmd_29[i:i+7]
+                    i += 7
+                    it = reg[0] # Tipo Tributa��o, T=ICMS
+                    ii = reg[1:3] # Indice da tributa��o
+                    iv = reg[3:] # Valor da tributa��o
+       
+                    if it == ' ':
+                        continue
+                    getval = tipo.get(it+ii, False)
+                    if getval:
+                        if it == 'T':
+                            tipo_t = 'ICMS'
+                        else:
+                            tipo_t = 'ISS'
+                        retval.append(
+                            (iv, getval, tipo_t)
+                        )
+        return retval
+        
+    def _get_aliquotas(self, tipo='ICMS'):
+        AliquotasStr = '';
+        Posicao      = 1;
+    
+        RetCmd = self.send_command('29', '3' )
+        if RetCmd[:3] == '.+T':
+            AliquotasStr += '%s|' % RetCmd
+        
+        RetCmd = self.send_command('29', '4' )
+        if RetCmd[:3] == '.+T':
+            AliquotasStr += '%s|' % RetCmd
+
+        RetCmd = self.send_command('29', '5' )
+        if RetCmd[:3] == '.+T':
+            AliquotasStr += RetCmd[7:] 
+  
+        return AliquotasStr
+     
+    def get_sintegra(self):
+        regs = self._get_totalizers_h()
+        if regs:
+            data = Settable(
+                opening_date=self._apura_totais()['DATA_FISCAL'],
+                serial=regs['SERIAL'],
+                serial_id=0,
+                coupon_start=int(self._apura_totais()['COO_INI']),
+                coupon_end=int(regs['COO']),
+                cro=int(regs['CRO']),
+                crz=int(regs['CRZ']),
+                coo=int(regs['COO']),
+                ccf=int(regs['CCF']),
+                gnf=int(regs['GNF']),
+                grg=int(regs['GRG']),
+                cdc=int(regs['CDC']),
+                period_total=self.get_venda_bruta(),
+                total=self.get_grande_total(),
+                taxes=self._get_taxes()
+            )
+            return data
+        else:
+            return None
+    def get_gerencial_reports(self):
+        retval = {}
+        reply = self.send_command('29', 'M')
+
+        i = 7
+        for x in range(8):
+            s = reply[i:i+15]
+            i += 15
+            if s == ' ' * 15:
+                continue
+            retval[x +1] = s
+        return retval
+        
+    def gerencial_report_open(self, id=0):    
+        reports = self.get_gerencial_reports()
+        if not reports.get(id, False):
+            raise Exception('Relatorio gerencial não cadastrado') 
+        self.send_command('13', 'S', reports[id])
+        
+    def gerencial_report_print(self, text):
+        for line in text.split('\n'):
+            self.send_command('08', '0', line)
+
+    def gerencial_report_close(self):
+        self.send_command('08', '}')
+
+    def open_cash_drawer(self, sec=0):
+        return self.send_command('21', '}')
+
+    def get_status_cash_drawer(self):
+        """ Status da gaveta:
+        .+G001 - Fechada
+        .+G000 - Aberta"""
+        retcmd = self.send_command('22', '}')
+        if not retcmd[1] == '+':
+            raise Exception('Não foi possível obter o status da gaveta.')
+        return retcmd[5] == '1'
+
+    def payment_receipt_open(self, identifier, coo, method, value):
+        #ESC . 1 9 TÍT(2) [COO(4) TIPO(2) IDENT(20) PARC(2)] }
+        coo = '%06d' % coo
+        self.send_command('19', '00', coo[2:], method)
+    
+    def payment_receipt_print(self, text):
+        # nesta funcao e na funcao 'gerencial_report_print'
+        # todo: Verificar o maximo de carcters que pode ser enviado para escrita e pergar o comando para cortar papel
+        for line in text.split('\n'):
+            self.send_command('08', '0', line)
+            
+    def payment_receipt_close(self):
+        self.send_command('08', '}')
+
+    def set_summertime(self, value):
+        p = 'S' if value else 'N'
+        return self.send_command('36', p)
+        
+
+    def get_summertime(self):
+        ret_cmd = self.get_transaction_status()
+        ret_val = False if ret_cmd['VERAO'] == 'N' else True
+        return ret_val
+
+    def get_data_mov(self):
+        retval = self._apura_totais()['DATA_FISCAL']
+        return retval
+
+    def get_date_sb(self):
+        reply = self.send_command('29', 'H')
+        retval = datetime.datetime.strptime(reply[70:80], "%d%m%y%H%M")
+        return retval
+
+    def get_date_last_mov_rz(self):
+        '''
+        :return: Retorna a data do movimento da ultima reducao Z emitida
+        '''
+        data = 0
+        try:
+            ultimo_z = self.get_last_reduce()
+            if ultimo_z:
+                data = ultimo_z.get('DATA_MOV_Z') # data do movimento do ultimo Z emitido
+
+        except Exception as e:
+            log.error('get_date_last_mov_rz: %s' % str(e))
+
+        return data
+
+    def get_last_reduce(self):
+        crz = int(self._get_totalizers_h()['CRZ']) # Obter o CRZ
+        retcmd = self._apura_totais(crz_i=crz, crz_f=crz)
+        return {
+            'DATA_MOV_Z': retcmd['DATA_MOV_Z'],
+            'VBD': retcmd['VBD'],
+            'CRO': retcmd['CRO_INI'],
+            'CRZ': retcmd['CRZ'],
+        }
+
+    def identifier_pafecf(self, text):
+        retval = self.send_command(self.CMD_IDENTIFIER_PAFECF, text[:120])
+        return retval
+
+    def get_coo_inicial_final(self):
+        #Implementar a funcção para sweda
+        coo_inicial = '0'
+        return coo_inicial
+    def get_read_ie(self):
+        #Implementar a função para sweda
+        return 'isento'
+
+    def get_emitir_mfd(self, relatorio, inicio, fim, dir_arq, ecf, tipo='1', dir_bin=''):
+        """
+        _______Relatório Espelho MFD_______
+        @param tipo: Quando relatório igual Espelho MFD, tipo aceita os valores 0,1,2
+        0 = Total
+        1 = Por data
+        2 =Por COO
+
+        @param inicio: STRING  inicial no formato  data ddmmaa, dd/mm/aa, ddmmaaaa ou dd/mm/aaaa ou o  CRZ/COO inicial.
+
+        @param fim: STRING data fim no formato  ddmmaa, dd/mm/aa, ddmmaaaa ou dd/mm/aaaa ou o  CRZ/COO inicial.
+
+        @param dir_arq: STRING  informando a localização e o nome do arquivo texto a  ser  gerado.
+
+        @param ecf: STRING  informando a código da ecf.
+
+        ________Relatório Arquivo MFD ______
+        Tipo:STRING que determina o tipo de relatório a ser gerado:Informar um dos seguintes valores ("0" a "3"):
+        "0"=L M F simplificada – formato texto.
+        "1"=L M F  COMPLETA    – formato texto.
+        "2"=L M F  COMPLETA    – formato COTEPE.
+         PatBin = caminho e nome do arquivo de Memória Fiscal no formato binário.
+
+        "2"=M F D – formato COTEPE.Processa o movimento gravado apenas no dispositivo Memória de Fita Detalhe - MFD.
+        Para que seja lido apenas a MFD, informar "" ou vazio no argumento abaixo - PatBin.  Assim, NÃO irá processar o movimento do dispositivo da Memória Fiscal.
+        A DLL vai procurar o arquivo binário MFD no computador automaticamente.
+
+        "3"=LMF  e  MFD – formato COTEPE.Também conhecido como "TDM" porque inclui movimento da Memória Fiscal e da MFD.
+        No argumento abaixo - PatBin -  deverá ser informado o  caminho e o nome do  arquivo  que contém a Memória Fiscal em formato binário.
+        Já os arquivos que contém a memória MFD não deverão ser informados. A DLL vai procurar os arquivos binários tipo  MFD  no computador automaticamente.
+
+        "4"=Gera o arquivo com a mesma informação do tipo "3". mas com as seguintes diferenças:
+·        A função não consulta o ECF sobre o conteúdo da MFD ·      Não exibe a caixa de geração do arquivo COTEPE. ·
+         Poderá usar a pasta informada  no argumento "PatBin" abaixo para obter os arquivos  MFD e gerar o arquivo cotepe na mesma pasta e na pasta informada em "PatTxt".
+         Os arquivos não precisam pertencer ao ECF conectado.
+
+
+        @param inicio: STRING com a data inicial no formato  ddmmaa, dd/mm/aa, ddmmaaaa ou dd/mm/aaaa ou o  CRZ inicial.
+        Será assumido que o valor refere-se ao contador COO se o comprimento do valor tiver 7 dígitos.
+
+        @param fim: STRING com a data fim no formato  ddmmaa, dd/mm/aa, ddmmaaaa ou dd/mm/aaaa ou o  CRZ final.
+        Será assumido que o valor refere-se ao contador COO se o comprimento do valor tiver 7 dígitos.
+
+        @param dir_arq: STRING  informando a localização e o nome do arquivo texto a  ser  gerado.
+
+        @param dir_bin: STRING  informando a localização e o nome do arquivo binário da Memória Fiscal. Usado para gerar o movimento da Memória Fiscal ou TDM. Este arquivo
+        é gerado, chamando a função  ECF_DownloadMF.
+
+
+        """
+        try:
+            libSweda =  CDLL('%s/libs/libconvecf.so' %root_path)
+            if relatorio == 'Espelho MFD':
+                status_mfd = libSweda.ECF_FormatoDadosMFD("",dir_arq, "0", tipo, inicio, fim, ecf)
+            elif relatorio == 'Arquivo MFD':
+                status_mfd = libSweda.ECF_ReproduzirMemoriaFiscalMFD(tipo,inicio,fim,dir_arq, dir_bin)
+            return status_mfd
+        except Exception as e:
+            print str(e)
+            return str(e)
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/sweda/IFST200.py trunk/stoqdrivers/printers/sweda/IFST200.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/printers/sweda/IFST200.py	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/printers/sweda/IFST200.py	2016-10-13 16:22:19.420311101 -0300
@@ -0,0 +1,48 @@
+'''
+Created on 09/02/2012
+
+@author: sig
+'''
+import operator
+from serial import Serial
+P = Serial('/dev/ttyS4', timeout=1)
+ACK = chr(6)
+
+def calc_checksum(data):
+    retval = operator.imod(sum([ord(x) for x in data]), 256)
+    return chr(retval)
+
+def envia_comando_ecf(cmd):
+    erromsg = ''
+    mensagem = ''
+    falhastx = 0
+    ack_ecf = 0
+        
+    if cmd[2:4] != '34':
+        print 'limpar cache info34'
+    
+    while ack_ecf != ACK:
+        P.flushInput() # Limpa a porta
+
+        ack_ecf = P.readline(1)
+        
+        
+        
+        
+        
+        
+cmd = chr(2) + '*34' + chr(3) 
+cmd += calc_checksum(cmd)
+print cmd
+#envia_comando_ecf(cmd)
+#P.open()
+P.write(cmd)
+
+s = ''
+while True:
+    r = P.readline(1)
+    if not r:
+        print '>>>', s
+        break
+    else:
+        s += r         
\ Falta o caracter nova linha no final do arquivo
Os arquivos binários versao_6D/stoqdrivers-0.9.11/stoqdrivers/pt_br.mo e trunk/stoqdrivers/pt_br.mo são diferentes
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/pt_br.po trunk/stoqdrivers/pt_br.po
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/pt_br.po	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/pt_br.po	2016-10-13 16:22:19.000318683 -0300
@@ -0,0 +1,40 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-05-26 07:53-0300\n"
+"PO-Revision-Date: 2011-05-26 07:54-0300\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-KeywordsList: _;gettext;gettext_noop\n"
+"X-Poedit-Basepath: .\n"
+"X-Poedit-Language: Portuguese\n"
+"X-Poedit-Country: BRAZIL\n"
+"X-Poedit-SourceCharset: utf-8\n"
+
+#: printers/fiscal.py:172
+msgid "You must totalize the coupon before add payments."
+msgstr "Totalizar Cupom"
+
+#: printers/fiscal.py:204
+msgid "You must totalize the coupon before closing it"
+msgstr ""
+
+#: printers/fiscal.py:207
+msgid "It is not possible close the coupon since there are no payments defined."
+msgstr ""
+
+#: printers/fiscal.py:210
+#, python-format
+msgid "Isn't possible close the coupon since the payments total (%.2f) doesn't match the totalized value (%.2f)."
+msgstr ""
+
+#~ msgid "Printer is out of paper"
+#~ msgstr "Impressora sem papel"
+
+#~ msgid "Printer has a coupon currently open"
+#~ msgstr "Impressora com o cupom aberto"
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/scales/filizola/MF30.py trunk/stoqdrivers/scales/filizola/MF30.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/scales/filizola/MF30.py	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/scales/filizola/MF30.py	2016-10-13 16:22:18.956319476 -0300
@@ -0,0 +1,30 @@
+# -*- Mode: Python; coding: iso-8859-1 -*-
+
+from serial import EIGHTBITS, STOPBITS_ONE, PARITY_NONE
+from zope.interface import implements
+
+from stoqdrivers.exceptions import InvalidReply
+from stoqdrivers.interfaces import IScale
+from stoqdrivers.serialbase import SerialBase
+from decimal import Decimal
+
+class MF30(SerialBase):
+    CMD_PREFIX = "\x05"
+    CMD_SUFFIX = "\x02"
+    EOL_DELIMIT = "\x03"
+
+    implements(IScale)
+
+    model_name = "Filizola MF-30"
+
+    def __init__(self, device, consts=None):
+        SerialBase.__init__(self, device)
+        device.set_options(baudrate=9600, bytesize=EIGHTBITS,
+                           stopbits=STOPBITS_ONE, parity=PARITY_NONE)
+
+    def read_data(self):
+        reply = self.writeline('')
+        if 'I' in reply:
+            raise InvalidReply(u'Erro ao obter o peso da balan�a')
+        return Decimal(reply[2:])
+
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/scales/generic/Generic.py trunk/stoqdrivers/scales/generic/Generic.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/scales/generic/Generic.py	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/scales/generic/Generic.py	2016-10-13 16:22:18.960319405 -0300
@@ -0,0 +1,36 @@
+# -*- Mode: Python; coding: iso-8859-1 -*-
+
+from serial import EIGHTBITS, STOPBITS_ONE, PARITY_NONE
+from zope.interface import implements
+
+from stoqdrivers.exceptions import InvalidReply
+from stoqdrivers.interfaces import IScale
+from stoqdrivers.serialbase import SerialBase
+from decimal import Decimal
+from sigpdv.tools.config import config
+
+class Generic(SerialBase):
+    CMD_PREFIX = chr(config['solicitaleitura'])
+    CMD_SUFFIX = ''
+    CMD_PREFIX_REAL = chr(config['charinicio'])
+    EOL_DELIMIT = chr(config['charfim'])
+
+    implements(IScale)
+
+    model_name = "Generic Scale"
+
+    def __init__(self, device, consts=None):
+        SerialBase.__init__(self, device)
+        device.set_options(baudrate=config['balancabaud'], bytesize=EIGHTBITS,
+                           stopbits=STOPBITS_ONE, parity=PARITY_NONE, read_timeout=0.1)
+
+    def read_data(self):
+        reply = self.writeline('')
+        if 'I' in reply:
+            return Decimal('0')
+        reply = reply.replace(self.CMD_PREFIX_REAL, '')
+
+        if config['modelo_balanca'] == 'Filizola':
+            return Decimal(reply) / Decimal('1e3')
+        return Decimal(reply)
+
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/scales/generic/__init__.py trunk/stoqdrivers/scales/generic/__init__.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/scales/generic/__init__.py	1969-12-31 21:00:00.000000000 -0300
+++ trunk/stoqdrivers/scales/generic/__init__.py	2016-10-13 16:22:18.960319405 -0300
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+__author__ = 'christian.douglas'
+  
\ Falta o caracter nova linha no final do arquivo
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/scales/scales.py trunk/stoqdrivers/scales/scales.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/scales/scales.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/scales/scales.py	2016-10-13 16:22:18.976319116 -0300
@@ -35,7 +35,9 @@
 
 class Scale(BaseScale):
     def read_data(self):
-        return self._driver.read_data()
+        ret = self._driver.read_data()
+        print '\033[93mB: [%s]\033[00;37m' % ret
+        return ret
 
 def test():
     scale = Scale()
diff -urN versao_6D/stoqdrivers-0.9.11/stoqdrivers/serialbase.py trunk/stoqdrivers/serialbase.py
--- versao_6D/stoqdrivers-0.9.11/stoqdrivers/serialbase.py	2016-12-29 14:30:59.000000000 -0200
+++ trunk/stoqdrivers/serialbase.py	2016-10-13 16:22:19.188315288 -0300
@@ -25,39 +25,49 @@
 ##              Henrique Romano  <henrique@async.com.br>
 ##
 
-from kiwi.log import Logger
 from serial import Serial, EIGHTBITS, PARITY_NONE, STOPBITS_ONE
 from zope.interface import implements
 
 from stoqdrivers.interfaces import ISerialPort
+from stoqdrivers.exceptions import CommError
+from sigpdv.tools.log import traced
 
-log = Logger('stoqdrivers.serial')
 
 class VirtualPort:
     implements(ISerialPort)
 
+    @traced
     def getDSR(self):
         return True
 
+    @traced
     def setDTR(self, value):
         pass
 
+    @traced
     def set_options(self, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_NONE,
-                    stopbits=STOPBITS_ONE, read_timeout=3, write_timeout=0):
+                    stopbits=STOPBITS_ONE, read_timeout=1, write_timeout=1):
         pass
 
+    @traced
     def write(self, data):
         pass
 
+    @traced
     def read(self, n_bytes=1):
         return ''
 
+
 class SerialPort(Serial):
     implements(ISerialPort)
 
     def __init__(self, device):
         Serial.__init__(self, device)
-        self.setDTR(True)
+#        self.setDTR(True)
+        try:
+            self.setDTR(True)
+        except:
+            pass
         self.flushInput()
         self.flushOutput()
         self.set_options()
@@ -66,8 +76,9 @@
     # this to work. Maybe we should change this and do all the driver specify
     # its options, but right now I think that's ok, since these options are
     # common to most of the drivers.
+    @traced
     def set_options(self, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_NONE,
-                    stopbits=STOPBITS_ONE, read_timeout=3, write_timeout=0):
+                    stopbits=STOPBITS_ONE, read_timeout=1, write_timeout=1):
         self.setBaudrate(baudrate)
         self.setByteSize(bytesize)
         self.setParity(parity)
@@ -75,6 +86,10 @@
         self.setTimeout(read_timeout)
         self.setWriteTimeout(write_timeout)
 
+    @traced
+    def open(self):
+        return super(SerialPort, self).open()
+
 class SerialBase(object):
 
     # All commands will have this prefixed
@@ -87,31 +102,51 @@
     def __init__(self, port):
         self._port = port
 
+    @traced
     def set_port(self, port):
         self._port = port
 
+    @traced
     def get_port(self):
         return self._port
 
+    @traced
     def fileno(self):
         return self._port.fileno()
 
-    def writeline(self, data):
+    @traced
+    def writeline(self, data, retries=10):
         self.write(self.CMD_PREFIX + data + self.CMD_SUFFIX)
-        return self.readline()
+        read = self.readline(retries=retries)
+        return read
 
+    @traced
     def write(self, data):
-        log.debug(">>> %r (%d bytes)" % (data, len(data)))
-        self._port.write(data)
+        while True: # Limpar buffer antes de enviar comando
+            c = self._port.read(0)
+            if not c:
+                break
+        return self._port.write(data)
 
+    @traced
     def read(self, n_bytes):
         return self._port.read(n_bytes)
 
-    def readline(self):
+    @traced
+    def readline(self, retries=10):
         out = ''
+        a = 0
+        retries = retries
         while True:
+            if a > retries:
+                raise CommError("Timeout")
+
             c = self._port.read(1)
+            if not c:
+                a += 1
+                continue
+            a = 0
+
             if c == self.EOL_DELIMIT:
-                log.debug('<<< %r' % out)
                 return out
-            out += c
+            out += c
\ Falta o caracter nova linha no final do arquivo
