Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Agregado WSFCRED y modificado pyfepdf para que emita la factura de cr… #74

Open
wants to merge 3 commits into
base: py3k
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nsis.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import os
import sys
from py2exe.build_exe import py2exe

# import py2exe

nsi_base_script = r"""\
; base.nsi
Expand Down
39 changes: 21 additions & 18 deletions pyfepdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,20 +120,23 @@ class FEPDF:
(1, 6, 11, 19, 51): 'Factura',
(2, 7, 12, 20, 52): 'Nota de D�bito',
(3, 8, 13, 21, 53): 'Nota de Cr�dito',
(201, 206, 211): u'Factura de Cr�dito MiPyMEs',
(202, 207, 212): u'Nota de D�bito MiPyMEs',
(203, 208, 213): u'Nota de Cr�dito MiPyMEs',
(4, 9, 15, 54): 'Recibo',
(10, 5): 'Nota de Venta al contado',
(60, 61): 'Cuenta de Venta y L�quido producto',
(63, 64): 'Liquidaci�n',
(91, ): 'Remito',
(39, 40): '???? (R.G. N� 3419)'}

letras_fact = {(1, 2, 3, 4, 5, 39, 60, 63): 'A',
(6, 7, 8, 9, 10, 40, 61, 64): 'B',
(11, 12, 13, 15): 'C',
letras_fact = {(1, 2, 3, 4, 5, 39, 60, 63, 201, 202, 203): 'A',
(6, 7, 8, 9, 10, 40, 61, 64, 206, 207, 208): 'B',
(11, 12, 13, 15, 211, 212, 213): 'C',
(51, 52, 53, 54): 'M',
(19, 20, 21): 'E',
(91, ): 'R',
}
}

def __init__(self):
self.Version = __version__
Expand Down Expand Up @@ -517,13 +520,13 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'):
fact[k] = ds.replace('<br/>', '\n')

# divido las observaciones por linea:
if fact.get('obs_generales') and 'obs' not in f and 'ObservacionesGenerales1' not in f:
if fact.get('obs_generales') and not f.has_key('obs') and not f.has_key('ObservacionesGenerales1'):
obs = "\n<U>Observaciones:</U>\n\n" + fact['obs_generales']
# limpiar texto (campos dbf) y reemplazar saltos de linea:
obs = obs.replace('\x00', '').replace('<br/>', '\n')
for ds in f.split_multicell(obs, 'Item.Descripcion01'):
li_items.append(dict(codigo=None, ds=ds, qty=None, umed=None, precio=None, importe=None))
if fact.get('obs_comerciales') and 'obs_comerciales' not in f and 'ObservacionesComerciales1' not in f:
if fact.get('obs_comerciales') and not f.has_key('obs_comerciales') and not f.has_key('ObservacionesComerciales1'):
obs = "\n<U>Observaciones Comerciales:</U>\n\n" + fact['obs_comerciales']
# limpiar texto (campos dbf) y reemplazar saltos de linea:
obs = obs.replace('\x00', '').replace('<br/>', '\n')
Expand All @@ -534,13 +537,13 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'):
permisos = ['Codigo de Despacho %s - Destino de la mercader�a: %s' % (
p['id_permiso'], self.paises.get(p['dst_merc'], p['dst_merc']))
for p in fact.get('permisos', [])]
#import dbg; dbg.set_trace()
if 'permiso.id1' in f and "permiso.delivery1" in f:

if f.has_key('permiso.id1') and f.has_key("permiso.delivery1"):
for i, p in enumerate(fact.get('permisos', [])):
self.AgregarDato("permiso.id%d" % (i + 1), p['id_permiso'])
pais_dst = self.paises.get(p['dst_merc'], p['dst_merc'])
self.AgregarDato("permiso.delivery%d" % (i + 1), pais_dst)
elif 'permisos' not in f and permisos:
elif not f.has_key('permisos') and permisos:
obs = "\n<U>Permisos de Embarque:</U>\n\n" + '\n'.join(permisos)
for ds in f.split_multicell(obs, 'Item.Descripcion01'):
li_items.append(dict(codigo=None, ds=ds, qty=None, umed=None, precio=None, importe=None))
Expand All @@ -549,7 +552,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'):
# agrego comprobantes asociados
cmps_asoc = ['%s %s %s' % self.fmt_fact(c['cbte_tipo'], c['cbte_punto_vta'], c['cbte_nro'])
for c in fact.get('cbtes_asoc', [])]
if 'cmps_asoc' not in f and cmps_asoc:
if not f.has_key('cmps_asoc') and cmps_asoc:
obs = "\n<U>Comprobantes Asociados:</U>\n\n" + '\n'.join(cmps_asoc)
for ds in f.split_multicell(obs, 'Item.Descripcion01'):
li_items.append(dict(codigo=None, ds=ds, qty=None, umed=None, precio=None, importe=None))
Expand All @@ -572,7 +575,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'):
# mostrar las validaciones no excluyentes de AFIP (observaciones)

if fact.get('motivos_obs') and fact['motivos_obs'] != '00':
if 'motivos_ds.L' not in f:
if not f.has_key('motivos_ds.L'):
motivos_ds = "Irregularidades observadas por AFIP (F136): %s" % fact['motivos_obs']
else:
motivos_ds = "%s" % fact['motivos_obs']
Expand All @@ -583,7 +586,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'):

if letra_fact in ('A', 'M'):
msg_no_iva = "\nEl IVA discriminado no puede computarse como Cr�dito Fiscal (RG2485/08 Art. 30 inc. c)."
if 'leyenda_credito_fiscal' not in f and motivos_ds:
if not f.has_key('leyenda_credito_fiscal') and motivos_ds:
motivos_ds += msg_no_iva

copias = {1: 'Original', 2: 'Duplicado', 3: 'Triplicado'}
Expand Down Expand Up @@ -687,7 +690,7 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'):
if it['codigo'] is not None:
f.set('Item.Codigo%02d' % li, it['codigo'])
if it['umed'] is not None:
if it['umed'] and "Item.Umed_ds01" in f:
if it['umed'] and f.has_key("Item.Umed_ds01"):
# recortar descripci�n:
umed_ds = self.umeds_ds.get(int(it['umed']))
s = f.split_multicell(umed_ds, 'Item.Umed_ds01')
Expand Down Expand Up @@ -829,9 +832,9 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'):

# Datos del pie de factura (obtenidos desde AFIP):
f.set('motivos_ds', motivos_ds)
if 'motivos_ds1' in f and motivos_ds:
if f.has_key('motivos_ds1') and motivos_ds:
if letra_fact in ('A', 'M'):
if 'leyenda_credito_fiscal' in f:
if f.has_key('leyenda_credito_fiscal'):
f.set('leyenda_credito_fiscal', msg_no_iva)
for i, txt in enumerate(f.split_multicell(motivos_ds, 'motivos_ds1')):
f.set('motivos_ds%d' % (i + 1), txt)
Expand Down Expand Up @@ -863,13 +866,13 @@ def ProcesarPlantilla(self, num_copias=3, lineas_max=36, qty_pos='izq'):
f.set('estado', "") # compatibilidad hacia atras

# colocar campos de observaciones (si no van en ds)
if 'observacionesgenerales1' in f and 'obs_generales' in fact:
if f.has_key('observacionesgenerales1') and 'obs_generales' in fact:
for i, txt in enumerate(f.split_multicell(fact['obs_generales'], 'ObservacionesGenerales1')):
f.set('ObservacionesGenerales%d' % (i + 1), txt)
if 'observacionescomerciales1' in f and 'obs_comerciales' in fact:
if f.has_key('observacionescomerciales1') and 'obs_comerciales' in fact:
for i, txt in enumerate(f.split_multicell(fact['obs_comerciales'], 'ObservacionesComerciales1')):
f.set('ObservacionesComerciales%d' % (i + 1), txt)
if 'enletras1' in f and 'en_letras' in fact:
if f.has_key('enletras1') and 'en_letras' in fact:
for i, txt in enumerate(f.split_multicell(fact['en_letras'], 'EnLetras1')):
f.set('EnLetras%d' % (i + 1), txt)

Expand Down
180 changes: 180 additions & 0 deletions pyqr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#!/usr/bin/python
# -*- coding: latin-1 -*-
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by the
# Free Software Foundation; either version 3, 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 MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.

"M?dulo para generar c?digos QR"

__author__ = "Mariano Reingart <reingart@gmail.com>"
__copyright__ = "Copyright (C) 2011 Mariano Reingart"
__license__ = "LGPL 3.0"
__version__ = "1.03b"

import base64
import json
import os
import sys
import tempfile

import qrcode


TEST_QR_DATA = """
eyJ2ZXIiOjEsImZlY2hhIjoiMjAyMC0xMC0xMyIsImN1aXQiOjMwMDAwMDAwMDA3LCJwdG9WdGEiOj
EwLCJ0aXBvQ21wIjoxLCJucm9DbXAiOjk0LCJpbXBvcnRlIjoxMjEwMCwibW9uZWRhIjoiRE9MIiwi
Y3R6Ijo2NSwidGlwb0RvY1JlYyI6ODAsIm5yb0RvY1JlYyI6MjAwMDAwMDAwMDEsInRpcG9Db2RBdX
QiOiJFIiwiY29kQXV0Ijo3MDQxNzA1NDM2NzQ3Nn0=""".replace("\n", "")


class PyQR:
"Interfaz para generar Codigo QR de Factura Electr?nica"
_public_methods_ = ['GenerarImagen', 'CrearArchivo',
]
_public_attrs_ = ['Version', 'Excepcion', 'Traceback', "URL", "Archivo",
'qr_ver', 'box_size', 'border', 'error_correction',
]

_reg_progid_ = "PyQR"
_reg_clsid_ = "{0868A2B6-2DC7-478D-8884-A10E92C588DE}"

URL = "https://www.afip.gob.ar/fe/qr/?p=%s"
Archivo = "qr.png"

# qrencode default parameters:
qr_ver = 1
box_size = 10
border = 4
error_correction = qrcode.constants.ERROR_CORRECT_L

def __init__(self):
self.Version = __version__
self.Exception = self.Traceback = ""

def CrearArchivo(self):
"""Crea un nombre de archivo temporal"""
# para evitar errores de permisos y poder generar varios qr simultaneos
tmp = tempfile.NamedTemporaryFile(prefix="qr_afip_",
suffix=".png",
delete=False)
self.Archivo = tmp.name
return self.Archivo

def GenerarImagen(self, ver=1,
fecha="2020-10-13",
cuit=30000000007,
pto_vta=10, tipo_cmp=1, nro_cmp=94,
importe=12100, moneda="PES", ctz=1.000,
tipo_doc_rec=80, nro_doc_rec=20000000001,
tipo_cod_aut="E", cod_aut=70417054367476,
):
"Generar una im?gen con el c?digo QR"
# basado en: https://www.afip.gob.ar/fe/qr/especificaciones.asp
datos_cmp = {
"ver": int(ver),
"fecha": fecha,
"cuit": int(cuit),
"ptoVta": int(pto_vta),
"tipoCmp": int(tipo_cmp),
"nroCmp": int(nro_cmp),
"importe": float(importe),
"moneda": moneda,
"ctz": float(ctz),
"tipoDocRec": int(tipo_doc_rec),
"nroDocRec": int(nro_doc_rec),
"tipoCodAut": tipo_cod_aut,
"codAut": int(cod_aut),
}

# convertir a representaci�n json y codificar en base64:
datos_cmp_json = json.dumps(datos_cmp)
url = self.URL % (base64.b64encode(datos_cmp_json))

qr = qrcode.QRCode(
version=self.qr_ver,
error_correction=self.error_correction,
box_size=self.box_size,
border=self.border,
)
qr.add_data(url)
qr.make(fit=True)

img = qr.make_image(fill_color="black", back_color="white")

img.save(self.Archivo, "PNG")
return url


if __name__ == '__main__':

if "--register" in sys.argv or "--unregister" in sys.argv:
import win32com.server.register
win32com.server.register.UseCommandLine(PyQR)
elif "/Automate" in sys.argv:
try:
# MS seems to like /automate to run the class factories.
import win32com.server.localserver
win32com.server.localserver.serve([PyQR._reg_clsid_])
except Exception:
raise
else:

pyqr = PyQR()

if '--datos' in sys.argv:
args = sys.argv[sys.argv.index("--datos")+1:]
(ver, fecha, cuit, pto_vta, tipo_cmp, nro_cmp, importe, moneda, ctz,
tipo_doc_rec, nro_doc_rec, tipo_cod_aut, cod_aut) = args
else:
ver = 1
fecha = "2020-10-13"
cuit = 30000000007
pto_vta = 10
tipo_cmp = 1
nro_cmp = 94
importe = 12100
moneda = "DOL"
ctz = 65.000
tipo_doc_rec = 80
nro_doc_rec = 20000000001
tipo_cod_aut = "E"
cod_aut = 70417054367476

if '--archivo' in sys.argv:
pyqr.Archivo = sys.argv[sys.argv.index("--archivo")+1]
else:
pyqr.CrearArchivo()

if '--url' in sys.argv:
pyqr.URL = sys.argv[sys.argv.index("--url")+1]

print("datos:", (ver, fecha, cuit, pto_vta, tipo_cmp, nro_cmp,
importe, moneda, ctz, tipo_doc_rec, nro_doc_rec,
tipo_cod_aut, cod_aut))
print("archivo", pyqr.Archivo)

url = pyqr.GenerarImagen(ver, fecha, cuit, pto_vta, tipo_cmp, nro_cmp,
importe, moneda, ctz, tipo_doc_rec, nro_doc_rec,
tipo_cod_aut, cod_aut)

print("url generada:", url)

if "--prueba" in sys.argv:
qr_data_test = json.loads(base64.b64decode(TEST_QR_DATA))
qr_data_gen = json.loads(base64.b64decode(url[33:]))
assert url.startswith("https://www.afip.gob.ar/fe/qr/?p=")
assert qr_data_test == qr_data_gen, "Diff: %r != %r" % (qr_data_test, qr_data_gen)
print("QR data ok:", qr_data_gen)

if not '--mostrar' in sys.argv:
pass
elif sys.platform=="linux2":
os.system("eog ""%s""" % pyqr.Archivo)
else:
os.startfile(pyqr.archivo)
19 changes: 10 additions & 9 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

