From 48619106c5fa975a6f9cc5bd4519c35707128aaf Mon Sep 17 00:00:00 2001 From: bit4bit <bit4bit@noemail.net> Date: Sun, 15 Nov 2020 23:21:52 +0000 Subject: [PATCH] se adiciona documentacion FossilOrigin-Name: 20a903a2426d4454a9909c78411b3ad7bd7f7d34d576ed5618e73784f77c8d92 --- README.rst | 18 ++- USAGE.rst | 24 ++++ ...nvoice.py => generate-invoice-from-cli.py} | 87 +++++++++---- examples/use-as-lib.py | 114 ++++++++++++++++++ 4 files changed, 211 insertions(+), 32 deletions(-) create mode 100644 USAGE.rst rename examples/{generate-invoice-invoice.py => generate-invoice-from-cli.py} (57%) create mode 100644 examples/use-as-lib.py diff --git a/README.rst b/README.rst index 187d0f7..dbdaf61 100644 --- a/README.rst +++ b/README.rst @@ -24,7 +24,7 @@ usando pip:: CLI === -tambien se provee linea de comandos **facho** para firmado y envio de documentos:: +tambien se provee linea de comandos **facho** para generacion, firmado y envio de documentos:: facho --help CONTRIBUIR @@ -32,17 +32,13 @@ CONTRIBUIR ver **CONTRIBUTING.rst** +USO +=== + +ver **USAGE.rst** + + DIAN HABILITACION ================= guia oficial actualizada al 2020-04-20: https://www.dian.gov.co/fizcalizacioncontrol/herramienconsulta/FacturaElectronica/Facturaci%C3%B3n_Gratuita_DIAN/Documents/Guia_usuario_08052019.pdf#search=numeracion - - -ERROR X509SerialNumber -====================== - - -lxml.etree.DocumentInvalid: Element '{http://www.w3.org/2000/09/xmldsig#}X509SerialNumber': '632837201711293159666920255411738137494572618415' is not a valid value of the atomic type 'xs:integer' - -Actualmente el xmlschema usado por xmlsig para el campo X509SerialNumber es tipo -integer ahi que parchar manualmente a tipo string, en el archivo site-packages/xmlsig/data/xmldsig-core-schema.xsd. diff --git a/USAGE.rst b/USAGE.rst new file mode 100644 index 0000000..468fb57 --- /dev/null +++ b/USAGE.rst @@ -0,0 +1,24 @@ +uso de la libreria +================== + +**facho** es tanto una libreria para modelar y generar los documentos xml requeridos para la facturacion, +asi como una herramienta de **consola** para facilitar algunas actividades como: generaciones de xml +apartir de una especificacion en python, comprimir y enviar archivos según el SOAP vigente. + +**facho** es diseñado para ser usado en conjunto con el documento **docs/DIAN/Anexo_Tecnico_Factura_Electronica_Vr1_7_2020.pdf**, ya que en gran medida sigue la terminologia presente en este. + + +Para ejemplos ver **examples/** . + +En terminos generales seria modelar la factura usando **facho/fe/form.py**, instanciar las extensiones requeridas ver **facho/fe/fe.py** y +una vez generado el objeto invoice y las extensiones requeridas se procede a crear el XML, ejemplo: + +~~~python +.... +xml = form_xml.DIANInvoiceXML(invoice) +extensions = module.extensions(invoice) +for extension in extensions: + xml.add_extension(extension) + +form_xml.DIANWriteSigned(xml, "factura.xml", "llave privada", "frase") +~~~ diff --git a/examples/generate-invoice-invoice.py b/examples/generate-invoice-from-cli.py similarity index 57% rename from examples/generate-invoice-invoice.py rename to examples/generate-invoice-from-cli.py index 91e2419..9c89ab3 100644 --- a/examples/generate-invoice-invoice.py +++ b/examples/generate-invoice-from-cli.py @@ -1,39 +1,49 @@ +# este archivo es un ejemplo para le generacion +# una factura de venta nacional usando el comando **facho**. +# +# ejemplo: facho generate-invoice generate-invoice-from-cli.py +# +# importar libreria de modelos import facho.fe.form as form +import facho.fe.form_xml + +# importar libreria extensiones xml para cumplir decreto from facho.fe import fe + +# importar otras necesarias from datetime import datetime -def extensions(inv): - nit = form.PartyIdentification('nit', '5', '31') - security_code = fe.DianXMLExtensionSoftwareSecurityCode('id software', 'pin', inv.invoice_ident) - authorization_provider = fe.DianXMLExtensionAuthorizationProvider() - cufe = fe.DianXMLExtensionCUFE(inv, fe.DianXMLExtensionCUFE.AMBIENTE_PRUEBAS, - 'clave tecnica') - software_provider = fe.DianXMLExtensionSoftwareProvider(nit, nit.dv, 'id software') - inv_authorization = fe.DianXMLExtensionInvoiceAuthorization('invoice autorization', - datetime(2019, 1, 19), - datetime(2030, 1, 19), - 'SETP', 990000001, 995000000) - return [security_code, authorization_provider, cufe, software_provider, inv_authorization] - - +# callback que retorna el modelado de documento electronico +# a generar def invoice(): - inv = form.Invoice() + # factura de venta nacional + inv = form.NationalSalesInvoice() + # asignar periodo de facturacion inv.set_period(datetime.now(), datetime.now()) + # asignar fecha de emision de la factura inv.set_issue(datetime.now()) + # asignar prefijo y numero del documento inv.set_ident('SETP990003033') + # asignar tipo de operacion ver DIAN:6.1.5 inv.set_operation_type('10') + # asignar proveedor inv.set_supplier(form.Party( legal_name = 'FACHO SOS', name = 'FACHO SOS', ident = form.PartyIdentification('900579212', '5', '31'), + # obligaciones del contribuyente ver DIAN:FAK26 responsability_code = form.Responsability(['O-07', 'O-09', 'O-14', 'O-48']), + # ver DIAN:FAJ28 responsability_regime_code = '48', + # tipo de organizacion juridica ver DIAN:6.2.3 organization_code = '1', email = "sdds@sd.com", address = form.Address( - '', '', form.City('05001', 'Medellín'), - form.Country('CO', 'Colombia'), - form.CountrySubentity('05', 'Antioquia')) + name = '', + street = '', + city = form.City('05001', 'Medellín'), + country = form.Country('CO', 'Colombia'), + countrysubentity = form.CountrySubentity('05', 'Antioquia')) )) inv.set_customer(form.Party( legal_name = 'facho-customer', @@ -44,22 +54,34 @@ def invoice(): organization_code = '2', email = "sdds@sd.com", address = form.Address( - '', '', form.City('05001', 'Medellín'), - form.Country('CO', 'Colombia'), - form.CountrySubentity('05', 'Antioquia')) + name = '', + street = '', + city = form.City('05001', 'Medellín'), + country = form.Country('CO', 'Colombia'), + countrysubentity = form.CountrySubentity('05', 'Antioquia')) )) + # asignar metodo de pago inv.set_payment_mean(form.PaymentMean( + # metodo de pago ver DIAN:3.4.1 id = '1', + # codigo correspondiente al medio de pago ver DIAN:3.4.2 code = '10', + # fecha de vencimiento de la factura due_at = datetime.now(), + + # identificador numerico payment_id = '1' )) + # adicionar una linea al documento inv.add_invoice_line(form.InvoiceLine( quantity = form.Quantity(1, '94'), description = 'producto facho', + # item general de codigo 999 item = form.StandardItem('test', 9999), price = form.Price( + # precio base del tiem amount = form.Amount(100.00), + # ver DIAN:6.3.5.1 type_code = '01', type = 'x' ), @@ -72,3 +94,26 @@ def invoice(): ) )) return inv + +# callback que retonar las extensiones XML necesarias +# para que el documento final XML cumpla el decreto. +# +# muchos de los valores usados son obtenidos +# del servicio web de la DIAN. +def extensions(inv): + security_code = fe.DianXMLExtensionSoftwareSecurityCode('id software', 'pin', inv.invoice_ident) + authorization_provider = fe.DianXMLExtensionAuthorizationProvider() + cufe = fe.DianXMLExtensionCUFE(inv, fe.DianXMLExtensionCUFE.AMBIENTE_PRUEBAS, + 'clave tecnica') + nit = form.PartyIdentification('nit', '5', '31') + software_provider = fe.DianXMLExtensionSoftwareProvider(nit, nit.dv, 'id software') + inv_authorization = fe.DianXMLExtensionInvoiceAuthorization('invoice autorization', + datetime(2019, 1, 19), + datetime(2030, 1, 19), + 'SETP', 990000001, 995000000) + return [security_code, authorization_provider, cufe, software_provider, inv_authorization] + + +# callback con transformador a XML +def document_xml(): + return form_xml.DIANInvoiceXML diff --git a/examples/use-as-lib.py b/examples/use-as-lib.py new file mode 100644 index 0000000..96dbbaa --- /dev/null +++ b/examples/use-as-lib.py @@ -0,0 +1,114 @@ +# importar libreria de modelos +import facho.fe.form as form +import facho.fe.form_xml + +PRIVATE_KEY_PATH='ruta a mi llave privada' +PRIVATE_PASSPHRASE='clave de la llave privada' + +# consultar las extensiones necesarias +def extensions(inv): + security_code = fe.DianXMLExtensionSoftwareSecurityCode('id software', 'pin', inv.invoice_ident) + authorization_provider = fe.DianXMLExtensionAuthorizationProvider() + cufe = fe.DianXMLExtensionCUFE(inv, fe.DianXMLExtensionCUFE.AMBIENTE_PRUEBAS, + 'clave tecnica') + nit = form.PartyIdentification('nit', '5', '31') + software_provider = fe.DianXMLExtensionSoftwareProvider(nit, nit.dv, 'id software') + inv_authorization = fe.DianXMLExtensionInvoiceAuthorization('invoice autorization', + datetime(2019, 1, 19), + datetime(2030, 1, 19), + 'SETP', 990000001, 995000000) + return [security_code, authorization_provider, cufe, software_provider, inv_authorization] + +# generar documento desde modelo a ruta indicada +def generate_document(invoice, filepath): + xml = form_xml.DIANInvoiceXML(invoice) + for extension in extensions(invoice): + xml.add_extension(extension) + form_xml.utils.DIANWriteSigned(xml, filepath, PRIVATE_KEY_PATH, PRIVATE_PASSPHRASE, True) + +# Modelars las facturas +# ... + +# factura de venta nacional +inv = form.NationalSalesInvoice() +# asignar periodo de facturacion +inv.set_period(datetime.now(), datetime.now()) +# asignar fecha de emision de la factura +inv.set_issue(datetime.now()) +# asignar prefijo y numero del documento +inv.set_ident('SETP990003033') +# asignar tipo de operacion ver DIAN:6.1.5 +inv.set_operation_type('10') +# asignar proveedor +inv.set_supplier(form.Party( + legal_name = 'FACHO SOS', + name = 'FACHO SOS', + ident = form.PartyIdentification('900579212', '5', '31'), + # obligaciones del contribuyente ver DIAN:FAK26 + responsability_code = form.Responsability(['O-07', 'O-09', 'O-14', 'O-48']), + # ver DIAN:FAJ28 + responsability_regime_code = '48', + # tipo de organizacion juridica ver DIAN:6.2.3 + organization_code = '1', + email = "sdds@sd.com", + address = form.Address( + name = '', + street = '', + city = form.City('05001', 'Medellín'), + country = form.Country('CO', 'Colombia'), + countrysubentity = form.CountrySubentity('05', 'Antioquia')) +)) +inv.set_customer(form.Party( + legal_name = 'facho-customer', + name = 'facho-customer', + ident = form.PartyIdentification('999999999', '', '13'), + responsability_code = form.Responsability(['R-99-PN']), + responsability_regime_code = '49', + organization_code = '2', + email = "sdds@sd.com", + address = form.Address( + name = '', + street = '', + city = form.City('05001', 'Medellín'), + country = form.Country('CO', 'Colombia'), + countrysubentity = form.CountrySubentity('05', 'Antioquia')) +)) +# asignar metodo de pago +inv.set_payment_mean(form.PaymentMean( + # metodo de pago ver DIAN:3.4.1 + id = '1', + # codigo correspondiente al medio de pago ver DIAN:3.4.2 + code = '10', + # fecha de vencimiento de la factura + due_at = datetime.now(), + + # identificador numerico + payment_id = '1' +)) +# adicionar una linea al documento +inv.add_invoice_line(form.InvoiceLine( + quantity = form.Quantity(1, '94'), + description = 'producto facho', + # item general de codigo 999 + item = form.StandardItem('test', 9999), + price = form.Price( + # precio base del tiem + amount = form.Amount(100.00), + # ver DIAN:6.3.5.1 + type_code = '01', + type = 'x' + ), + tax = form.TaxTotal( + subtotals = [ + form.TaxSubTotal( + percent = 19.00, + ) + ] + ) +)) + +# refrescar valores de la factura +inv.calculate() + +# generar el documento xml en la ruta indicada +generate_document(inv, 'ruta a donde guardar el .xml')