NOTA IMPORTANTE: Este proyecto es una mejora del original https://github.com/reingart/pyfiscalprinter
Realizé un refactor del proyecto original para adaptarlo a una necesidad distinta y moderna, mejorándolo para tal uso.
Fiscalberry es un 3x1, actúa como: protocolo, servidor y driver facilitando al programador la impresión de tickets, facturas o comprobantes fiscales.
- PROCOLO: Siguiendo la estructura del JSON indicado, se podrá imprimir independientemente de la impresora conectada. Fiscalberry se encargará de conectarse y pelear con los códigos y comandos especiales de cada marca/modelo.
- SERVIDOR: gracias al servidor de websockets es posible conectar tu aplicación para que ésta fácilmente pueda enviar JSON's y recibir las respuestas de manera asíncrona.
- DRIVER: Es el encargado de transformar el JSON genérico en un conjunto de comandos especiales según marca y modelo de la impresora. Aquí es donde reutilicé el código del proyecto de Reingart (https://github.com/reingart/pyfiscalprinter) para impresoras Hasar y Epson.
Funciona en cualquier PC con cualquier sistema operativo que soporte python. La idea original fue pensada para que funcione en una raspberry pi, cuyo fin es integrar las fiscales al mundo de la Internet de las Cosas (IOT). Yo tengo funcionando varias fiscales conectadas a una raspberry pi.
Practicamente todos: Javascript, nodejs, python, php, etc.
Los que se puedan actuar como "cliente Web Socket" y conectarse con el servidor para enviar/recibir JSON's.
mas info en la WIKI: https://github.com/ristorantino/fiscalberry/wiki
usando git
git clone https://github.com/ristorantino/fiscalberry.git
o directamente el ZIP: https://github.com/ristorantino/fiscalberry/archive/master.zip
git clone https://github.com/Intelintec/pyutf8.git
cd pyutf8
python setup.py install # con sudo, tal vez.
Renombrar el archivo "config.ini.install" como "config.ini" y configurar la marca, modelo, path y driver de la impresora. (al inicializar el servicio "server.py", si el archivo no existe, lo crea automáticamente)
Las opciones son:
para marca: [Hasar
, Epson
]
modelo:
(para Hasar)
"615"
"715v1"
"715v2"
"320"
(para Epson)
"tickeadoras"
"epsonlx300+"
path: en windows: (COM1, COM2, etc) en linux: (/dev/ttyUSB0, /dev/ttyS0, etc)
driver: (puede quedar vacio, en ese caso si la marca setteada es Hasar, el driver sera Hasar, lo mismo con Epson. Modificar File o Dummy es útil para hacer pruebas o desarrollo) Hasar, Epson, Dummy, File
En el caso de seleccionar File, en la variable "path" hay que colocar el nombre del archivo que deseo crear para que se escriban las salidas. Ej en linux: "/tmp/archivo.txt"
probado bajo python 2.7.6 en Linux, Ubuntu, Raspian
Se necesitan las dependencias:
- serial (para conectarse con impresoras seriales)
- tornado (para usar como servidor de web sockets)
sudo apt-get install python-pip libjpeg-dev
sudo pip install pyserial requests
sudo apt-get install build-essential python-dev
sudo pip install tornado
sudo apt-get install nmap
sudo pip install python-nmap
Si se quiere usar las comanderas hay que instalar
sudo apt-get install python-imaging python-dev python-setuptools
sudo pip install python-escpos
Si te encontrs con el error "socket.gaierror: Name or service not known"
A veces, en Linux, ser necesario poner el nombre del equipo (hostname) en el archivo /etc/hosts, si es que aun no lo tenias. Generalmente el archivo hosts viene solo con la direccion "127.0.0.1 localhost",
para solucionarlo debés ejecutar el comando
hostname
y ver cual es el nombre de la máquina para agregarlo al archivo /etc/hosts 127.0.0.1 nombre-PC localhost
Usa python 2
sudo apt install build-essential libjpeg-dev python-dev python-serial python-setuptools nmap
mkvirtualenv -p python2 fiscalberry
workon fiscalberry
(fiscalberry) pip install -r requirements.txt
En el archivo "fiscalberry-server-rc" deberas abrirlo y modificar la lionea donde dice "DIR=/insertPATHHERE" colocanmdo el path donde se encuentra la carpeta de fiscalberry. Ej: "DIR=/home/pi/fiscalberry"
luego deberas copiar el archivo a /etc/init.d/
sudo update-rc.d fiscalberry-server-rc defaults
para usar impresora de comandas ESCP en raspbian
sudo apt-get install libjpeg-dev
Además será necesario seguir una serie de pasos adicionales detallados en esta libreria utilizada por fiscalberry: https://python-escpos.readthedocs.io/en/latest/user/raspi.html
sudo python server.py
Hay un ejemplo de página web con javascript dentro de la carpeta js_browser_client. Deberás abrir el HTML en un browser y jugar un poco con él. el archivo fiscalberry.js te servirá si queres enviar a imprimir desde el browser.
Supongamos que tenemos este JSON genérico:
{
"ACCION_A_EJECUTAR": {
PARAMETROS_DE_LA_ACCION
...
}
}
Lo enviamos usando websockets a un host y puerto determinado (el servidor fiscalberry), éste lo procesa, envia a imprimir, y responde al cliente con la respuesta de la impresora. Por ejemplo, devolviendo el número del último comprobante impreso.
Otro ejemplo más concreto: queremos imprimir un ticket, esta acción en el protocolo fiscalberry se lo llama como accion "printTicket" y está compuesta de 2 parámetros obligatorios: "encabezado" e "items".
El "encabezado" indica el tipo de comprobante a imprimir (y también podria agregarle datos del cliente, que son opcionales). Los ítems son una lista de productos a imprimir donde, en este ejemplo, tenemos una coca cola, con impuesto de 21%, importe $10, descripción del producto "COCA COLA" y la cantidad vendida es 2.
{
"printTicket": {
"encabezado": {
"tipo_cbte": "T", // tipo tiquet *obligatorio
},
"items": [
{
"alic_iva": 21.0, // impuesto
"importe": 10, // importe
"ds": "COCA COLA", // descripcion producto
"qty": 2.0 // cantidad
}
]
}
}
Protocolo para formar un JSON con el objetivo de imprimir un ticket. Se compone de 3 distintas partes:
- Encabezado
- Ítems
- Pagos (opcional)
Al imprimir un ticket el servidor enviará 3 comandos previos que pueden resultar en un mensaje de warning: "comando no es valido para el estado de la impresora fiscal ". Esto no es un error, sino que antes de imprimir un tiquet envia:
- CANCELAR CUALQUIER TIQUET ABIERTO
- CANCELAR COMPROBANTE NO FISCAL
- CANCELAR NOTA ED CREDITO O DEBITO Es una comprobación útil que ahorrará dolores de cabeza y posibilidades de bloquear la impresora fiscal.
tipo: Json
{
"encabezado": {
"tipo_cbte": "FA", // tipo tiquet VARIABLE ESTATICA *obligatorio
"nro_doc": "20267565393", // identificacion cliente
"domicilio_cliente": "Rua 76 km 34.5 Alagoas", // domicilio
"tipo_doc": "DNI", // tipo documento VARIABLE ESTATICA (mas abajo se detallan)
"nombre_cliente": "Joao Da Silva", // nombre cliente
"tipo_responsable": "RESPONSABLE_INSCRIPTO" // VARIABLE ESTATICA
}
}
ejemplo Nota de Crédito "A"
{
"encabezado": {
"tipo_cbte": "NCB", // tipo tiquet VARIABLE ESTATICA *obligatorio
"referencia": 000100012 // numero de comprobante impreso
}
}
tipo_cbte
"T" # tiques
"FA",
"FB",
"NDA",
"NCA",
"NDB",
"NCB",
"FC",
"NDC",
"NDC",
tipo_responsable
"RESPONSABLE_INSCRIPTO"
"RESPONSABLE_NO_INSCRIPTO"
"NO_RESPONSABLE"
"EXENTO"
"CONSUMIDOR_FINAL"
"RESPONSABLE_MONOTRIBUTO"
"NO_CATEGORIZADO"
"PEQUENIO_CONTRIBUYENTE_EVENTUAL"
"MONOTRIBUTISTA_SOCIAL"
"PEQUENIO_CONTRIBUYENTE_EVENTUAL_SOCIAL"
tipo_doc
"DNI"
"CUIT"
"LIBRETA_ENROLAMIENTO"
"LIBRETA_CIVICA"
"CEDULA"
"PASAPORTE"
"SIN_CALIFICADOR"
tipo: list
{
"items": [
{
"alic_iva": 21.0, // * Obligatorio
"importe": 0.01, // * Obligatorio
"ds": "Descripcion Producto", // * Obligatorio
"qty": 1.0 // * Obligatorio
},
{
"alic_iva": 21.0,
"importe": 0.22,
"ds": "COCA COLA",
"qty": 2.0
}
]
}
Todos los campos del ítem son obligatorios
tipo: list
{
"pagos": [
{
"ds": "efectivo", // * Obligatorio
"importe": 1.0 // * Obligatorio
}
]
}
json = {
"printTicket": {
"encabezado": {
"tipo_cbte": "FA",
"nro_doc": "20267565393",
"domicilio_cliente": "Rua 76 km 34.5 Alagoas",
"tipo_doc": "DNI",
"nombre_cliente": "Joao Da Silva",
"tipo_responsable": "RESPONSABLE_INSCRIPTO"
},
"items": [
{
"alic_iva": 21.0,
"importe": 0.01,
"ds": "PIPI",
"qty": 1.0
},
{
"alic_iva": 21.0,
"importe": 0.22,
"ds": "COCA",
"qty": 2.0
}
],
"pagos": [
{
"ds": "efectivo",
"importe": 1.0
}
]
}
}
json = {
"printTicket": {
"encabezado": {
"tipo_cbte": "T"
},
"items": [{
"alic_iva": 21.0,
"importe": 0.01,
"ds": "PEPSI",
"qty": 1.0
}, {
"alic_iva": 21.0,
"importe": 0.12,
"ds": "COCA",
"qty": 2.0
}]
}
}
Al enviar este JSON se puede configurar el servidor de impresión directamente desde el cliente. El archivo de configuración que será modificado es config.ini que también puede ser modificado a mano desde consola.
Los parámetros son:
Se pueden configurar muchas impresoras. Cada impresora estara como nombre de segmento del archivo config.ini
se deberá indicar un nombre para cada impresora.
NOTA: Tiene que haber al menos una impresora fiscal con el nombre "IMPRESORA_FISCAL"
Las opciones son:
- "Epson"
- "Hasar"
- "Epsond" Para Dummy
- "Hasard" Para Dummy
Epson:
- "tickeadoras"
- "epsonlx300+"
- "tm-220-af"
- "tm-t900fa"
Hasar:
- "615"
- "715v1"
- "715v2"
- "320"
En Windows "COM1"... "COM2", etc.
En linux "/dev/ttyUSB0". Pueden listarse con ./linux_ls_ttyusb.sh
No es requerido para Epson y Hasard
Es la "salida" o sea, es el medio por donde saldrán las impresiones.
Opciones:
- Hasar
- Epson
- Hasard -> Dummy Driver
- Epsond -> Dummy Driver
- Dummy
- File
Por defecto se utiliza el mismo driver que la impresora, pero en algunas casos (desarrollo) se pueden utilizar drivers extra:
- Dummy (no presenta salidas en ningun lado, por lo tanto no usa el campo "path")
- File (para usar este driver es necesario que en el campo "path" se coloque la ruta donde escribir la salida que será un archivo donde imprimirá las respuestas.
// EJ:
{
"configure": {
"printerName": "IMPRESORA_FISCAL",
"marca": "Hasar",
"modelo": "715v2",
"path": "/dev/ttyUSB0"
}
}
{
"configure": {
"printerName": "IMPRESORA_FISCAL",
"marca": "Epson",
"modelo": "tm-220-af",
"path": "/tmp/respuestas.txt",
"driver": "File"
}
}
Abre la gaveta de dinero. No es necesario pasar parámetros extra.
// EJ:
{
"openDrawer": true
}
retorna la configuracon actual del servidor. No es necesario pasar parámetros extra.
// EJ:
{
"getStatus": {}
}
Imprime un cierre fiscal X o Z dependiendo el parámetro enviado
// EJ: Imprime un cierre "Zeta"
{
"dailyClose": "Z"
}
// Ej: imprimiendo un "X"
{
"dailyClose": "X"
}
lista todas las impresoras configuradas en el archivo config.ini
Permite agregar lineas al encabezado
{
"setHeader": [
"Linea 1",
"Linea 22 22",
"Linea 3 3 3 3 3"
]
}
Permite agregar lineas al encabezado
{
"setTrailer": [
"Linea 1",
"Linea 22 22",
"Linea 3 3 3 3 3"
]
}
// cada item de la lista es una linea a modificar, por ejemplo
{
"setTrailer": [
"", // dejara la linea 1 vacia
"Linea 22 modif", // modificara la linea 2
]
}
Devuelve el numero del ultimo comprobante impreso segun tipo de factura como parámetro hay que pasarle una variable estatica "tipo_cbte"
// EJ: ultimo numero de tiquet
{
"getLastNumber": "T"
}
// EJ: ultimo comprobante Factura A
{
"getLastNumber": "FA"
}
// EJ: ultimo comprobante Nota de Credito A
{
"getLastNumber": "NCA"
}
Existen 2 tipos de respuesta y siempre vienen con la forma de un JSON.
Aquellos que son una respuesta a un comando enviado, comienzan con "ret" {"ret": ......}
Aquellos que son un mensaje de la impresora fiscal, vienen con "msg"
{"msg": ......}
// ejemplo retorno de un mensaje cuando no hay papel
{"msg": ["Poco papel para comprobantes o tickets"]}
Deberas enviar JSON válidos al servidor. Recomendamos usar la pagina http://jsonlint.com/ para verificar como tu programa esta generando los JSON.