__version__ = "%s.%s.%s" % (sys.version_info[0:2] + (rev, ))

HOMO = True
HOMO = False

# build a one-click-installer for windows:
if 'py2exe' in sys.argv:
Expand All @@ -38,31 +38,32 @@
#import pyrece
from . import wsaa
from . import wsfev1, rece1, rg3685
#import wsfexv1, recex1
import wsfexv1, recex1
#import wsbfev1, receb1
#import wsmtx, recem
#import wsct, recet
#import ws_sr_padron
#import pyfepdf
#import pyemail
#import pyi25
import ws_sr_padron
import pyfepdf
import pyemail
import pyi25
#import wsctg
#import wslpg
#import wsltv
#import wslum
#import wslsp
#import wsremcarne
#import wscoc
#import wscdc
import wscdc
#import cot
#import iibb
#import trazamed
#import trazaprodmed
#import trazarenpre
#import trazafito
#import trazavet
#import padron
#import sired
import padron
import sired
import pyqr

data_files = [
(".", ["licencia.txt", ]),
Expand Down
2 changes: 1 addition & 1 deletion tests/wsfev1.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.

from pyafipws import wsfev1
from pyafipws.wsaa import WSAA
from pyafipws.wsfev1 import WSFEv1
"Pruebas para WSFEv1 de AFIP (Factura Electr�nica Mercado Interno sin detalle)"
Expand Down
Loading