def _get_afip(): """Build and authenticate AFIP structure.""" # AFIP init wsaa = WSAA() wsfev1 = WSFEv1() # get access ticket (token y sign) certificate = settings.AFIP['auth_cert_path'] private_key = settings.AFIP['auth_key_path'] if not os.path.exists(certificate): raise ValueError( "Auth certificate can not be found (got {!r})".format(certificate)) if not os.path.exists(private_key): raise ValueError( "Auth key can not be found (got {!r})".format(private_key)) ta = wsaa.Autenticar("wsfe", certificate, private_key, wsdl=settings.AFIP['url_wsaa'], cache=CACHE, debug=True) wsfev1.Cuit = settings.AFIP['cuit'] wsfev1.SetTicketAcceso(ta) wsfev1.Conectar(CACHE, settings.AFIP['url_wsfev1']) return wsfev1
def auth(request): if 'dontusefix' in request.keywords: return z = request.module.__obj__ z.Cuit = CUIT wsaa = WSAA() ta = wsaa.Autenticar(request.module.__service__, CERT, PKEY) z.SetTicketAcceso(ta) z.Conectar(CACHE, request.module.__WSDL__) return z
class _Afip(object): ''' Clase que abstrae la emision de comprobantes electronicos en la afip. ''' def __init__(self, privada, certificado, cuit): # instanciar el componente para factura electrónica mercado interno self.webservice = WSFEv1() self.webservice.LanzarExcepciones = True # datos de conexión (cambiar URL para producción) cache = None wsdl = AFIP_WSDL_URL proxy = "" wrapper = "" cacert = None # conectar try: self.webservice.Conectar(cache, wsdl, proxy, wrapper, cacert) except (ExpatError, ServerNotFoundError, SoapFault): raise AfipErrorRed("Error en la conexion inicial con la AFIP.") self.cert = certificado # archivos a tramitar previamente ante AFIP self.clave = privada self.wsaa_url = AFIP_WSAA_URL self.wsaa = WSAA() self.autenticar() self.webservice.Cuit = cuit def autenticar(self): ''' Pide un ticket de acceso a la AFIP que sera usado en todas las requests. ''' try: self.ticket_autenticacion = self.wsaa.Autenticar( "wsfe", self.cert, self.clave, self.wsaa_url, cache=CACHE_PATH, debug=True) except SoapFault as e: raise AfipErrorRed("Error autenticando en la Afip: " + str(e)) if not self.ticket_autenticacion and self.wsaa.Excepcion: raise AfipError("Error WSAA: %s" % self.wsaa.Excepcion) # establecer credenciales (token y sign): self.webservice.SetTicketAcceso(self.ticket_autenticacion) @requiere_ticket def emitir_comprobante(self, comprobante_cedir, lineas): ''' Toma un comprobante nuestro y una lista de lineas, lo traduce al formato que usa la AFIP en su webservice y trata de emitirlo. En caso de exito, setea las propiedades faltantes del comprobante que son dependientes de la AFIP. En caso de error, levanta una excepcion. ''' nro = self.consultar_proximo_numero(comprobante_cedir.nro_terminal, comprobante_cedir.tipo_comprobante, comprobante_cedir.sub_tipo) fecha = comprobante_cedir.fecha_emision.strftime("%Y%m%d") # En estos tipos de comprobante en especifico, la AFIP te prohibe poner un campo fecha de vencimiento. fecha_vto = None if comprobante_cedir.tipo_comprobante.id in [ID_TIPO_COMPROBANTE_NOTA_DE_DEBITO_ELECTRONICA, ID_TIPO_COMPROBANTE_NOTA_DE_CREDITO_ELECTRONICA] else comprobante_cedir.fecha_vencimiento.strftime("%Y%m%d") imp_iva = sum([l.iva for l in lineas]) if comprobante_cedir.gravado.id == IVA_EXCENTO: imp_neto = Decimal("0.00") imp_op_ex = sum([l.importe_neto for l in lineas]) else: # importe neto gravado (todas las alicuotas) imp_neto = sum([l.importe_neto for l in lineas]) imp_op_ex = Decimal("0.00") # importe total operaciones exentas self.webservice.CrearFactura( concepto=2, # 2 es servicios, lo unico que hace el cedir. tipo_doc=comprobante_cedir.tipo_id_afip, nro_doc=comprobante_cedir.nro_id_afip, tipo_cbte=comprobante_cedir.codigo_afip, punto_vta=comprobante_cedir.nro_terminal, cbt_desde=nro, cbt_hasta=nro, imp_total=Decimal(comprobante_cedir.total_facturado).quantize(Decimal('.01'), ROUND_UP), imp_neto=Decimal(imp_neto).quantize(Decimal('.01'), ROUND_UP), imp_iva=Decimal(imp_iva).quantize(Decimal('.01'), ROUND_UP), imp_op_ex=Decimal(imp_op_ex).quantize(Decimal('.01'), ROUND_UP), fecha_cbte=fecha, # Estas fechas no cambian nunca, pero son requisito de la AFIP para el concepto=2 fecha_serv_desde=fecha, fecha_serv_hasta=fecha, fecha_venc_pago=fecha_vto) # Agregar el IVA if comprobante_cedir.gravado.id == IVA_10_5: self.webservice.AgregarIva( iva_id=4, base_imp=Decimal(imp_neto).quantize(Decimal('.01'), ROUND_UP), importe=Decimal(imp_iva).quantize(Decimal('.01'), ROUND_UP)) elif comprobante_cedir.gravado.id == IVA_21: self.webservice.AgregarIva( iva_id=5, base_imp=Decimal(imp_neto).quantize(Decimal('.01'), ROUND_UP), importe=Decimal(imp_iva).quantize(Decimal('.01'), ROUND_UP)) # Si hay comprobantes asociados, los agregamos. if comprobante_cedir.tipo_comprobante.id in [ ID_TIPO_COMPROBANTE_NOTA_DE_DEBITO, # Para comprobantes no electronicos puede no ser necesario pero se los deja por completitud ID_TIPO_COMPROBANTE_NOTA_DE_CREDITO, ID_TIPO_COMPROBANTE_NOTA_DE_DEBITO_ELECTRONICA, ID_TIPO_COMPROBANTE_NOTA_DE_CREDITO_ELECTRONICA] and comprobante_cedir.factura: comprobante_asociado = comprobante_cedir.factura self.webservice.AgregarCmpAsoc( tipo=comprobante_asociado.codigo_afip, pto_vta=comprobante_asociado.nro_terminal, nro=comprobante_asociado.numero, cuit=self.webservice.Cuit, # Cuit emisor. fecha=comprobante_asociado.fecha_emision.strftime("%Y%m%d") ) # Si es Factura de Credito Electronica, hayq eu agregar como opcional el CBU del Cedir # y el opcional Sistema de Circulacion Abierta if comprobante_cedir.tipo_comprobante.id in [ ID_TIPO_COMPROBANTE_FACTURA_CREDITO_ELECTRONICA]: self.webservice.AgregarOpcional(2101, "0150506102000109564632") self.webservice.AgregarOpcional(27, 'SCA') # Si es Nota de Debito/Credito Electronica, hay que agregar un opcional indicando que no es anulacion. # En principio, el Cedir nunca anula facturas. if comprobante_cedir.tipo_comprobante.id in [ ID_TIPO_COMPROBANTE_NOTA_DE_DEBITO_ELECTRONICA, ID_TIPO_COMPROBANTE_NOTA_DE_CREDITO_ELECTRONICA]: self.webservice.AgregarOpcional(22, "N") # llamar al webservice de AFIP para autorizar la factura y obtener CAE: try: self.webservice.CAESolicitar() except (ExpatError, ServerNotFoundError, SoapFault): raise AfipErrorRed("Error de red emitiendo el comprobante.") if self.webservice.Resultado == "R": # Si la AFIP nos rechaza el comprobante, lanzamos excepcion. raise AfipErrorValidacion(self.webservice.ErrMsg or self.webservice.Obs) if self.webservice.Resultado == "O": # Si hay observaciones (en self.webservice.Obs), deberiamos logearlas en la DB. pass comprobante_cedir.cae = self.webservice.CAE comprobante_cedir.numero = nro if self.webservice.Vencimiento: comprobante_cedir.vencimiento_cae = datetime.strptime(self.webservice.Vencimiento,'%Y%m%d').date() @requiere_ticket def consultar_comprobante(self, comprobante): ''' Consulta que informacion tiene la AFIP sobre un comprobante nuestro. Devuelve un diccionario con todos los datos. ''' codigo_afip_tipo = comprobante.codigo_afip nro_terminal = comprobante.nro_terminal cbte_nro = comprobante.numero self.webservice.CompConsultar(codigo_afip_tipo, nro_terminal, cbte_nro) # Todos estos datos se setean en el objeto afip cuando la llamada a ConsultarComprobante es exitosa. # Notar que si falla (comprobante invalido, conexion...) queda con valores viejos! return { "fecha": self.webservice.FechaCbte, "numero": self.webservice.CbteNro, "punto_venta": self.webservice.PuntoVenta, "vencimiento": self.webservice.Vencimiento, "importe_total": self.webservice.ImpTotal, "CAE": self.webservice.CAE, "emision_tipo": self.webservice.EmisionTipo } @requiere_ticket def consultar_proximo_numero(self, nro_terminal, tipo_comprobante, sub_tipo): codigo_afip = Comprobante(nro_terminal=nro_terminal, tipo_comprobante=tipo_comprobante, sub_tipo=sub_tipo).codigo_afip return int(self.webservice.CompUltimoAutorizado(codigo_afip, nro_terminal) or 0) + 1
def facturar(registros): """Rutina para emitir facturas electrónicas en PDF c/CAE AFIP Argentina""" # inicialización AFIP: wsaa = WSAA() wsfev1 = WSFEv1() # obtener ticket de acceso (token y sign): ta = wsaa.Autenticar( "wsfe", CERT, PRIVATEKEY, wsdl=URL_WSAA, cache=CACHE, debug=True ) wsfev1.Cuit = CUIT wsfev1.SetTicketAcceso(ta) wsfev1.Conectar(CACHE, URL_WSFEv1) # inicialización PDF fepdf = FEPDF() fepdf.CargarFormato("factura.csv") fepdf.FmtCantidad = "0.2" fepdf.FmtPrecio = "0.2" fepdf.CUIT = CUIT for k, v in CONF_PDF.items(): fepdf.AgregarDato(k, v) if "h**o" in URL_WSAA: fepdf.AgregarCampo( "DEMO", "T", 120, 260, 0, 0, text="DEMOSTRACION", size=70, rotate=45, foreground=0x808080, priority=-1, ) fepdf.AgregarDato("motivos_obs", "Ejemplo Sin validez fiscal") # recorrer los registros a facturar, solicitar CAE y generar el PDF: for reg in registros: hoy = datetime.date.today().strftime("%Y%m%d") cbte = Comprobante( tipo_cbte=6, punto_vta=4000, fecha_cbte=hoy, cbte_nro=reg.get("nro"), tipo_doc=96, nro_doc=reg["dni"], nombre_cliente=reg["nombre"], # "Juan Perez" domicilio_cliente=reg["domicilio"], # "Balcarce 50" fecha_serv_desde=reg.get("periodo_desde"), fecha_serv_hasta=reg.get("periodo_hasta"), fecha_venc_pago=reg.get("venc_pago", hoy), ) cbte.agregar_item( ds=reg["descripcion"], qty=reg.get("cantidad", 1), precio=reg.get("precio", 0), tasa_iva=reg.get("tasa_iva", 21.0), ) ok = cbte.autorizar(wsfev1) nro = cbte.encabezado["cbte_nro"] print("Factura autorizada", nro, cbte.encabezado["cae"]) if "h**o" in URL_WSFEv1: cbte.encabezado["motivos_obs"] = "Ejemplo Sin validez fiscal" ok = cbte.generar_pdf(fepdf, "/tmp/factura_{}.pdf".format(nro)) print("PDF generado", ok)
# Desde la versión 2.07 de PyAfipWs se puede usar el método Autenticar() # # https://www.sistemasagiles.com.ar/trac/wiki/ManualPyAfipWs#M%C3%A9todos # ------------------------------------------------------------------------ wsaa_url = 'https://wsaa.afip.gov.ar/ws/services/LoginCms' # solo usar si hay servidor intermedio proxy = "" # httplib2 (default), pycurl (depende proxy) wrapper = "" # autoridades certificantes (servidores) cacert = "pyafipws/conf/afip_ca_info.crt" # Directorio para archivos temporales (dejar en blanco para usar predeterminado) cache = "pyafipws/cache" ta = wsaa.Autenticar(SERVICIO, CERTIFICADO, CLAVEPRIVADA, wsaa_url, proxy, wrapper, cacert, cache) # utilizar las credenciales: #print wsaa.Token #print wsaa.Sign #wsaa.SetTicketAcceso(ta) # conectar al webservice de padrón: padron = WSSrPadronA5() padron.SetTicketAcceso(ta) padron.Cuit = "20267268038" padron.Conectar( wsdl="https://aws.afip.gov.ar/sr-padron/webservices/personaServiceA5?wsdl" )
def generate_invoices(records): """Generate the invoices in PDF using AFIP API resources.""" # AFIP init wsaa = WSAA() wsfev1 = WSFEv1() # get access ticket (token y sign) certificate = settings.AFIP['auth_cert_path'] private_key = settings.AFIP['auth_key_path'] if not os.path.exists(certificate): raise ValueError( "Auth certificate can not be found (got {!r})".format(certificate)) if not os.path.exists(private_key): raise ValueError( "Auth key can not be found (got {!r})".format(private_key)) ta = wsaa.Autenticar("wsfe", certificate, private_key, wsdl=settings.AFIP['url_wsaa'], cache=CACHE, debug=True) wsfev1.Cuit = settings.AFIP['cuit'] wsfev1.SetTicketAcceso(ta) wsfev1.Conectar(CACHE, settings.AFIP['url_wsfev1']) # init PDF builder fepdf = FEPDF() fepdf.CargarFormato("factura.csv") fepdf.FmtCantidad = "0.2" fepdf.FmtPrecio = "0.2" fepdf.CUIT = settings.AFIP['cuit'] for k, v in CONFIG_PDF.items(): fepdf.AgregarDato(k, v) # safeguard when using test webservice endpoints if "h**o" in settings.AFIP['url_wsaa']: fepdf.AgregarCampo("DEMO", 'T', 120, 260, 0, 0, text="DEMOSTRACION", size=70, rotate=45, foreground=0x808080, priority=-1) fepdf.AgregarDato("motivos_obs", "Ejemplo Sin Validez Fiscal") # get CAE for each record and generate corresponding PDF results = {} for record in records: invoice = MemberInvoice(document_number=record['dni'], fullname=record['fullname'], address=record['address'], city=record['city'], zip_code=record['zip_code'], province=record['province'], invoice_date=record['invoice_date'], invoice_number=record['invoice'], service_date_from=record['service_date_from'], service_date_to=record['service_date_to']) invoice.add_item(description=record['description'], quantity=record['quantity'], amount=record['amount'], comment=record['payment_comment']) authorized_ok = invoice.autorizar(wsfev1) invoice_number = invoice.header["cbte_nro"] print(" invoice generated: number={} CAE={} authorized={}".format( invoice_number, invoice.header["cae"], authorized_ok)) results[invoice_number] = {'invoice_ok': authorized_ok} if not authorized_ok: print("WARNING not auth") return # another safeguard if "h**o" in settings.AFIP['url_wsfev1']: invoice.header["motivos_obs"] = "Ejemplo Sin validez fiscal" # generate the PDF pdf_name = "FacturaPyArAC-{:04d}-{:08d}.pdf".format( settings.AFIP['selling_point'], invoice_number) pdf_path = os.path.join(PDF_PATH, pdf_name) invoice.generate_pdf(fepdf, pdf_path) print(" PDF generated {!r}".format(pdf_path)) results[invoice_number]['pdf_path'] = pdf_path return results