# -*- coding: utf-8 -*- import sys import base64 import warnings import click import logging.config logging.config.dictConfig({ 'version': 1, 'formatters': { 'verbose': { 'format': '%(name)s: %(message)s' } }, 'handlers': { 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'verbose', }, }, 'loggers': { 'zeep.transports': { 'level': 'DEBUG', 'propagate': True, 'handlers': ['console'], }, } }) def disable_ssl(): # MACHETE import ssl if getattr(ssl, '_create_unverified_context', None): ssl._create_default_https_context = ssl._create_unverified_context warnings.warn("be sure!! ssl disable") else: warnings.warn("can't disable ssl") @click.command() @click.option('--nit', required=True) @click.option('--nit-proveedor', required=True) @click.option('--id-software', required=True) @click.option('--username', required=True) @click.option('--password', required=True) def consultaResolucionesFacturacion(nit, nit_proveedor, id_software, username, password): from facho.fe.client import dian client_dian = dian.DianClient(username, password) resp = client_dian.request(dian.ConsultaResolucionesFacturacionPeticion( nit, nit_proveedor, id_software )) print(str(resp)) @click.command() @click.option('--private-key', required=True) @click.option('--public-key', required=True) @click.option('--habilitacion/--produccion', default=False) @click.option('--password') @click.option('--test-setid', required=True) @click.argument('filename', required=True) @click.argument('zipfile', type=click.Path(exists=True)) def soap_send_test_set_async(private_key, public_key, habilitacion, password, test_setid, filename, zipfile): from facho.fe.client import dian client = dian.DianSignatureClient(private_key, public_key, password=password) req = dian.SendTestSetAsync if habilitacion: req = dian.Habilitacion.SendTestSetAsync resp = client.request(req( filename, open(zipfile, 'rb').read(), test_setid, )) print(resp) @click.command() @click.option('--private-key', required=True) @click.option('--public-key', required=True) @click.option('--habilitacion/--produccion', default=False) @click.option('--password') @click.argument('filename', required=True) @click.argument('zipfile', type=click.Path(exists=True)) def soap_send_bill_async(private_key, public_key, habilitacion, password, filename, zipfile): from facho.fe.client import dian client = dian.DianSignatureClient(private_key, public_key, password=password) req = dian.SendBillAsync if habilitacion: req = dian.Habilitacion.SendBillAsync resp = client.request(req( filename, open(zipfile, 'rb').read() )) print(resp) @click.command() @click.option('--private-key', required=True) @click.option('--public-key', required=True) @click.option('--habilitacion/--produccion', default=False) @click.option('--password') @click.argument('filename', required=True) @click.argument('zipfile', type=click.Path(exists=True)) def soap_send_bill_sync(private_key, public_key, habilitacion, password, filename, zipfile): from facho.fe.client import dian client = dian.DianSignatureClient(private_key, public_key, password=password) req = dian.SendBillSync if habilitacion: req = dian.Habilitacion.SendBillSync resp = client.request(req( filename, open(zipfile, 'rb').read() )) print(resp) @click.command() @click.option('--private-key', required=True) @click.option('--public-key', required=True) @click.option('--habilitacion/--produccion', default=False) @click.option('--password') @click.option('--track-id', required=True) def soap_get_status_zip(private_key, public_key, habilitacion, password, track_id): from facho.fe.client import dian client = dian.DianSignatureClient(private_key, public_key, password=password) req = dian.GetStatusZip if habilitacion: req = dian.Habilitacion.GetStatusZip resp = client.request(req( trackId = track_id )) print("StatusCode:", resp.StatusCode) print("StatusDescription:", resp.StatusDescription) print("===ERRORES NOTIFICADOS====") for error_msg in resp.ErrorMessage: print("*", error_msg) @click.command() @click.option('--private-key', required=True) @click.option('--public-key', required=True) @click.option('--habilitacion/--produccion', default=False) @click.option('--password') @click.option('--track-id', required=True) def soap_get_status(private_key, public_key, habilitacion, password, track_id): from facho.fe.client import dian client = dian.DianSignatureClient(private_key, public_key, password=password) req = dian.GetStatus if habilitacion: req = dian.Habilitacion.GetStatus resp = client.request(req( trackId = track_id )) print(resp) @click.command() @click.option('--private-key', required=True) @click.option('--public-key', required=True) @click.option('--habilitacion/--produccion', default=False) @click.option('--password') @click.option('--nit', required=True) @click.option('--nit-proveedor', required=True) @click.option('--id-software', required=True) def soap_get_numbering_range(private_key, public_key, habilitacion, password, nit, nit_proveedor, id_software): from facho.fe.client import dian client = dian.DianSignatureClient(private_key, public_key, password=password) req = dian.GetNumberingRange if habilitacion: req = dian.Habilitacion.GetNumberingRange resp = client.request(req( nit, nit_proveedor, id_software )) print(resp) @click.command() @click.argument('invoice_path') def validate_invoice(invoice_path): warnings.warn("!! NO APROBADO FUNCIONAMIENTO") from facho.fe.data.dian import XSD content = open(invoice_path, 'r').read() # TODO donde ubicar esta responsabilidad? # esto es requerido por el XSD de la DIAN content = content.replace( 'xmlns:fe="http://www.dian.gov.co/contratos/facturaelectronica/v1"', 'xmlns:fe="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"' ) XSD.validate(content, XSD.UBLInvoice) @click.command() @click.argument('nomina_path') def validate_nominaindividual(nomina_path): from facho.fe.data.dian import XSD content = open(nomina_path, 'r').read() content = content.replace( 'xmlns="http://www.dian.gov.co/contratos/facturaelectronica/v1"', 'xmlns="dian:gov:co:facturaelectronica:NominaIndividual"', ) XSD.validate(content, XSD.NominaIndividual) @click.command() @click.option('--private-key', type=click.Path(exists=True)) @click.option('--passphrase') @click.option('--ssl/--no-ssl', default=False) @click.option('--use-cache-policy/--no-use-cache-policy', default=False) @click.argument('xmlfile', type=click.Path(exists=True), required=True) @click.argument('output', required=True) def sign_xml(private_key, passphrase, xmlfile, ssl=True, use_cache_policy=False, output=None): if not ssl: disable_ssl() from facho import fe if use_cache_policy: warnings.warn("xades using cache policy") signer = fe.DianXMLExtensionSigner(private_key, passphrase=passphrase, localpolicy=use_cache_policy) document = open(xmlfile, 'r').read().encode('utf-8') with open(output, 'w') as f: f.write(signer.sign_xml_string(document)) @click.command() @click.option('--private-key', type=click.Path(exists=True)) @click.option('--generate/--validate', default=False) @click.option('--passphrase') @click.option('--ssl/--no-ssl', default=False) @click.option('--sign/--no-sign', default=False) @click.option('--use-cache-policy/--no-use-cache-policy', default=False) @click.argument('scriptname', type=click.Path(exists=True), required=True) @click.argument('output', required=True) def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=True, sign=False, use_cache_policy=False, output=None): """ imprime xml en pantalla. SCRIPTNAME espera def invoice() -> form.Invoice def extensions(form.Invoice): -> List[facho.FachoXMLExtension] """ if not ssl: disable_ssl() import importlib.util spec = importlib.util.spec_from_file_location('invoice', scriptname) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) import facho.fe.form as form from facho.fe.form_xml import DIANInvoiceXML, DIANWriteSigned,DIANWrite from facho import fe try: invoice_xml = module.document_xml() except AttributeError: invoice_xml = DIANInvoiceXML print("Using document xml:", invoice_xml) invoice = module.invoice() invoice.calculate() if generate: xml = invoice_xml(invoice) extensions = module.extensions(invoice) for extension in extensions: xml.add_extension(extension) if sign: DIANWriteSigned(xml, output, private_key, passphrase, use_cache_policy) else: DIANWrite(xml, output) @click.command() @click.option('--private-key', type=click.Path(exists=True)) @click.option('--passphrase') @click.option('--ssl/--no-ssl', default=False) @click.option('--sign/--no-sign', default=False) @click.option('--use-cache-policy/--no-use-cache-policy', default=False) @click.argument('scriptname', type=click.Path(exists=True), required=True) @click.argument('output', required=True) def generate_nomina(private_key, passphrase, scriptname, ssl=True, sign=False, use_cache_policy=False, output=None): """ imprime xml en pantalla. SCRIPTNAME espera def nomina() -> fe.nomina.NominaIndividual def extensions(fe.nomina.NominaIndividual): -> List[facho.FachoXMLExtension] """ if not ssl: disable_ssl() import importlib.util spec = importlib.util.spec_from_file_location('nomina', scriptname) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) from facho.fe.form_xml import DIANWriteSigned, DIANWrite import facho.fe nomina = module.nomina() xml = nomina.toFachoXML() extensions = module.extensions(nomina) for extension in extensions: xml.add_extension(extension) if sign: DIANWriteSigned(xml, output, private_key, passphrase, use_cache_policy, dian_signer=facho.fe.nomina.DianXMLExtensionSigner) else: DIANWrite(xml, output) @click.command() @click.option('--private-key', required=True) @click.option('--public-key', required=True) @click.option('--habilitacion/--produccion', default=False) @click.option('--password') @click.argument('filename', required=True) @click.argument('zipfile', type=click.Path(exists=True)) def soap_send_nomina_sync(private_key, public_key, habilitacion, password, filename, zipfile): from facho.fe.client import dian client = dian.DianSignatureClient(private_key, public_key, password=password) req = dian.SendNominaSync if habilitacion: req = dian.Habilitacion.SendNominaSync resp = client.request(req( open(zipfile, 'rb').read() )) print(resp) @click.command() @click.option('--private-key', type=click.Path(exists=True)) @click.option('--passphrase') @click.option('--ssl/--no-ssl', default=False) @click.option('--use-cache-policy/--no-use-cache-policy', default=False) @click.argument('xmlfile', type=click.Path(exists=True), required=True) def sign_verify_xml(private_key, passphrase, xmlfile, ssl=True, use_cache_policy=False, output=None): if not ssl: disable_ssl() from facho.fe import fe if use_cache_policy: warnings.warn("xades using cache policy") print("THIS ONLY WORKS FOR DOCUMENTS GENERATE WITH FACHO") signer = fe.DianXMLExtensionSignerVerifier(private_key, passphrase=passphrase, localpolicy=use_cache_policy) document = open(xmlfile, 'r').read().encode('utf-8') if signer.verify_string(document): print("+OK") else: print("-INVALID") @click.command() @click.option('--software-id') @click.option('--software-pin') @click.option('--nit') @click.option('--dv') @click.option('--output-zippath') def generate_nomina_habilitacion(software_id, software_pin, nit, dv, output_zippath): from facho import fe generador = fe.nomina.habilitacion.Habilitacion( fe.nomina.habilitacion.Habilitacion.Metadata( software_id=software_id, software_pin=software_pin, nit=nit, dv=dv ) ) generador.generar(output_zippath) @click.group() def main(): pass main.add_command(consultaResolucionesFacturacion) main.add_command(soap_send_test_set_async) main.add_command(soap_send_bill_async) main.add_command(soap_send_bill_sync) main.add_command(soap_get_status) main.add_command(soap_get_status_zip) main.add_command(soap_get_numbering_range) main.add_command(generate_invoice) main.add_command(validate_invoice) main.add_command(sign_xml) main.add_command(sign_verify_xml) main.add_command(generate_nomina) main.add_command(soap_send_nomina_sync) main.add_command(validate_nominaindividual) main.add_command(generate_nomina_habilitacion)