diff --git a/facho/cli.py b/facho/cli.py index 9ef1fec..5c331fb 100644 --- a/facho/cli.py +++ b/facho/cli.py @@ -1,39 +1,37 @@ # -*- 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', +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'], + "loggers": { + "zeep.transports": { + "level": "DEBUG", + "propagate": True, + "handlers": ["console"], + }, }, } -}) +) + def disable_ssl(): # MACHETE import ssl - if getattr(ssl, '_create_unverified_context', None): + + if getattr(ssl, "_create_unverified_context", None): ssl._create_default_https_context = ssl._create_unverified_context warnings.warn("be sure!! ssl disable") else: @@ -41,99 +39,122 @@ def 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): +@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 - )) + + 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): +@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) + 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, - )) + 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): +@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) + 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() - )) + 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): +@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) + 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() - )) + 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): +@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) + 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 - )) + resp = client.request(req(trackId=track_id)) print("StatusCode:", resp.StatusCode) print("StatusDescription:", resp.StatusDescription) @@ -143,68 +164,78 @@ def soap_get_status_zip(private_key, public_key, habilitacion, password, track_i @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): +@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) + 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 - )) + 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): +@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) + 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 - )) + resp = client.request(req(nit, nit_proveedor, id_software)) print(resp) + @click.command() -@click.argument('invoice_path') +@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() + + 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"' + 'xmlns:fe="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"', ) XSD.validate(content, XSD.UBLInvoice) @click.command() -@click.argument('nomina_path') +@click.argument("nomina_path") def validate_nominaindividual(nomina_path): from facho.fe.data.dian import XSD - content = open(nomina_path, 'r').read() + + content = open(nomina_path, "r").read() content = content.replace( 'xmlns="http://www.dian.gov.co/contratos/facturaelectronica/v1"', 'xmlns="dian:gov:co:facturaelectronica:NominaIndividual"', @@ -213,35 +244,55 @@ def validate_nominaindividual(nomina_path): @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): +@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: + 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): +@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 @@ -254,19 +305,21 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr import importlib.util - spec = importlib.util.spec_from_file_location('invoice', scriptname) + 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, DIANSupportDocumentXML - from facho import fe + from facho.fe.form_xml import ( + DIANWriteSigned, + DIANWrite, + DIANSupportDocumentXML, + ) try: invoice_xml = module.document_xml() except AttributeError: - #invoice_xml = DIANInvoiceXML - invoice_xml = DIANSupportDocumentXML + # invoice_xml = DIANInvoiceXML + invoice_xml = DIANSupportDocumentXML print("Using document xml:", invoice_xml) invoice = module.invoice() invoice.calculate() @@ -279,24 +332,39 @@ def generate_invoice(private_key, passphrase, scriptname, generate=False, ssl=Tr xml.add_extension(extension) if sign: - DIANWriteSigned(xml, output, private_key, passphrase, use_cache_policy) + 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): +@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] + def nomina() -> ( + fe.nomina.NominaIndividual + ) + def extensions( + fe.nomina.NominaIndividual + ): -> List[facho.FachoXMLExtension] """ if not ssl: @@ -304,7 +372,7 @@ def generate_nomina(private_key, passphrase, scriptname, ssl=True, sign=False, u import importlib.util - spec = importlib.util.spec_from_file_location('nomina', scriptname) + spec = importlib.util.spec_from_file_location("nomina", scriptname) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) @@ -320,59 +388,83 @@ def generate_nomina(private_key, passphrase, scriptname, ssl=True, sign=False, u xml.add_extension(extension) if sign: - DIANWriteSigned(xml, output, private_key, passphrase, use_cache_policy, dian_signer=facho.fe.nomina.DianXMLExtensionSigner) + 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): +@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) + 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() - )) + 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): +@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') + 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): +@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( @@ -380,15 +472,17 @@ def generate_nomina_habilitacion(software_id, software_pin, nit, dv, output_zipp software_id=software_id, software_pin=software_pin, nit=nit, - dv=dv + 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) diff --git a/facho/facho.py b/facho/facho.py index 19dc8e6..1b903d7 100644 --- a/facho/facho.py +++ b/facho/facho.py @@ -119,7 +119,7 @@ class LXMLBuilder: def is_attribute(self, elem, key, value): return elem.get(key, False) == value - def set_attribute(self, elem, key, value): + def set_attribute(self, elem, key, value): elem.attrib[key] = value @classmethod @@ -156,6 +156,7 @@ class FachoXML: """ Decora XML con funciones de consulta XPATH de un solo elemento """ + def __init__(self, root, builder=None, nsmap=None, fragment_prefix='', fragment_root_element=None): if builder is None: diff --git a/facho/fe/__init__.py b/facho/fe/__init__.py index fdfaedc..4a63092 100644 --- a/facho/fe/__init__.py +++ b/facho/fe/__init__.py @@ -14,3 +14,22 @@ from .fe import AMBIENTE_PRUEBAS from .fe import AMBIENTE_PRODUCCION from . import form_xml from . import nomina + +__all__ = [ + 'FeXML', + 'fe_from_string', + 'NAMESPACES', + 'DianXMLExtensionSigner', + 'DianXMLExtensionSoftwareSecurityCode', + 'DianXMLExtensionCUFE', + 'DianXMLExtensionCUDE', + 'DianXMLExtensionCUDS', + 'DianXMLExtensionInvoiceAuthorization', + 'DianXMLExtensionSoftwareProvider', + 'DianXMLExtensionAuthorizationProvider', + 'DianZIP', + 'AMBIENTE_PRUEBAS', + 'AMBIENTE_PRODUCCION', + 'form_xml', + 'nomina', +] diff --git a/facho/fe/client/dian.py b/facho/fe/client/dian.py index fdd3611..c076f82 100644 --- a/facho/fe/client/dian.py +++ b/facho/fe/client/dian.py @@ -1,23 +1,13 @@ -from facho import facho import zeep from zeep.wsse.username import UsernameToken -from .wsse.signature import Signature, BinarySignature -from zeep.wsa import WsAddressingPlugin +from .wsse.signature import BinarySignature import xmlsec -import urllib.request -from datetime import datetime -from dataclasses import dataclass, asdict, field - -import http.client -import hashlib -import secrets -import base64 +from dataclasses import dataclass, asdict -__all__ = ['DianClient', - 'ConsultaResolucionesFacturacionPeticion', - 'ConsultaResolucionesFacturacionRespuesta'] +__all__ = ['DianClient'] + class SOAPService: @@ -33,6 +23,7 @@ class SOAPService: def todict(self): return asdict(self) + @dataclass class GetNumberingRangeResponse: @@ -49,7 +40,6 @@ class GetNumberingRangeResponse: NumberRangeResponse: list[NumberRangeResponse] - @classmethod def fromdict(cls, data): return cls( @@ -100,6 +90,7 @@ class SendTestSetAsyncResponse: data['ErrorMessageList'] or [] ) + @dataclass class SendTestSetAsync(SOAPService): fileName: str @@ -115,6 +106,7 @@ class SendTestSetAsync(SOAPService): def build_response(self, as_dict): return SendTestSetAsyncResponse.fromdict(as_dict) + @dataclass class SendBillSync(SOAPService): fileName: str @@ -129,6 +121,7 @@ class SendBillSync(SOAPService): def build_response(self, as_dict): return as_dict + @dataclass class GetStatusResponse: IsValid: bool @@ -142,12 +135,12 @@ class GetStatusResponse: error_message = data['ErrorMessage']['string'] else: error_message = None - + return cls(data['IsValid'], data['StatusDescription'], data['StatusCode'], error_message) - + @dataclass class GetStatus(SOAPService): @@ -162,6 +155,7 @@ class GetStatus(SOAPService): def build_response(self, as_dict): return GetStatusResponse.fromdict(as_dict) + @dataclass class GetStatusZip(SOAPService): trackId: bytes @@ -175,6 +169,7 @@ class GetStatusZip(SOAPService): def build_response(self, as_dict): return GetStatusResponse.fromdict(as_dict[0]) + @dataclass class SendNominaSync(SOAPService): contentFile: bytes @@ -188,7 +183,7 @@ class SendNominaSync(SOAPService): def build_response(self, as_dict): return as_dict - + class Habilitacion: WSDL = 'https://vpfe-hab.dian.gov.co/WcfDianCustomerServices.svc?wsdl' @@ -220,6 +215,7 @@ class Habilitacion: def get_wsdl(self): return Habilitacion.WSDL + class DianGateway: def _open(self, service): @@ -250,7 +246,11 @@ class DianClient(DianGateway): self._password = password def _open(self, service): - return zeep.Client(service.get_wsdl(), wsse=UsernameToken(self._username, self._password)) + return zeep.Client( + service.get_wsdl(), + wsse=UsernameToken( + self._username, + self._password)) class DianSignatureClient(DianGateway): @@ -262,13 +262,10 @@ class DianSignatureClient(DianGateway): def _open(self, service): # RESOLUCCION 0004: pagina 756 - from zeep.wsse import utils - client = zeep.Client(service.get_wsdl(), wsse= - BinarySignature( - self.private_key_path, self.public_key_path, self.password, - signature_method=xmlsec.Transform.RSA_SHA256, - digest_method=xmlsec.Transform.SHA256) - , + client = zeep.Client(service.get_wsdl(), wsse=BinarySignature( + self.private_key_path, self.public_key_path, self.password, + signature_method=xmlsec.Transform.RSA_SHA256, + digest_method=xmlsec.Transform.SHA256), ) return client diff --git a/facho/fe/client/wsse/signature.py b/facho/fe/client/wsse/signature.py index 6d69adc..87e31c4 100644 --- a/facho/fe/client/wsse/signature.py +++ b/facho/fe/client/wsse/signature.py @@ -70,8 +70,11 @@ class MemorySignature(object): def apply(self, envelope, headers): key = _make_sign_key(self.key_data, self.cert_data, self.password) _sign_envelope_with_key( - envelope, key, self.signature_method, self.digest_method, expires_dt=self.expires_dt - ) + envelope, + key, + self.signature_method, + self.digest_method, + expires_dt=self.expires_dt) return envelope, headers def verify(self, envelope): @@ -81,7 +84,7 @@ class MemorySignature(object): class Signature(MemorySignature): - """Sign given SOAP envelope with WSSE sig using given key file and cert file.""" + """Sign given SOAP envelope with WSSE sig using given key and cert file.""" def __init__( self, @@ -101,15 +104,18 @@ class Signature(MemorySignature): class BinarySignature(Signature): - """Sign given SOAP envelope with WSSE sig using given key file and cert file. + """Sign given SOAP envelope with WSSE sig using given key and cert file. Place the key information into BinarySecurityElement.""" def apply(self, envelope, headers): key = _make_sign_key(self.key_data, self.cert_data, self.password) _sign_envelope_with_key_binary( - envelope, key, self.signature_method, self.digest_method, expires_dt = self.expires_dt - ) + envelope, + key, + self.signature_method, + self.digest_method, + expires_dt=self.expires_dt) return envelope, headers @@ -219,9 +225,11 @@ def sign_envelope( """ # Load the signing key and certificate. key = _make_sign_key(_read_file(keyfile), _read_file(certfile), password) - return _sign_envelope_with_key(envelope, key, signature_method, digest_method) + return _sign_envelope_with_key( + envelope, key, signature_method, digest_method) -def get_timestamp(timestamp = None, delta=None): + +def get_timestamp(timestamp=None, delta=None): timestamp = timestamp or datetime.now(timezone.utc) if delta: timestamp += delta @@ -230,24 +238,33 @@ def get_timestamp(timestamp = None, delta=None): timestamp = timestamp.replace(tzinfo=pytz.utc, microsecond=0) return timestamp.strftime(format_) + def _append_timestamp(security, expires_dt=None): if expires_dt is None: expires_dt = timedelta(seconds=6000) - etimestamp = utils.WSU.Timestamp({'{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Id': utils.get_unique_id()}) + etimestamp = utils.WSU.Timestamp({ + '{http://docs.oasis-open.org/wss/2004/01/' + 'oasis-200401-wss-wssecurity-utility-1.0.xsd}Id': + utils.get_unique_id()}) etimestamp.append(utils.WSU.Created(get_timestamp())) etimestamp.append(utils.WSU.Expires(get_timestamp(delta=expires_dt))) security.insert(0, etimestamp) if etree.LXML_VERSION[:2] >= (3, 5): etree.cleanup_namespaces(security, - keep_ns_prefixes = security.nsmap, + keep_ns_prefixes=security.nsmap, top_nsmap=utils.NSMAP) else: - etree.cleanup_namespaces(header) + etree.cleanup_namespaces(security) -def _signature_prepare(envelope, key, signature_method, digest_method, expires_dt=None): + +def _signature_prepare( + envelope, + key, + signature_method, + digest_method, + expires_dt=None): """Prepare envelope and sign.""" - soap_env = detect_soap_env(envelope) # Create the Signature node. signature = xmlsec.template.create( @@ -278,7 +295,7 @@ def _signature_prepare(envelope, key, signature_method, digest_method, expires_d _append_timestamp(security, expires_dt=expires_dt) timestamp = security.find(QName(ns.WSU, "Timestamp")) - if timestamp != None: + if timestamp is not None: _sign_node(ctx, signature, timestamp, digest_method) ctx.sign(signature) @@ -286,18 +303,30 @@ def _signature_prepare(envelope, key, signature_method, digest_method, expires_d # KeyInfo. The recipient expects this structure, but we can't rearrange # like this until after signing, because otherwise xmlsec won't populate # the X509 data (because it doesn't understand WSSE). - sec_token_ref = etree.SubElement(key_info, QName(ns.WSSE, "SecurityTokenReference")) + sec_token_ref = etree.SubElement( + key_info, QName( + ns.WSSE, "SecurityTokenReference")) return security, sec_token_ref, x509_data -def _sign_envelope_with_key(envelope, key, signature_method, digest_method, expires_dt=None): +def _sign_envelope_with_key( + envelope, + key, + signature_method, + digest_method, + expires_dt=None): _, sec_token_ref, x509_data = _signature_prepare( envelope, key, signature_method, digest_method, expires_dt=expires_dt ) sec_token_ref.append(x509_data) -def _sign_envelope_with_key_binary(envelope, key, signature_method, digest_method, expires_dt=None): +def _sign_envelope_with_key_binary( + envelope, + key, + signature_method, + digest_method, + expires_dt=None): security, sec_token_ref, x509_data = _signature_prepare( envelope, key, signature_method, digest_method, expires_dt=expires_dt ) @@ -353,7 +382,10 @@ def _verify_envelope_with_key(envelope, key): ctx = xmlsec.SignatureContext() # Find each signed element and register its ID with the signing context. - refs = signature.xpath("ds:SignedInfo/ds:Reference", namespaces={"ds": ns.DS}) + refs = signature.xpath( + "ds:SignedInfo/ds:Reference", + namespaces={ + "ds": ns.DS}) for ref in refs: # Get the reference URI and cut off the initial '#' referenced_id = ref.get("URI")[1:] diff --git a/facho/fe/data/dian/XSD/__init__.py b/facho/fe/data/dian/XSD/__init__.py index ca33563..98eba3a 100644 --- a/facho/fe/data/dian/XSD/__init__.py +++ b/facho/fe/data/dian/XSD/__init__.py @@ -7,10 +7,17 @@ def path_for_xsd(dirname, xsdname): data_dir = os.path.dirname(os.path.abspath(__file__)) return os.path.join(data_dir, dirname, xsdname) -UBLInvoice= xmlschema.XMLSchema(path_for_xsd('maindoc', 'UBL-Invoice-2.1.xsd')) -NominaIndividual = xmlschema.XMLSchema(path_for_xsd('nomina', 'NominaIndividualElectronicaXSDV1.0.6.xsd')) -NominaIndividualDeAjuste = xmlschema.XMLSchema(path_for_xsd('nomina', 'NominaIndividualDeAjusteElectronicaXSDV1.0.6.xsd')) +UBLInvoice = xmlschema.XMLSchema( + path_for_xsd( + 'maindoc', + 'UBL-Invoice-2.1.xsd')) + +NominaIndividual = xmlschema.XMLSchema(path_for_xsd( + 'nomina', 'NominaIndividualElectronicaXSDV1.0.6.xsd')) +NominaIndividualDeAjuste = xmlschema.XMLSchema(path_for_xsd( + 'nomina', 'NominaIndividualDeAjusteElectronicaXSDV1.0.6.xsd')) + def validate(xml, schema): schema.validate(xml) diff --git a/facho/fe/data/dian/codelist/__init__.py b/facho/fe/data/dian/codelist/__init__.py index 4c8d9bf..f3af0c8 100644 --- a/facho/fe/data/dian/codelist/__init__.py +++ b/facho/fe/data/dian/codelist/__init__.py @@ -20,12 +20,12 @@ class CodeList: def _load(self, filename, primary_column): tree = etree.parse(filename) - #obtener identificadores... + # obtener identificadores... self.short_name = tree.find('./Identification/ShortName').text self.long_name = tree.find('./Identification/LongName').text self.version = tree.find('./Identification/Version').text - #obtener registros... + # obtener registros... for row in tree.findall('./SimpleCodeList/Row'): new_row = self.xmlrow_to_dict(row) primary_key = new_row[primary_column] @@ -34,7 +34,7 @@ class CodeList: def xmlrow_to_dict(self, xmlrow): row = {} - #construir registro... + # construir registro... for value in list(xmlrow): row[value.attrib['ColumnRef']] = list(value)[0].text @@ -60,6 +60,7 @@ class CodeList: # nombres de variables igual a ./Identification/ShortName # TODO: garantizar unica carga en python + __all__ = ['TipoOrganizacion', 'TipoResponsabilidad', 'TipoAmbiente', @@ -72,38 +73,88 @@ __all__ = ['TipoOrganizacion', 'Municipio', 'Departamento'] + def path_for_codelist(name): return os.path.join(DATA_DIR, name) -TipoOrganizacion = CodeList(path_for_codelist('TipoOrganizacion-2.1.gc'), 'code', 'name') -TipoResponsabilidad = CodeList(path_for_codelist('TipoResponsabilidad-2.1.gc'), 'code', 'name')\ - .update(CodeList(path_for_codelist('TipoResponsabilidad-2.1.custom.gc'), 'code', 'name')) -TipoAmbiente = CodeList(path_for_codelist('TipoAmbiente-2.1.gc'), 'code', 'name') -TipoDocumento = CodeList(path_for_codelist('TipoDocumento-2.1.gc'), 'code', 'name') -TipoImpuesto = CodeList(path_for_codelist('TipoImpuesto-2.1.gc'), 'code', 'name')\ - .update(CodeList(path_for_codelist('TipoImpuesto-2.1.custom.gc'), 'code', 'name')) -TarifaImpuesto = CodeList(path_for_codelist('TarifaImpuestoINC-2.1.gc'), 'code', 'name')\ - .update(CodeList(path_for_codelist('TarifaImpuestoIVA-2.1.gc'), 'code', 'name'))\ - .update(CodeList(path_for_codelist('TarifaImpuestoReteIVA-2.1.gc'), 'code', 'name'))\ - .update(CodeList(path_for_codelist('TarifaImpuestoReteRenta-2.1.gc'), 'code', 'name')) -CodigoPrecioReferencia = CodeList(path_for_codelist('CodigoPrecioReferencia-2.1.gc'), 'code', 'name') + +TipoOrganizacion = CodeList(path_for_codelist( + 'TipoOrganizacion-2.1.gc'), 'code', 'name') +TipoResponsabilidad = CodeList( + path_for_codelist('TipoResponsabilidad-2.1.gc'), + 'code', + 'name') .update( + CodeList( + path_for_codelist('TipoResponsabilidad-2.1.custom.gc'), + 'code', + 'name')) +TipoAmbiente = CodeList(path_for_codelist( + 'TipoAmbiente-2.1.gc'), 'code', 'name') +TipoDocumento = CodeList(path_for_codelist( + 'TipoDocumento-2.1.gc'), 'code', 'name') +TipoImpuesto = CodeList( + path_for_codelist('TipoImpuesto-2.1.gc'), + 'code', + 'name') .update( + CodeList( + path_for_codelist('TipoImpuesto-2.1.custom.gc'), + 'code', + 'name')) +TarifaImpuesto = CodeList( + path_for_codelist('TarifaImpuestoINC-2.1.gc'), + 'code', + 'name') .update( + CodeList( + path_for_codelist('TarifaImpuestoIVA-2.1.gc'), + 'code', + 'name')) .update( + CodeList( + path_for_codelist('TarifaImpuestoReteIVA-2.1.gc'), + 'code', + 'name')) .update( + CodeList( + path_for_codelist( + 'TarifaImpuestoReteRenta-2.1.gc'), + 'code', + 'name')) +CodigoPrecioReferencia = CodeList(path_for_codelist( + 'CodigoPrecioReferencia-2.1.gc'), 'code', 'name') MediosPago = CodeList(path_for_codelist('MediosPago-2.1.gc'), 'code', 'name') FormasPago = CodeList(path_for_codelist('FormasPago-2.1.gc'), 'code', 'name') -RegimenFiscal = CodeList(path_for_codelist('RegimenFiscal-2.1.custom.gc'), 'code', 'name') -TipoOperacionNC = CodeList(path_for_codelist('TipoOperacionNC-2.1.gc'), 'code', 'name') -TipoOperacionNCDS = CodeList(path_for_codelist('TipoOperacionNCDS-2.1.gc'), 'code', 'name') -TipoOperacionND = CodeList(path_for_codelist('TipoOperacionND-2.1 - copia.gc'), 'code', 'name') -TipoOperacionF = CodeList(path_for_codelist('TipoOperacionF-2.1.gc'), 'code', 'name')\ - .update(CodeList(path_for_codelist('TipoOperacionF-2.1.custom.gc'), 'code', 'name')) +RegimenFiscal = CodeList(path_for_codelist( + 'RegimenFiscal-2.1.custom.gc'), 'code', 'name') +TipoOperacionNC = CodeList(path_for_codelist( + 'TipoOperacionNC-2.1.gc'), 'code', 'name') +TipoOperacionNCDS = CodeList(path_for_codelist( + 'TipoOperacionNCDS-2.1.gc'), 'code', 'name') +TipoOperacionND = CodeList(path_for_codelist( + 'TipoOperacionND-2.1 - copia.gc'), 'code', 'name') +TipoOperacionF = CodeList( + path_for_codelist('TipoOperacionF-2.1.gc'), + 'code', + 'name') .update( + CodeList( + path_for_codelist('TipoOperacionF-2.1.custom.gc'), + 'code', + 'name')) Municipio = CodeList(path_for_codelist('Municipio-2.1.gc'), 'code', 'name') -Departamento = CodeList(path_for_codelist('Departamentos-2.1.gc'), 'code', 'name') +Departamento = CodeList(path_for_codelist( + 'Departamentos-2.1.gc'), 'code', 'name') Paises = CodeList(path_for_codelist('Paises-2.1.gc'), 'code', 'name') -TipoIdFiscal = CodeList(path_for_codelist('TipoIdFiscal-2.1.gc'), 'code', 'name') -CodigoDescuento = CodeList(path_for_codelist('CodigoDescuento-2.1.gc'), 'code', 'name') -UnidadesMedida = CodeList(path_for_codelist('UnidadesMedida-2.1.gc'), 'code', 'name') -TipoTrabajador = CodeList(path_for_codelist('TipoTrabajador-2.1.gc'), 'code', 'name') -SubTipoTrabajador = CodeList(path_for_codelist('SubTipoTrabajador-2.1.gc'), 'code', 'name') -TipoContrato = CodeList(path_for_codelist('TipoContrato-2.1.gc'), 'code', 'name') -PeriodoNomina = CodeList(path_for_codelist('PeriodoNomina-2.1.gc'), 'code', 'name') +TipoIdFiscal = CodeList(path_for_codelist( + 'TipoIdFiscal-2.1.gc'), 'code', 'name') +CodigoDescuento = CodeList(path_for_codelist( + 'CodigoDescuento-2.1.gc'), 'code', 'name') +UnidadesMedida = CodeList(path_for_codelist( + 'UnidadesMedida-2.1.gc'), 'code', 'name') +TipoTrabajador = CodeList(path_for_codelist( + 'TipoTrabajador-2.1.gc'), 'code', 'name') +SubTipoTrabajador = CodeList(path_for_codelist( + 'SubTipoTrabajador-2.1.gc'), 'code', 'name') +TipoContrato = CodeList(path_for_codelist( + 'TipoContrato-2.1.gc'), 'code', 'name') +PeriodoNomina = CodeList(path_for_codelist( + 'PeriodoNomina-2.1.gc'), 'code', 'name') TipoMoneda = CodeList(path_for_codelist('TipoMoneda-2.1.gc'), 'code', 'name') -IdiomaISO6391 = CodeList(path_for_codelist('Idioma-2.1.gc'), 'iso-639-1', 'name') +IdiomaISO6391 = CodeList(path_for_codelist( + 'Idioma-2.1.gc'), 'iso-639-1', 'name') diff --git a/facho/fe/fe.py b/facho/fe/fe.py index 1c6163e..e594152 100644 --- a/facho/fe/fe.py +++ b/facho/fe/fe.py @@ -37,34 +37,48 @@ AMBIENTE_PRODUCCION = codelist.TipoAmbiente.by_name('Producción')['code'] SCHEME_AGENCY_ATTRS = { - 'schemeAgencyName': 'CO, DIAN (Dirección de Impuestos y Aduanas Nacionales)', - 'schemeAgencyID': '195' -} + 'schemeAgencyName': 'CO, DIAN (Dirección de Impuestos y Aduanas' + ' Nacionales)', + 'schemeAgencyID': '195'} # RESOLUCION 0001: pagina 516 -POLICY_ID = 'https://facturaelectronica.dian.gov.co/politicadefirma/v2/politicadefirmav2.pdf' -POLICY_NAME = u'Política de firma para facturas electrónicas de la República de Colombia.' +POLICY_ID = ( + 'https://facturaelectronica.dian.gov.co/politicadefirma/v2' + '/politicadefirmav2.pdf' +) +POLICY_NAME = ( + u'Política de firma para facturas electrónicas de la República de ' + u'Colombia.' +) Bogota = tz.gettz('America/Bogota') # NAMESPACES = { # 'atd': 'urn:oasis:names:specification:ubl:schema:xsd:AttachedDocument-2', # 'nomina': 'dian:gov:co:facturaelectronica:NominaIndividual', -# 'nominaajuste': 'dian:gov:co:facturaelectronica:NominaIndividualDeAjuste', +# 'nominaajuste': 'dian:gov:co:facturaelectronica:' +# 'NominaIndividualDeAjuste', # 'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1', -# 'xs': 'http://www.w3.org/2001/XMLSchema-instance', -# 'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2', -# 'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', -# 'cdt': 'urn:DocumentInformation:names:specification:ubl:colombia:schema:xsd:DocumentInformationAggregateComponents-1', +# 'xs': 'http://www.w3.org/2001/XMLSchema-instance', +# 'cac': 'urn:oasis:names:specification:ubl:schema:xsd:' +# 'CommonAggregateComponents-2', +# 'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:' +# 'CommonBasicComponents-2', +# 'cdt': 'urn:DocumentInformation:names:specification:ubl:colombia:' +# 'schema:xsd:DocumentInformationAggregateComponents-1', # 'clm54217': 'urn:un:unece:uncefact:codelist:specification:54217:2001', -# 'clmIANAMIMEMediaType': 'urn:un:unece:uncefact:codelist:specification:IANAMIMEMediaType:2003', -# 'ext': 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2', -# 'qdt': 'urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2', +# 'clmIANAMIMEMediaType': 'urn:un:unece:uncefact:codelist:specification:' +# 'IANAMIMEMediaType:2003', +# 'ext': 'urn:oasis:names:specification:ubl:schema:xsd:' +# 'CommonExtensionComponents-2', +# 'qdt': 'urn:oasis:names:specification:ubl:schema:xsd:' +# 'QualifiedDatatypes-2', # 'sts': 'dian:gov:co:facturaelectronica:Structures-2-1', -# 'udt': 'urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2', +# 'udt': 'urn:un:unece:uncefact:data:specification:' +# 'UnqualifiedDataTypesSchemaModule:2', # 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', # 'xades': 'http://uri.etsi.org/01903/v1.3.2#', -# 'xades141': 'http://uri.etsi.org/01903/v1.4.1#', +# 'xades141': 'http://uri.etsi.org/01903/v1.4.1#', # 'ds': 'http://www.w3.org/2000/09/xmldsig#', # 'sig': 'http://www.w3.org/2000/09/xmldsig#', # } @@ -72,12 +86,19 @@ Bogota = tz.gettz('America/Bogota') NAMESPACES = { 'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1', - 'cac': 'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2', - 'cbc': 'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2', - 'ext': 'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2', + 'cac': ( + 'urn:oasis:names:specification:ubl:schema:xsd' + ':CommonAggregateComponents-2'), + 'cbc': 'urn:oasis:names:specification:ubl:schema:xsd' + ':CommonBasicComponents-2', + 'ext': ( + 'urn:oasis:names:specification:ubl:schema:xsd' + ':CommonExtensionComponents-2'), 'qdt': 'urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2', 'sts': 'dian:gov:co:facturaelectronica:Structures-2-1', - 'udt': 'urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2', + 'udt': ( + 'urn:un:unece:uncefact:data:specification' + ':UnqualifiedDataTypesSchemaModule:2'), 'xsi': 'http://www.w3.org/2001/XMLSchema-instance', 'ds': 'http://www.w3.org/2000/09/xmldsig#', 'xades': 'http://uri.etsi.org/01903/v1.3.2#', @@ -119,18 +140,21 @@ class FeXML(FachoXML): return super().from_string(document, namespaces=NAMESPACES) def tostring(self, **kw): - # MACHETE(bit4bit) la DIAN espera que la etiqueta raiz no este en un namespace + # MACHETE(bit4bit) la DIAN espera que la etiqueta raiz no este en un + # namespace root_namespace = self.root_namespace() root_localname = self.root_localname() xmlns_name = {v: k for k, v in NAMESPACES.items()}[root_namespace] if root_localname == 'Invoice': - urn_oasis = 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2' + urn_oasis = ( + 'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2') if root_localname == 'CreditNote': - urn_oasis = 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2' + urn_oasis = ( + 'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2') return super().tostring(**kw)\ - .replace(xmlns_name + ':', '')\ - .replace('xmlns:'+xmlns_name, 'xmlns')\ - .replace(root_namespace, urn_oasis) + .replace(xmlns_name + ':', '')\ + .replace('xmlns:' + xmlns_name, 'xmlns')\ + .replace(root_namespace, urn_oasis) class DianXMLExtensionCUDFE(FachoXMLExtension): @@ -149,9 +173,12 @@ class DianXMLExtensionCUDFE(FachoXMLExtension): def _get_qrcode(self, cufe): url_for = { - AMBIENTE_PRUEBAS: 'https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey=', - AMBIENTE_PRODUCCION: 'https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey=' - } + AMBIENTE_PRUEBAS: ( + 'https://catalogo-vpfe-hab.dian.gov.co/document/' + 'searchqr?documentkey='), + AMBIENTE_PRODUCCION: ( + 'https://catalogo-vpfe.dian.gov.co/document/' + 'searchqr?documentkey=')} return url_for[self.tipo_ambiente] + cufe def build(self, fachoxml): @@ -162,21 +189,29 @@ class DianXMLExtensionCUDFE(FachoXMLExtension): if self.schemeName() == "CUDS-SHA384": if fachoxml.tag_document() == 'Invoice': - fachoxml.set_element('./cbc:ProfileID', - 'DIAN 2.1: documento soporte en adquisiciones efectuadas a no obligados a facturar.') + fachoxml.set_element( + './cbc:ProfileID', + 'DIAN 2.1: documento soporte en adquisiciones ' + 'efectuadas a no obligados a facturar.') else: - fachoxml.set_element('./cbc:ProfileID', - 'DIAN 2.1: Nota de ajuste al documento soporte en adquisiciones efectuadas a sujetos no obligados a expedir factura o documento equivalente') + fachoxml.set_element( + './cbc:ProfileID', + 'DIAN 2.1: Nota de ajuste al documento soporte en ' + 'adquisiciones efectuadas a sujetos no obligados a ' + 'expedir factura o documento equivalente') else: - fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta') + fachoxml.set_element( + './cbc:ProfileID', + 'DIAN 2.1: Factura Electrónica de Venta') - # #DIAN 1.8.-2021: FAD03 - # fachoxml.set_element('./cbc:ProfileID', 'DIAN 2.1: Factura Electrónica de Venta') + # fachoxml.set_element('./cbc:ProfileID', + # 'DIAN 2.1: Factura Electrónica de Venta') fachoxml.set_element( './cbc:ProfileExecutionID', self._tipo_ambiente_int()) - #DIAN 1.7.-2020: FAB36 + # DIAN 1.7.-2020: FAB36 fachoxml.set_element( - './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:QRCode', + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:QRCode', self._get_qrcode(cufe)) def issue_time(self, datetime_): @@ -192,7 +227,8 @@ class DianXMLExtensionCUDFE(FachoXMLExtension): build_vars['FecFac'] = self.issue_date(invoice.invoice_issue) build_vars['HoraFac'] = self.issue_time(invoice.invoice_issue) # PAG 601 - build_vars['ValorBruto'] = invoice.invoice_legal_monetary_total.line_extension_amount + build_vars['ValorBruto'] = ( + invoice.invoice_legal_monetary_total.line_extension_amount) build_vars['ValorTotalPagar' ] = invoice.invoice_legal_monetary_total.payable_amount ValorImpuestoPara = defaultdict(lambda: form.Amount(0.0)) @@ -204,7 +240,8 @@ class DianXMLExtensionCUDFE(FachoXMLExtension): if subtotal.scheme is not None: # TODO cual es la naturaleza de tax_scheme_ident? codigo_impuesto = subtotal.scheme.code - ValorImpuestoPara.setdefault(codigo_impuesto, form.Amount(0.0)) + ValorImpuestoPara.setdefault( + codigo_impuesto, form.Amount(0.0)) ValorImpuestoPara[codigo_impuesto] += subtotal.tax_amount build_vars['ValorImpuestoPara'] = ValorImpuestoPara @@ -225,7 +262,7 @@ class DianXMLExtensionCUDFE(FachoXMLExtension): class DianXMLExtensionCUFE(DianXMLExtensionCUDFE): def __init__( - self, invoice, clave_tecnica='', tipo_ambiente=AMBIENTE_PRUEBAS): + self, invoice, clave_tecnica='', tipo_ambiente=AMBIENTE_PRUEBAS): self.tipo_ambiente = tipo_ambiente self.clave_tecnica = clave_tecnica self.invoice = invoice @@ -244,26 +281,40 @@ class DianXMLExtensionCUFE(DianXMLExtensionCUDFE): CodImpuesto2 = build_vars['CodImpuesto2'] CodImpuesto3 = build_vars['CodImpuesto3'] return [ - '%s' % build_vars['NumFac'], - '%s' % build_vars['FecFac'], - '%s' % build_vars['HoraFac'], - form.Amount(build_vars['ValorBruto']).truncate_as_string(2), + '%s' % + build_vars['NumFac'], + '%s' % + build_vars['FecFac'], + '%s' % + build_vars['HoraFac'], + form.Amount( + build_vars['ValorBruto']).truncate_as_string(2), CodImpuesto1, - build_vars['ValorImpuestoPara'].get(CodImpuesto1, form.Amount(0.0)).truncate_as_string(2), + build_vars['ValorImpuestoPara'].get( + CodImpuesto1, + form.Amount(0.0)).truncate_as_string(2), CodImpuesto2, - build_vars['ValorImpuestoPara'].get(CodImpuesto2, form.Amount(0.0)).truncate_as_string(2), + build_vars['ValorImpuestoPara'].get( + CodImpuesto2, + form.Amount(0.0)).truncate_as_string(2), CodImpuesto3, - build_vars['ValorImpuestoPara'].get(CodImpuesto3, form.Amount(0.0)).truncate_as_string(2), + build_vars['ValorImpuestoPara'].get( + CodImpuesto3, + form.Amount(0.0)).truncate_as_string(2), build_vars['ValorTotalPagar'].truncate_as_string(2), - '%s' % build_vars['NitOFE'], - '%s' % build_vars['NumAdq'], - '%s' % build_vars['ClTec'], - '%d' % build_vars['TipoAmb'], + '%s' % + build_vars['NitOFE'], + '%s' % + build_vars['NumAdq'], + '%s' % + build_vars['ClTec'], + '%d' % + build_vars['TipoAmb'], ] class DianXMLExtensionCUDE(DianXMLExtensionCUDFE): - def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS): + def __init__(self, invoice, software_pin, tipo_ambiente=AMBIENTE_PRUEBAS): self.tipo_ambiente = tipo_ambiente self.software_pin = software_pin self.invoice = invoice @@ -282,26 +333,44 @@ class DianXMLExtensionCUDE(DianXMLExtensionCUDFE): CodImpuesto2 = build_vars['CodImpuesto2'] CodImpuesto3 = build_vars['CodImpuesto3'] return [ - '%s' % build_vars['NumFac'], - '%s' % build_vars['FecFac'], - '%s' % build_vars['HoraFac'], - form.Amount(build_vars['ValorBruto']).truncate_as_string(2), + '%s' % + build_vars['NumFac'], + '%s' % + build_vars['FecFac'], + '%s' % + build_vars['HoraFac'], + form.Amount( + build_vars['ValorBruto']).truncate_as_string(2), CodImpuesto1, - form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto1, 0.0)).truncate_as_string(2), + form.Amount( + build_vars['ValorImpuestoPara'].get( + CodImpuesto1, + 0.0)).truncate_as_string(2), CodImpuesto2, - form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto2, 0.0)).truncate_as_string(2), + form.Amount( + build_vars['ValorImpuestoPara'].get( + CodImpuesto2, + 0.0)).truncate_as_string(2), CodImpuesto3, - form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto3, 0.0)).truncate_as_string(2), - form.Amount(build_vars['ValorTotalPagar']).truncate_as_string(2), - '%s' % build_vars['NitOFE'], - '%s' % build_vars['NumAdq'], - '%s' % build_vars['Software-PIN'], - '%d' % build_vars['TipoAmb'], + form.Amount( + build_vars['ValorImpuestoPara'].get( + CodImpuesto3, + 0.0)).truncate_as_string(2), + form.Amount( + build_vars['ValorTotalPagar']).truncate_as_string(2), + '%s' % + build_vars['NitOFE'], + '%s' % + build_vars['NumAdq'], + '%s' % + build_vars['Software-PIN'], + '%d' % + build_vars['TipoAmb'], ] class DianXMLExtensionCUDS(DianXMLExtensionCUDFE): - def __init__(self, invoice, software_pin, tipo_ambiente = AMBIENTE_PRUEBAS): + def __init__(self, invoice, software_pin, tipo_ambiente=AMBIENTE_PRUEBAS): self.tipo_ambiente = tipo_ambiente self.software_pin = software_pin self.invoice = invoice @@ -317,20 +386,30 @@ class DianXMLExtensionCUDS(DianXMLExtensionCUDFE): def formatVars(self): build_vars = self.buildVars() CodImpuesto1 = build_vars['CodImpuesto1'] - CodImpuesto2 = build_vars['CodImpuesto2'] - CodImpuesto3 = build_vars['CodImpuesto3'] return [ - '%s' % build_vars['NumFac'], - '%s' % build_vars['FecFac'], - '%s' % build_vars['HoraFac'], - form.Amount(build_vars['ValorBruto']).truncate_as_string(2), + '%s' % + build_vars['NumFac'], + '%s' % + build_vars['FecFac'], + '%s' % + build_vars['HoraFac'], + form.Amount( + build_vars['ValorBruto']).truncate_as_string(2), CodImpuesto1, - form.Amount(build_vars['ValorImpuestoPara'].get(CodImpuesto1, 0.0)).truncate_as_string(2), - form.Amount(build_vars['ValorTotalPagar']).truncate_as_string(2), - '%s' % build_vars['NitOFE'], - '%s' % build_vars['NumAdq'], - '%s' % build_vars['Software-PIN'], - '%d' % build_vars['TipoAmb'], + form.Amount( + build_vars['ValorImpuestoPara'].get( + CodImpuesto1, + 0.0)).truncate_as_string(2), + form.Amount( + build_vars['ValorTotalPagar']).truncate_as_string(2), + '%s' % + build_vars['NitOFE'], + '%s' % + build_vars['NumAdq'], + '%s' % + build_vars['Software-PIN'], + '%d' % + build_vars['TipoAmb'], ] @@ -344,15 +423,20 @@ class DianXMLExtensionSoftwareProvider(FachoXMLExtension): def build(self, fexml): software_provider = fexml.fragment( - './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider') + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:SoftwareProvider') provider_id_attrs = SCHEME_AGENCY_ATTRS.copy() provider_id_attrs.update({'schemeID': self.dv}) - #DIAN 1.7.-2020: FAB23 + # DIAN 1.7.-2020: FAB23 provider_id_attrs.update({'schemeName': '31'}) - software_provider.set_element('/sts:SoftwareProvider/sts:ProviderID', self.nit, - **provider_id_attrs) - software_provider.set_element('/sts:SoftwareProvider/sts:SoftwareID', self.id_software, - **SCHEME_AGENCY_ATTRS) + software_provider.set_element( + '/sts:SoftwareProvider/sts:ProviderID', + self.nit, + **provider_id_attrs) + software_provider.set_element( + '/sts:SoftwareProvider/sts:SoftwareID', + self.id_software, + **SCHEME_AGENCY_ATTRS) class DianXMLExtensionSoftwareSecurityCode(FachoXMLExtension): @@ -364,7 +448,10 @@ class DianXMLExtensionSoftwareSecurityCode(FachoXMLExtension): self.invoice_ident = invoice_ident def build(self, fexml): - dian_path = './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareSecurityCode' + dian_path = ( + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:SoftwareSecurityCode' + ) code = str(self.id_software) + str(self.pin) + str(self.invoice_ident) m = hashlib.sha384() m.update(code.encode('utf-8')) @@ -401,8 +488,8 @@ class DianXMLExtensionSigner: xml = LXMLBuilder.from_string(document) signature = self.sign_xml_element(xml) - fachoxml = FachoXML(xml,nsmap=NAMESPACES) - #DIAN 1.7.-2020: FAB01 + fachoxml = FachoXML(xml, nsmap=NAMESPACES) + # DIAN 1.7.-2020: FAB01 extcontent = self._element_extension_content(fachoxml) fachoxml.append_element(extcontent, signature) @@ -418,8 +505,11 @@ class DianXMLExtensionSigner: xml.append(signature) ref = xmlsig.template.add_reference( - signature, xmlsig.constants.TransformSha256, uri="", name="xmldsig-%s-ref0" % (id_uuid) - ) + signature, + xmlsig.constants.TransformSha256, + uri="", + name="xmldsig-%s-ref0" % + (id_uuid)) xmlsig.template.add_transform(ref, xmlsig.constants.TransformEnveloped) id_keyinfo = "xmldsig-%s-KeyInfo" % (id_uuid) @@ -438,9 +528,8 @@ class DianXMLExtensionSigner: id_props = "xmldsig-%s-signedprops" % (id_uuid) props_ref = xmlsig.template.add_reference( - signature, xmlsig.constants.TransformSha256, uri="#%s" % (id_props), - uri_type="http://uri.etsi.org/01903#SignedProperties" - ) + signature, xmlsig.constants.TransformSha256, uri="#%s" % + (id_props), uri_type="http://uri.etsi.org/01903#SignedProperties") xmlsig.template.add_transform( props_ref, xmlsig.constants.TransformInclC14N) @@ -481,7 +570,9 @@ class DianXMLExtensionAuthorizationProvider(FachoXMLExtension): def build(self, fexml): attrs = {'schemeID': '4', 'schemeName': '31'} attrs.update(SCHEME_AGENCY_ATTRS) - authorization_provider = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider') + authorization_provider = fexml.fragment( + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:AuthorizationProvider') authorization_provider.set_element('./sts:AuthorizationProviderID', '800197268', **attrs) @@ -490,12 +581,17 @@ class DianXMLExtensionAuthorizationProvider(FachoXMLExtension): class DianXMLExtensionInvoiceSource(FachoXMLExtension): # CAB13 def build(self, fexml): - dian_path = '/fe:CreditNote/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode' + dian_path = ( + '/fe:CreditNote/ext:UBLExtensions/ext:UBLExtension/ext:ExtensionCo' + 'ntent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode' + ) fexml.set_element( dian_path, 'CO', listAgencyID="6", listAgencyName="United Nations Economic Commission for Europe", - listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1") + listSchemeURI=( + "urn:oasis:names:specification:ubl:codelist:gc:" + "CountryIdentificationCode-2.1")) class DianXMLExtensionInvoiceAuthorization(FachoXMLExtension): @@ -512,28 +608,38 @@ class DianXMLExtensionInvoiceAuthorization(FachoXMLExtension): self.to = to def build(self, fexml): - invoice_control = fexml.fragment('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceControl') - invoice_control.set_element('/sts:InvoiceControl/sts:InvoiceAuthorization', self.authorization) - invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizationPeriod/cbc:StartDate', - self.period_startdate.strftime('%Y-%m-%d')) - invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizationPeriod/cbc:EndDate', - self.period_enddate.strftime('%Y-%m-%d')) - invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizedInvoices/sts:Prefix', - self.prefix) - invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizedInvoices/sts:From', - self.from_) - invoice_control.set_element('/sts:InvoiceControl/sts:AuthorizedInvoices/sts:To', - self.to) + invoice_control = fexml.fragment( + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:InvoiceControl') + invoice_control.set_element( + '/sts:InvoiceControl/sts:InvoiceAuthorization', + self.authorization) + invoice_control.set_element( + '/sts:InvoiceControl/sts:AuthorizationPeriod/cbc:StartDate', + self.period_startdate.strftime('%Y-%m-%d')) + invoice_control.set_element( + '/sts:InvoiceControl/sts:AuthorizationPeriod/cbc:EndDate', + self.period_enddate.strftime('%Y-%m-%d')) + invoice_control.set_element( + '/sts:InvoiceControl/sts:AuthorizedInvoices/sts:Prefix', + self.prefix) + invoice_control.set_element( + '/sts:InvoiceControl/sts:AuthorizedInvoices/sts:From', self.from_) + invoice_control.set_element( + '/sts:InvoiceControl/sts:AuthorizedInvoices/sts:To', self.to) fexml.set_element( - './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource/cbc:IdentificationCode', + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:InvoiceSource/cbc:IdentificationCode', 'CO', # DIAN 1.7.-2020: FAB15 listAgencyID="6", # DIAN 1.7.-2020: FAB16 listAgencyName="United Nations Economic Commission for Europe", # DIAN 1.7.-2020: FAB17 - listSchemeURI="urn:oasis:names:specification:ubl:codelist:gc:CountryIdentificationCode-2.1") + listSchemeURI=( + "urn:oasis:names:specification:ubl:codelist:gc:" + "CountryIdentificationCode-2.1")) class DianZIP: @@ -576,7 +682,11 @@ class DianZIP: class DianXMLExtensionSignerVerifier: - def __init__(self, pkcs12_path_or_bytes, passphrase=None, localpolicy=True): + def __init__( + self, + pkcs12_path_or_bytes, + passphrase=None, + localpolicy=True): self._pkcs12_path_or_bytes = pkcs12_path_or_bytes self._passphrase = None self._localpolicy = localpolicy @@ -610,5 +720,5 @@ class DianXMLExtensionSignerVerifier: else: ctx.verify(signature) return True - except: + except BaseException: return False diff --git a/facho/fe/form/__init__.py b/facho/fe/form/__init__.py index 267b8f8..9f63473 100644 --- a/facho/fe/form/__init__.py +++ b/facho/fe/form/__init__.py @@ -5,9 +5,11 @@ # from functools import reduce # import copy +from __future__ import annotations + import dataclasses from dataclasses import dataclass, field -from datetime import datetime, date +from datetime import datetime # from collections import defaultdict import decimal from decimal import Decimal @@ -278,17 +280,6 @@ class Responsability: raise ValueError("code %s not found" % (code)) -@dataclass -class TaxScheme: - code: str - name: str = '' - - def __post_init__(self): - if self.code not in codelist.TipoImpuesto: - raise ValueError("code not found") - self.name = codelist.TipoImpuesto[self.code]['name'] - - @dataclass class Party: name: str diff --git a/facho/fe/form/query.py b/facho/fe/form/query.py index 054037d..497052c 100644 --- a/facho/fe/form/query.py +++ b/facho/fe/form/query.py @@ -6,16 +6,19 @@ from .. import form from ..fe import fe_from_string from datetime import datetime -def billing_reference(xmldocument: str, klass: form.BillingReference) -> form.BillingReference: + +def billing_reference( + xmldocument: str, + klass: form.BillingReference) -> form.BillingReference: """ construye BillingReference desde XMLDOCUMENT usando KLASS como clase. """ if not issubclass(klass, form.BillingReference): raise TypeError('klass expected subclass of BillingReference') - + fachoxml = fe_from_string(xmldocument) - + uid = fachoxml.get_element_text('./cbc:ID') uuid = fachoxml.get_element_text('./cbc:UUID') issue_date = fachoxml.get_element_text('./cbc:IssueDate') diff --git a/facho/fe/form_xml/__init__.py b/facho/fe/form_xml/__init__.py index 8eb322f..e1e9645 100644 --- a/facho/fe/form_xml/__init__.py +++ b/facho/fe/form_xml/__init__.py @@ -1,7 +1,18 @@ -from .invoice import * -from .credit_note import * -from .debit_note import * -from .utils import * -from .attached_document import * -from .support_document import * -from .support_document_credit_note import * +from .invoice import DIANInvoiceXML +from .credit_note import DIANCreditNoteXML +from .debit_note import DIANDebitNoteXML +from .utils import DIANWrite, DIANWriteSigned +from .attached_document import AttachedDocument +from .support_document import DIANSupportDocumentXML +from .support_document_credit_note import DIANSupportDocumentCreditNoteXML + +__all__ = [ + 'DIANInvoiceXML', + 'DIANCreditNoteXML', + 'DIANDebitNoteXML', + 'DIANWrite', + 'DIANWriteSigned', + 'AttachedDocument', + 'DIANSupportDocumentXML', + 'DIANSupportDocumentCreditNoteXML', +] diff --git a/facho/fe/form_xml/invoice.py b/facho/fe/form_xml/invoice.py index c009d43..60e97ec 100644 --- a/facho/fe/form_xml/invoice.py +++ b/facho/fe/form_xml/invoice.py @@ -1,363 +1,521 @@ -from .. import fe -from ..form import * from collections import defaultdict +from .. import fe +from ..form import ( + Amount, + CreditNoteDocumentReference, + DebitNoteDocumentReference, + InvoiceDocumentReference, + TaxTotalOmit, + WithholdingTaxTotalOmit, +) + __all__ = ['DIANInvoiceXML'] + class DIANInvoiceXML(fe.FeXML): """ DianInvoiceXML mapea objeto form.Invoice a XML segun lo indicado para la facturacion electronica. """ - def __init__(self, invoice, tag_document = 'Invoice'): - super().__init__(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1') - self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceControl') - self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource') - self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider') - self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareSecurityCode') - self.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider/sts:AuthorizationProviderID') - + def __init__(self, invoice, tag_document='Invoice'): + super().__init__( + tag_document, + 'http://www.dian.gov.co/contratos/facturaelectronica/v1') + self.placeholder_for( + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:InvoiceControl') + self.placeholder_for( + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:InvoiceSource') + self.placeholder_for( + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:SoftwareProvider') + self.placeholder_for( + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:SoftwareSecurityCode') + self.placeholder_for( + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/' + 'sts:DianExtensions/sts:AuthorizationProvider/' + 'sts:AuthorizationProviderID') + # ZE02 se requiere existencia para firmar - ublextension = self.fragment('./ext:UBLExtensions/ext:UBLExtension', append=True) - extcontent = ublextension.find_or_create_element('/ext:UBLExtension/ext:ExtensionContent') + ublextension = self.fragment( + './ext:UBLExtensions/ext:UBLExtension', append=True) + ublextension.find_or_create_element( + '/ext:UBLExtension/ext:ExtensionContent') self.attach_invoice(invoice) def set_supplier(fexml, invoice): fexml.placeholder_for('./cac:AccountingSupplierParty') - #DIAN 1.7.-2020: CAJ02 - #DIAN 1.7.-2020: FAJ02 - fexml.set_element('./cac:AccountingSupplierParty/cbc:AdditionalAccountID', - invoice.invoice_supplier.organization_code) + # DIAN 1.7.-2020: CAJ02 + # DIAN 1.7.-2020: FAJ02 + fexml.set_element( + './cac:AccountingSupplierParty/cbc:AdditionalAccountID', + invoice.invoice_supplier.organization_code) - #DIAN 1.7.-2020: CAJ06 - #DIAN 1.7.-2020: FAJ06 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name', - invoice.invoice_supplier.name) + # DIAN 1.7.-2020: CAJ06 + # DIAN 1.7.-2020: FAJ06 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyName/cbc:Name', + invoice.invoice_supplier.name) - #DIAN 1.7.-2020: CAJ07, CAJ08 - #DIAN 1.7.-2020: FAJ07 - fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address') + # DIAN 1.7.-2020: CAJ07, CAJ08 + # DIAN 1.7.-2020: FAJ07 + fexml.placeholder_for( + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address') - #DIAN 1.7.-2020: FAJ08 - #DIAN 1.7.-2020: CAJ09 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID', - invoice.invoice_supplier.address.city.code) - #DIAN 1.7.-2020: FAJ09 - #DIAN 1.7.-2020: CAJ10 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName', - invoice.invoice_supplier.address.city.name) - #DIAN 1.7.-2020: FAJ11 - #DIAN 1.7.-2020: CAJ11 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity', - invoice.invoice_supplier.address.countrysubentity.name) + # DIAN 1.7.-2020: FAJ08 + # DIAN 1.7.-2020: CAJ09 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:ID', + invoice.invoice_supplier.address.city.code) + # DIAN 1.7.-2020: FAJ09 + # DIAN 1.7.-2020: CAJ10 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:CityName', + invoice.invoice_supplier.address.city.name) + # DIAN 1.7.-2020: FAJ11 + # DIAN 1.7.-2020: CAJ11 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:CountrySubentity', + invoice.invoice_supplier.address.countrysubentity.name) - #DIAN 1.7.-2020: FAJ12 - #DIAN 1.7.-2020: CAJ12 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode', - invoice.invoice_supplier.address.countrysubentity.code) - #DIAN 1.7.-2020: FAJ14 - #DIAN 1.7.-2020: CAJ13, CAJ14 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line', - invoice.invoice_supplier.address.street) + # DIAN 1.7.-2020: FAJ12 + # DIAN 1.7.-2020: CAJ12 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:CountrySubentityCode', + invoice.invoice_supplier.address.countrysubentity.code) + # DIAN 1.7.-2020: FAJ14 + # DIAN 1.7.-2020: CAJ13, CAJ14 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cac:AddressLine/cbc:Line', + invoice.invoice_supplier.address.street) - #DIAN 1.7.-2020: FAJ16 - #DIAN 1.7.-2020: CAJ16, CAJ16 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode', - invoice.invoice_supplier.address.country.code) + # DIAN 1.7.-2020: FAJ16 + # DIAN 1.7.-2020: CAJ16, CAJ16 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cac:Country/cbc:IdentificationCode', + invoice.invoice_supplier.address.country.code) - #DIAN 1.7.-2020: FAJ17 - #DIAN 1.7.-2020: CAJ17 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name', - invoice.invoice_supplier.address.country.name, - #DIAN 1.7.-2020: FAJ18 - languageID = 'es') + # DIAN 1.7.-2020: FAJ17 + # DIAN 1.7.-2020: CAJ17 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cac:Country/cbc:Name', + invoice.invoice_supplier.address.country.name, + # DIAN 1.7.-2020: FAJ18 + languageID='es') supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy() - supplier_company_id_attrs.update({'schemeID': invoice.invoice_supplier.ident.dv, - 'schemeName': invoice.invoice_supplier.ident.type_fiscal}) + supplier_company_id_attrs.update( + { + 'schemeID': invoice.invoice_supplier.ident.dv, + 'schemeName': invoice.invoice_supplier.ident.type_fiscal}) - #DIAN 1.7.-2020: FAJ19 - #DIAN 1.7.-2020: CAJ19 - fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme') + # DIAN 1.7.-2020: FAJ19 + # DIAN 1.7.-2020: CAJ19 + fexml.placeholder_for( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme') - #DIAN 1.7.-2020: FAJ20 - #DIAN 1.7.-2020: CAJ20 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName', - invoice.invoice_supplier.legal_name) + # DIAN 1.7.-2020: FAJ20 + # DIAN 1.7.-2020: CAJ20 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc' + ':RegistrationName', + invoice.invoice_supplier.legal_name) - #DIAN 1.7.-2020: FAJ21 - #DIAN 1.7.-2020: CAJ21 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', - invoice.invoice_supplier.ident, - #DIAN 1.7.-2020: FAJ22,FAJ23,FAJ24,FAJ25 - **supplier_company_id_attrs) - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode', - #DIAN 1.7.-2020: FAJ26 - #DIAN 1.7.-2020: CAJ26 - invoice.invoice_supplier.responsability_code, - #DIAN 1.7.-2020: FAJ27 - #DIAN 1.7.-2020: CAJ27 - listName=invoice.invoice_supplier.responsability_regime_code) - #DIAN 1.7.-2020: FAJ28 - #DIAN 1.7.-2020: CAJ28 - fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress') + # DIAN 1.7.-2020: FAJ21 + # DIAN 1.7.-2020: CAJ21 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc' + ':CompanyID', + invoice.invoice_supplier.ident, + # DIAN 1.7.-2020: FAJ22,FAJ23,FAJ24,FAJ25 + **supplier_company_id_attrs) + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc' + ':TaxLevelCode', + # DIAN 1.7.-2020: FAJ26 + # DIAN 1.7.-2020: CAJ26 + invoice.invoice_supplier.responsability_code, + # DIAN 1.7.-2020: FAJ27 + # DIAN 1.7.-2020: CAJ27 + listName=invoice.invoice_supplier.responsability_regime_code) + # DIAN 1.7.-2020: FAJ28 + # DIAN 1.7.-2020: CAJ28 + fexml.placeholder_for( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress') - #DIAN 1.7.-2020: FAJ29 - #DIAN 1.7.-2020: CAJ29 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID', - invoice.invoice_supplier.address.city.code) + # DIAN 1.7.-2020: FAJ29 + # DIAN 1.7.-2020: CAJ29 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cbc:ID', + invoice.invoice_supplier.address.city.code) - #DIAN 1.7.-2020: FAJ30 - #DIAN 1.7.-2020: CAJ30 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName', invoice.invoice_supplier.address.city.name) + # DIAN 1.7.-2020: FAJ30 + # DIAN 1.7.-2020: CAJ30 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cbc:CityName', + invoice.invoice_supplier.address.city.name) - #DIAN 1.7.-2020: FAJ31 - #DIAN 1.7.-2020: CAJ31 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity', - invoice.invoice_supplier.address.countrysubentity.name) + # DIAN 1.7.-2020: FAJ31 + # DIAN 1.7.-2020: CAJ31 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cbc:CountrySubentity', + invoice.invoice_supplier.address.countrysubentity.name) - #DIAN 1.7.-2020: FAJ32 - #DIAN 1.7.-2020: CAJ32 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode', - invoice.invoice_supplier.address.countrysubentity.code) + # DIAN 1.7.-2020: FAJ32 + # DIAN 1.7.-2020: CAJ32 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cbc:CountrySubentityCode', + invoice.invoice_supplier.address.countrysubentity.code) - #DIAN 1.7.-2020: FAJ33,FAJ34 - #DIAN 1.7.-2020: CAJ33,CAJ34 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line', - invoice.invoice_supplier.address.street) + # DIAN 1.7.-2020: FAJ33,FAJ34 + # DIAN 1.7.-2020: CAJ33,CAJ34 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cac:AddressLine/cbc:Line', + invoice.invoice_supplier.address.street) - #DIAN 1.7.-2020: FAJ35,FAJ36 - #DIAN 1.7.-2020: CAJ35,CAJ36 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode', - invoice.invoice_supplier.address.country.code) + # DIAN 1.7.-2020: FAJ35,FAJ36 + # DIAN 1.7.-2020: CAJ35,CAJ36 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cac:Country/cbc:IdentificationCode', + invoice.invoice_supplier.address.country.code) - #DIAN 1.7.-2020: FAJ37,FAJ38 - #DIAN 1.7.-2020: CAJ37,CAJ38 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name', - invoice.invoice_supplier.address.country.name, - languageID='es') + # DIAN 1.7.-2020: FAJ37,FAJ38 + # DIAN 1.7.-2020: CAJ37,CAJ38 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cac:Country/cbc:Name', + invoice.invoice_supplier.address.country.name, + languageID='es') - #DIAN 1.7.-2020: FAJ39 - #DIAN 1.7.-2020: CAJ39 - fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme') + # DIAN 1.7.-2020: FAJ39 + # DIAN 1.7.-2020: CAJ39 + fexml.placeholder_for( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme') - #DIAN 1.7.-2020: CAJ40 - #DIAN 1.7.-2020: FAJ40 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID', - invoice.invoice_customer.tax_scheme.code) + # DIAN 1.7.-2020: CAJ40 + # DIAN 1.7.-2020: FAJ40 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme/cbc:ID', + invoice.invoice_customer.tax_scheme.code) - #DIAN 1.7.-2020: CAJ41 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name', - invoice.invoice_customer.tax_scheme.name) + # DIAN 1.7.-2020: CAJ41 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme/cbc:Name', + invoice.invoice_customer.tax_scheme.name) - #DIAN 1.7.-2020: FAJ42 - #DIAN 1.7.-2020: CAJ42 - fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity') + # DIAN 1.7.-2020: FAJ42 + # DIAN 1.7.-2020: CAJ42 + fexml.placeholder_for( + './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity') - #DIAN 1.7.-2020: FAJ43 - #DIAN 1.7.-2020: CAJ43 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName', - invoice.invoice_supplier.legal_name) + # DIAN 1.7.-2020: FAJ43 + # DIAN 1.7.-2020: CAJ43 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc' + ':RegistrationName', + invoice.invoice_supplier.legal_name) - #DIAN 1.7.-2020: FAJ44,FAJ45,FAJ46,FAJ47,FAJ48 - #DIAN 1.7.-2020: CAJ44,CAJ45,CAJ46,CAJ47,CAJ48 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID', - invoice.invoice_supplier.ident, - **supplier_company_id_attrs) + # DIAN 1.7.-2020: FAJ44,FAJ45,FAJ46,FAJ47,FAJ48 + # DIAN 1.7.-2020: CAJ44,CAJ45,CAJ46,CAJ47,CAJ48 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cbc' + ':CompanyID', + invoice.invoice_supplier.ident, + **supplier_company_id_attrs) - #DIAN 1.7.-2020: FAJ49 - #DIAN 1.7.-2020: CAJ49 - fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme') + # DIAN 1.7.-2020: FAJ49 + # DIAN 1.7.-2020: CAJ49 + fexml.placeholder_for( + './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac' + ':CorporateRegistrationScheme') - #DIAN 1.7.-2020: FAJ50 - #DIAN 1.7.-2020: CAJ50 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac:CorporateRegistrationScheme/cbc:ID', - invoice.invoice_ident_prefix) + # DIAN 1.7.-2020: FAJ50 + # DIAN 1.7.-2020: CAJ50 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PartyLegalEntity/cac' + ':CorporateRegistrationScheme/cbc:ID', + invoice.invoice_ident_prefix) - #DIAN 1.7.-2020: CAJ67 - fexml.placeholder_for('./cac:AccountingSupplierParty/cac:Party/cac:Contact') - - #DIAN 1.7.-2020: FAJ71 - #DIAN 1.7.-2020: CAJ71 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc:ElectronicMail', - invoice.invoice_supplier.email) + # DIAN 1.7.-2020: CAJ67 + fexml.placeholder_for( + './cac:AccountingSupplierParty/cac:Party/cac:Contact') + # DIAN 1.7.-2020: FAJ71 + # DIAN 1.7.-2020: CAJ71 + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:Contact/cbc' + ':ElectronicMail', + invoice.invoice_supplier.email) def set_customer(fexml, invoice): fexml.placeholder_for('./cac:AccountingCustomerParty') - fexml.set_element('./cac:AccountingCustomerParty/cbc:AdditionalAccountID', - invoice.invoice_customer.organization_code) - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification/cbc:ID', - invoice.invoice_customer.ident) - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name', - invoice.invoice_customer.name) + fexml.set_element( + './cac:AccountingCustomerParty/cbc:AdditionalAccountID', + invoice.invoice_customer.organization_code) + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyIdentification' + '/cbc:ID', + invoice.invoice_customer.ident) + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyName/cbc:Name', + invoice.invoice_customer.name) - fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation') + fexml.placeholder_for( + './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation') customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy() - #DIAN 1.7.-2020: FAK25 - #DIAN 1.7.-2020: CAK25 - customer_company_id_attrs.update({'schemeID': invoice.invoice_customer.ident.dv, - 'schemeName': invoice.invoice_customer.ident.type_fiscal}) + # DIAN 1.7.-2020: FAK25 + # DIAN 1.7.-2020: CAK25 + customer_company_id_attrs.update( + { + 'schemeID': invoice.invoice_customer.ident.dv, + 'schemeName': invoice.invoice_customer.ident.type_fiscal}) - #DIAN 1.7.-2020: FAK07 - #DIAN 1.7.-2020: CAK07 - fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address') + # DIAN 1.7.-2020: FAK07 + # DIAN 1.7.-2020: CAK07 + fexml.placeholder_for( + './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac' + ':Address') - #DIAN 1.7.-2020: FAK08 - #DIAN 1.7.-2020: CAK08 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID', - invoice.invoice_customer.address.city.code) - #DIAN 1.7.-2020: FAK09 - #DIAN 1.7.-2020: CAK09 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName', invoice.invoice_customer.address.city.name) + # DIAN 1.7.-2020: FAK08 + # DIAN 1.7.-2020: CAK08 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:ID', + invoice.invoice_customer.address.city.code) + # DIAN 1.7.-2020: FAK09 + # DIAN 1.7.-2020: CAK09 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:CityName', + invoice.invoice_customer.address.city.name) - #DIAN 1.7.-2020: FAK11 - #DIAN 1.7.-2020: CAK11 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity', - invoice.invoice_customer.address.countrysubentity.name) + # DIAN 1.7.-2020: FAK11 + # DIAN 1.7.-2020: CAK11 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:CountrySubentity', + invoice.invoice_customer.address.countrysubentity.name) - #DIAN 1.7.-2020: FAK12 - #DIAN 1.7.-2020: CAK12 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode', - invoice.invoice_customer.address.countrysubentity.code) + # DIAN 1.7.-2020: FAK12 + # DIAN 1.7.-2020: CAK12 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:CountrySubentityCode', + invoice.invoice_customer.address.countrysubentity.code) - #DIAN 1.7.-2020: CAK13, CAK14 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line', - invoice.invoice_customer.address.street) + # DIAN 1.7.-2020: CAK13, CAK14 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cac:AddressLine/cbc:Line', + invoice.invoice_customer.address.street) - #DIAN 1.7.-2020: CAK16 - #DIAN 1.7.-2020: FAK16 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode', - invoice.invoice_customer.address.country.code) + # DIAN 1.7.-2020: CAK16 + # DIAN 1.7.-2020: FAK16 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cac:Country/cbc:IdentificationCode', + invoice.invoice_customer.address.country.code) - #DIAN 1.7.-2020: FAK17 - #DIAN 1.7.-2020: CAK17 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name', - invoice.invoice_customer.address.country.name, - #DIAN 1.7.-2020: FAK18 - #DIAN 1.7.-2020: CAK18 - languageID='es') + # DIAN 1.7.-2020: FAK17 + # DIAN 1.7.-2020: CAK17 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cac:Country/cbc:Name', + invoice.invoice_customer.address.country.name, + # DIAN 1.7.-2020: FAK18 + # DIAN 1.7.-2020: CAK18 + languageID='es') - #DIAN 1.7.-2020: FAK17,FAK19 - #DIAN 1.7.-2020: CAK19 - fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme') + # DIAN 1.7.-2020: FAK17,FAK19 + # DIAN 1.7.-2020: CAK19 + fexml.placeholder_for( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme') - #DIAN 1.7.-2020: FAK17,FAK20 - #DIAN 1.7.-2020: CAK20 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName', - invoice.invoice_customer.legal_name) + # DIAN 1.7.-2020: FAK17,FAK20 + # DIAN 1.7.-2020: CAK20 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc' + ':RegistrationName', + invoice.invoice_customer.legal_name) - #DIAN 1.7.-2020: CAK21 - #DIAN 1.7.-2020: FAK21 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', - invoice.invoice_customer.ident, - #DIAN 1.7.-2020: CAK22, CAK23, CAK24, CAK25 - **customer_company_id_attrs) + # DIAN 1.7.-2020: CAK21 + # DIAN 1.7.-2020: FAK21 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc' + ':CompanyID', + invoice.invoice_customer.ident, + # DIAN 1.7.-2020: CAK22, CAK23, CAK24, CAK25 + **customer_company_id_attrs) - #DIAN 1.7.-2020: CAK26 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode', - #DIAN 1.7.-2020: FAK26 - invoice.invoice_customer.responsability_code, - #DIAN 1.7.-2020: FAK27 - #DIAN 1.7.-2020: CAK27 - listName=invoice.invoice_customer.responsability_regime_code) + # DIAN 1.7.-2020: CAK26 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc' + ':TaxLevelCode', + # DIAN 1.7.-2020: FAK26 + invoice.invoice_customer.responsability_code, + # DIAN 1.7.-2020: FAK27 + # DIAN 1.7.-2020: CAK27 + listName=invoice.invoice_customer.responsability_regime_code) - #DIAN 1.7.-2020: FAK28 - #DIAN 1.7.-2020: CAK28 - fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress') + # DIAN 1.7.-2020: FAK28 + # DIAN 1.7.-2020: CAK28 + fexml.placeholder_for( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress') - #DIAN 1.7.-2020: FAK29 - #DIAN 1.7.-2020: CAK29 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:ID', - invoice.invoice_customer.address.city.code) + # DIAN 1.7.-2020: FAK29 + # DIAN 1.7.-2020: CAK29 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cbc:ID', + invoice.invoice_customer.address.city.code) - #DIAN 1.7.-2020: FAK30 - #DIAN 1.7.-2020: CAK30 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CityName', - invoice.invoice_customer.address.city.name) + # DIAN 1.7.-2020: FAK30 + # DIAN 1.7.-2020: CAK30 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cbc:CityName', + invoice.invoice_customer.address.city.name) - #DIAN 1.7.-2020: FAK31 - #DIAN 1.7.-2020: CAK31 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentity', - invoice.invoice_customer.address.countrysubentity.name) + # DIAN 1.7.-2020: FAK31 + # DIAN 1.7.-2020: CAK31 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cbc:CountrySubentity', + invoice.invoice_customer.address.countrysubentity.name) - #DIAN 1.7.-2020: FAK32 - #DIAN 1.7.-2020: CAK32 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cbc:CountrySubentityCode', - invoice.invoice_customer.address.countrysubentity.code) + # DIAN 1.7.-2020: FAK32 + # DIAN 1.7.-2020: CAK32 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cbc:CountrySubentityCode', + invoice.invoice_customer.address.countrysubentity.code) - #DIAN 1.7.-2020: FAK33 - #DIAN 1.7.-2020: CAK33 - fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine') + # DIAN 1.7.-2020: FAK33 + # DIAN 1.7.-2020: CAK33 + fexml.placeholder_for( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cac:AddressLine') - #DIAN 1.7.-2020: FAK34 - #DIAN 1.7.-2020: CAK34 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:AddressLine/cbc:Line', - invoice.invoice_customer.address.street) + # DIAN 1.7.-2020: FAK34 + # DIAN 1.7.-2020: CAK34 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cac:AddressLine/cbc:Line', + invoice.invoice_customer.address.street) - #DIAN 1.7.-2020: CAK35 - #DIAN 1.7.-2020: FAK35 - fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country') + # DIAN 1.7.-2020: CAK35 + # DIAN 1.7.-2020: FAK35 + fexml.placeholder_for( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cac:Country') - #DIAN 1.7.-2020: CAK36 - #DIAN 1.7.-2020: FAK36 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode', - invoice.invoice_customer.address.country.code) + # DIAN 1.7.-2020: CAK36 + # DIAN 1.7.-2020: FAK36 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cac:Country/cbc:IdentificationCode', + invoice.invoice_customer.address.country.code) - #DIAN 1.7.-2020: CAK37 - #DIAN 1.7.-2020: FAK37 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:Name', invoice.invoice_customer.address.country.name) + # DIAN 1.7.-2020: CAK37 + # DIAN 1.7.-2020: FAK37 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cac:Country/cbc:Name', + invoice.invoice_customer.address.country.name) - #DIAN 1.7.-2020: FAK38 - #DIAN 1.7.-2020: CAK38 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:RegistrationAddress/cac:Country/cbc:IdentificationCode', - invoice.invoice_customer.address.country.code, - languageID='es') + # DIAN 1.7.-2020: FAK38 + # DIAN 1.7.-2020: CAK38 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':RegistrationAddress/cac:Country/cbc:IdentificationCode', + invoice.invoice_customer.address.country.code, + languageID='es') - #DIAN 1.7.-2020: CAK39 - #DIAN 1.7.-2020: FAK39 - fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme') + # DIAN 1.7.-2020: CAK39 + # DIAN 1.7.-2020: FAK39 + fexml.placeholder_for( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme') - #DIAN 1.7.-2020: CAK40 Machete Construir Validación - #DIAN 1.7.-2020: FAK40 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID', - invoice.invoice_customer.tax_scheme.code) + # DIAN 1.7.-2020: CAK40 Machete Construir Validación + # DIAN 1.7.-2020: FAK40 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme/cbc:ID', + invoice.invoice_customer.tax_scheme.code) - #DIAN 1.7.-2020: FAK41 - #DIAN 1.7.-2020: CAK41 Machete Construir Validación - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name', - invoice.invoice_customer.tax_scheme.name) - #DIAN 1.7.-2020: FAK42 - #DIAN 1.7.-2020: CAK42 - fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity') + # DIAN 1.7.-2020: FAK41 + # DIAN 1.7.-2020: CAK41 Machete Construir Validación + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme/cbc:Name', + invoice.invoice_customer.tax_scheme.name) + # DIAN 1.7.-2020: FAK42 + # DIAN 1.7.-2020: CAK42 + fexml.placeholder_for( + './cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity') - #DIAN 1.7.-2020: FAK43 - #DIAN 1.7.-2020: CAK43 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:RegistrationName', - invoice.invoice_customer.legal_name) + # DIAN 1.7.-2020: FAK43 + # DIAN 1.7.-2020: CAK43 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc' + ':RegistrationName', + invoice.invoice_customer.legal_name) - #DIAN 1.7.-2020: CAK44 - #DIAN 1.7.-2020: FAK44,FAK45,FAK46,FAK47,FAK48 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc:CompanyID', - invoice.invoice_customer.ident, - **customer_company_id_attrs) + # DIAN 1.7.-2020: CAK44 + # DIAN 1.7.-2020: FAK44,FAK45,FAK46,FAK47,FAK48 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity/cbc' + ':CompanyID', + invoice.invoice_customer.ident, + **customer_company_id_attrs) - fexml.placeholder_for('./cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity') - - #DIAN 1.7.-2020: FAK55 - #DIAN 1.7.-2020: CAK51, CAK55 - fexml.set_element('./cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc:ElectronicMail', - invoice.invoice_customer.email) + fexml.placeholder_for( + './cac:AccountingCustomerParty/cac:Party/cac:PartyLegalEntity') + # DIAN 1.7.-2020: FAK55 + # DIAN 1.7.-2020: CAK51, CAK55 + fexml.set_element( + './cac:AccountingCustomerParty/cac:Party/cac:Contact/cbc' + ':ElectronicMail', + invoice.invoice_customer.email) def set_payment_mean(fexml, invoice): payment_mean = invoice.invoice_payment_mean fexml.set_element('./cac:PaymentMeans/cbc:ID', payment_mean.id) - fexml.set_element('./cac:PaymentMeans/cbc:PaymentMeansCode', payment_mean.code) - fexml.set_element('./cac:PaymentMeans/cbc:PaymentDueDate', payment_mean.due_at.strftime('%Y-%m-%d')) - fexml.set_element('./cac:PaymentMeans/cbc:PaymentID', payment_mean.payment_id) + fexml.set_element( + './cac:PaymentMeans/cbc:PaymentMeansCode', + payment_mean.code) + fexml.set_element( + './cac:PaymentMeans/cbc:PaymentDueDate', + payment_mean.due_at.strftime('%Y-%m-%d')) + fexml.set_element( + './cac:PaymentMeans/cbc:PaymentID', + payment_mean.payment_id) def set_element_amount_for(fexml, xml, xpath, amount): if not isinstance(amount, Amount): @@ -372,39 +530,49 @@ class DIANInvoiceXML(fe.FeXML): fexml.set_element(xpath, amount, currencyID=amount.currency.code) def set_legal_monetary(fexml, invoice): - fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:LineExtensionAmount', - invoice.invoice_legal_monetary_total.line_extension_amount) + fexml.set_element_amount( + './cac:LegalMonetaryTotal/cbc:LineExtensionAmount', + invoice.invoice_legal_monetary_total.line_extension_amount) - fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount', - invoice.invoice_legal_monetary_total.tax_exclusive_amount) + fexml.set_element_amount( + './cac:LegalMonetaryTotal/cbc:TaxExclusiveAmount', + invoice.invoice_legal_monetary_total.tax_exclusive_amount) - fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount', - invoice.invoice_legal_monetary_total.tax_inclusive_amount) + fexml.set_element_amount( + './cac:LegalMonetaryTotal/cbc:TaxInclusiveAmount', + invoice.invoice_legal_monetary_total.tax_inclusive_amount) - fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:ChargeTotalAmount', - invoice.invoice_legal_monetary_total.charge_total_amount) - - fexml.set_element_amount('./cac:LegalMonetaryTotal/cbc:PayableAmount', - invoice.invoice_legal_monetary_total.payable_amount) + fexml.set_element_amount( + './cac:LegalMonetaryTotal/cbc:ChargeTotalAmount', + invoice.invoice_legal_monetary_total.charge_total_amount) + fexml.set_element_amount( + './cac:LegalMonetaryTotal/cbc:PayableAmount', + invoice.invoice_legal_monetary_total.payable_amount) def _set_invoice_document_reference(fexml, reference): - fexml._do_set_billing_reference(reference, 'cac:InvoiceDocumentReference') + fexml._do_set_billing_reference( + reference, 'cac:InvoiceDocumentReference') def _set_credit_note_document_reference(fexml, reference): - fexml._do_set_billing_reference(reference, 'cac:CreditNoteDocumentReference') + fexml._do_set_billing_reference( + reference, 'cac:CreditNoteDocumentReference') def _set_debit_note_document_reference(fexml, reference): - fexml._do_set_billing_reference(reference, 'cac:DebitNoteDocumentReference') + fexml._do_set_billing_reference( + reference, 'cac:DebitNoteDocumentReference') def _do_set_billing_reference(fexml, reference, tag_document): - fexml.set_element('./cac:BillingReference/%s/cbc:ID' %(tag_document), + fexml.set_element('./cac:BillingReference/%s/cbc:ID' % (tag_document), reference.ident) - fexml.set_element('./cac:BillingReference/cac:InvoiceDocumentReference/cbc:UUID', - reference.uuid, - schemeName='CUFE-SHA384') - fexml.set_element('./cac:BillingReference/cac:InvoiceDocumentReference/cbc:IssueDate', - reference.date.strftime("%Y-%m-%d")) + fexml.set_element( + './cac:BillingReference/cac:InvoiceDocumentReference/cbc:UUID', + reference.uuid, + schemeName='CUFE-SHA384') + fexml.set_element( + './cac:BillingReference/cac:InvoiceDocumentReference/' + 'cbc:IssueDate', + reference.date.strftime("%Y-%m-%d")) def set_billing_reference(fexml, invoice): reference = invoice.invoice_billing_reference @@ -421,17 +589,18 @@ class DIANInvoiceXML(fe.FeXML): def set_invoice_totals(fexml, invoice): tax_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0))) - withholding_amount_for = defaultdict(lambda: defaultdict(lambda: Amount(0.0))) + withholding_amount_for = defaultdict( + lambda: defaultdict(lambda: Amount(0.0))) percent_for = defaultdict(lambda: None) - #requeridos para CUFE - #tax_amount_for['01']['tax_amount'] = Amount(0.0) - #tax_amount_for['01']['taxable_amount'] = Amount(0.0) - #DIAN 1.7.-2020: FAS07 => Se debe construir estrategia para su manejo - #tax_amount_for['04']['tax_amount'] += 0.0 - #tax_amount_for['04']['taxable_amount'] += 0.0 - #tax_amount_for['03']['tax_amount'] += 0.0 - #tax_amount_for['03']['taxable_amount'] += 0.0 + # requeridos para CUFE + # tax_amount_for['01']['tax_amount'] = Amount(0.0) + # tax_amount_for['01']['taxable_amount'] = Amount(0.0) + # DIAN 1.7.-2020: FAS07 => Se debe construir estrategia para su manejo + # tax_amount_for['04']['tax_amount'] += 0.0 + # tax_amount_for['04']['taxable_amount'] += 0.0 + # tax_amount_for['03']['tax_amount'] += 0.0 + # tax_amount_for['03']['taxable_amount'] += 0.0 total_tax_amount = Amount(0.0) total_withholding_amount = Amount(0.0) @@ -439,9 +608,12 @@ class DIANInvoiceXML(fe.FeXML): for invoice_line in invoice.invoice_lines: for subtotal in invoice_line.tax.subtotals: if subtotal.scheme is not None: - tax_amount_for[subtotal.scheme.code]['tax_amount'] += subtotal.tax_amount - tax_amount_for[subtotal.scheme.code]['taxable_amount'] += invoice_line.taxable_amount - tax_amount_for[subtotal.scheme.code]['name'] = subtotal.scheme.name + scheme = subtotal.scheme.code + tax_amount_for[scheme][ + 'tax_amount'] += subtotal.tax_amount + tax_amount_for[scheme][ + 'taxable_amount'] += invoice_line.taxable_amount + tax_amount_for[scheme]['name'] = subtotal.scheme.name # MACHETE ojo InvoiceLine.tax pasar a Invoice percent_for[subtotal.scheme.code] = subtotal.percent @@ -450,93 +622,113 @@ class DIANInvoiceXML(fe.FeXML): for subtotal_withholding in invoice_line.withholding.subtotals: if subtotal_withholding.scheme is not None: - withholding_amount_for[subtotal_withholding.scheme.code]['tax_amount'] += subtotal_withholding.tax_amount - withholding_amount_for[subtotal_withholding.scheme.code]['taxable_amount'] += invoice_line.withholding_taxable_amount + scheme = subtotal_withholding.scheme.code + withholding_amount_for[scheme][ + 'tax_amount'] += subtotal_withholding.tax_amount + withholding_amount_for[scheme][ + 'taxable_amount'] += ( + invoice_line.withholding_taxable_amount) # MACHETE ojo InvoiceLine.tax pasar a Invoice - - percent_for[subtotal_withholding.scheme.code] = subtotal_withholding.percent + + percent_for[scheme] = subtotal_withholding.percent total_withholding_amount += subtotal_withholding.tax_amount if total_tax_amount != Amount(0.0): fexml.placeholder_for('./cac:TaxTotal') fexml.set_element_amount('./cac:TaxTotal/cbc:TaxAmount', - total_tax_amount) - + total_tax_amount) + for index, item in enumerate(tax_amount_for.items()): cod_impuesto, amount_of = item next_append = index > 0 - #DIAN 1.7.-2020: FAS01 + # DIAN 1.7.-2020: FAS01 line = fexml.fragment('./cac:TaxTotal', append=next_append) - #DIAN 1.7.-2020: FAU06 + # DIAN 1.7.-2020: FAU06 tax_amount = amount_of['tax_amount'] fexml.set_element_amount_for(line, '/cac:TaxTotal/cbc:TaxAmount', tax_amount) - #DIAN 1.7.-2020: FAS05 - fexml.set_element_amount_for(line, - '/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount', - amount_of['taxable_amount']) + # DIAN 1.7.-2020: FAS05 + fexml.set_element_amount_for( + line, + '/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount', + amount_of['taxable_amount']) - #DIAN 1.7.-2020: FAU06 - fexml.set_element_amount_for(line, - '/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', - amount_of['tax_amount']) + # DIAN 1.7.-2020: FAU06 + fexml.set_element_amount_for( + line, + '/cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', + amount_of['tax_amount']) - #DIAN 1.7.-2020: FAS07 + # DIAN 1.7.-2020: FAS07 if percent_for[cod_impuesto]: line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cbc:Percent', percent_for[cod_impuesto]) - if percent_for[cod_impuesto]: - line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', - percent_for[cod_impuesto]) - - line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', - cod_impuesto) - line.set_element('/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', - amount_of['name']) - + line.set_element( + '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/' + 'cbc:Percent', + percent_for[cod_impuesto]) + + line.set_element( + '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme' + '/cbc:ID', + cod_impuesto) + line.set_element( + '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme' + '/cbc:Name', + amount_of['name']) + for index, item in enumerate(withholding_amount_for.items()): cod_impuesto, amount_of = item next_append = index > 0 - #DIAN 1.7.-2020: FAS01 - line = fexml.fragment('./cac:WithholdingTaxTotal', append=next_append) - #DIAN 1.7.-2020: FAU06 + # DIAN 1.7.-2020: FAS01 + line = fexml.fragment( + './cac:WithholdingTaxTotal', + append=next_append) + # DIAN 1.7.-2020: FAU06 tax_amount = amount_of['tax_amount'] - fexml.set_element_amount_for(line, - '/cac:WithholdingTaxTotal/cbc:TaxAmount', - tax_amount) + fexml.set_element_amount_for( + line, '/cac:WithholdingTaxTotal/cbc:TaxAmount', tax_amount) - #DIAN 1.7.-2020: FAS05 - fexml.set_element_amount_for(line, - '/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount', - amount_of['taxable_amount']) + # DIAN 1.7.-2020: FAS05 + fexml.set_element_amount_for( + line, + '/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount', + amount_of['taxable_amount']) - #DIAN 1.7.-2020: FAU06 - fexml.set_element_amount_for(line, - '/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount', - amount_of['tax_amount']) + # DIAN 1.7.-2020: FAU06 + fexml.set_element_amount_for( + line, + '/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount', + amount_of['tax_amount']) - #DIAN 1.7.-2020: FAS07 + # DIAN 1.7.-2020: FAS07 if percent_for[cod_impuesto]: - line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:Percent', - percent_for[cod_impuesto]) - + line.set_element( + '/cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:Percent', + percent_for[cod_impuesto]) if percent_for[cod_impuesto]: - line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', - percent_for[cod_impuesto]) - - line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', - cod_impuesto) - line.set_element('/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', - 'ReteRenta') + line.set_element( + '/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory' + '/cbc:Percent', + percent_for[cod_impuesto]) + + line.set_element( + '/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac' + ':TaxScheme/cbc:ID', + cod_impuesto) + line.set_element( + '/cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac' + ':TaxScheme/cbc:Name', + 'ReteRenta') # abstract method def tag_document(fexml): @@ -551,51 +743,88 @@ class DIANInvoiceXML(fe.FeXML): './cac:TaxTotal/cbc:TaxAmount', invoice_line.tax_amount) - #DIAN 1.7.-2020: FAX05 - fexml.set_element_amount_for(line, - './cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount', - invoice_line.taxable_amount) + # DIAN 1.7.-2020: FAX05 + fexml.set_element_amount_for( + line, + './cac:TaxTotal/cac:TaxSubtotal/cbc:TaxableAmount', + invoice_line.taxable_amount) for subtotal in invoice_line.tax.subtotals: - line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP') + line.set_element( + './cac:TaxTotal/cac:TaxSubtotal/cbc:TaxAmount', + subtotal.tax_amount, + currencyID='COP') if subtotal.percent is not None: - line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', '%0.2f' % round(subtotal.percent, 2)) + line.set_element( + './cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc' + ':Percent', + '%0.2f' % + round( + subtotal.percent, + 2)) if subtotal.scheme is not None: - #DIAN 1.7.-2020: FAX15 - line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code) - line.set_element('./cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name) - + # DIAN 1.7.-2020: FAX15 + line.set_element( + './cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac' + ':TaxScheme/cbc:ID', + subtotal.scheme.code) + line.set_element( + './cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac' + ':TaxScheme/cbc:Name', + subtotal.scheme.name) def set_invoice_line_withholding(fexml, line, invoice_line): fexml.set_element_amount_for(line, './cac:WithholdingTaxTotal/cbc:TaxAmount', invoice_line.withholding_amount) - #DIAN 1.7.-2020: FAX05 - fexml.set_element_amount_for(line, - './cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount', - invoice_line.withholding_taxable_amount) + # DIAN 1.7.-2020: FAX05 + fexml.set_element_amount_for( + line, + './cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxableAmount', + invoice_line.withholding_taxable_amount) for subtotal in invoice_line.withholding.subtotals: - line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount', subtotal.tax_amount, currencyID='COP') + line.set_element( + './cac:WithholdingTaxTotal/cac:TaxSubtotal/cbc:TaxAmount', + subtotal.tax_amount, + currencyID='COP') if subtotal.percent is not None: - line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', '%0.2f' % round(subtotal.percent, 2)) + line.set_element( + './cac:WithholdingTaxTotal/cac:TaxSubtotal/cac' + ':TaxCategory/cbc:Percent', + '%0.2f' % + round( + subtotal.percent, + 2)) if subtotal.scheme is not None: - #DIAN 1.7.-2020: FAX15 - line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code) - line.set_element('./cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name) + # DIAN 1.7.-2020: FAX15 + line.set_element( + './cac:WithholdingTaxTotal/cac:TaxSubtotal/cac' + ':TaxCategory/cac:TaxScheme/cbc:ID', + subtotal.scheme.code) + line.set_element( + './cac:WithholdingTaxTotal/cac:TaxSubtotal/cac' + ':TaxCategory/cac:TaxScheme/cbc:Name', + subtotal.scheme.name) - def set_invoice_lines(fexml, invoice): next_append = False for index, invoice_line in enumerate(invoice.invoice_lines): - line = fexml.fragment('./cac:%sLine' % (fexml.tag_document()), append=next_append) + line = fexml.fragment( + './cac:%sLine' % + (fexml.tag_document()), + append=next_append) next_append = True line.set_element('./cbc:ID', index + 1) - line.set_element('./cbc:%sQuantity' % (fexml.tag_document_concilied()), invoice_line.quantity, unitCode = 'NAR') + line.set_element( + './cbc:%sQuantity' % + (fexml.tag_document_concilied()), + invoice_line.quantity, + unitCode='NAR') fexml.set_element_amount_for(line, './cbc:LineExtensionAmount', invoice_line.total_amount) @@ -603,26 +832,35 @@ class DIANInvoiceXML(fe.FeXML): if not isinstance(invoice_line.tax, TaxTotalOmit): fexml.set_invoice_line_tax(line, invoice_line) - if not isinstance(invoice_line.withholding, WithholdingTaxTotalOmit): + if not isinstance( + invoice_line.withholding, + WithholdingTaxTotalOmit): fexml.set_invoice_line_withholding(line, invoice_line) - line.set_element('./cac:Item/cbc:Description', invoice_line.item.description) + line.set_element( + './cac:Item/cbc:Description', + invoice_line.item.description) - line.set_element('./cac:Item/cac:StandardItemIdentification/cbc:ID', - invoice_line.item.id, - schemeID=invoice_line.item.scheme_id, - schemeName=invoice_line.item.scheme_name, - schemeAgencyID=invoice_line.item.scheme_agency_id) + line.set_element( + './cac:Item/cac:StandardItemIdentification/cbc:ID', + invoice_line.item.id, + schemeID=invoice_line.item.scheme_id, + schemeName=invoice_line.item.scheme_name, + schemeAgencyID=invoice_line.item.scheme_agency_id) - line.set_element('./cac:Price/cbc:PriceAmount', invoice_line.price.amount, currencyID=invoice_line.price.amount.currency.code) - #DIAN 1.7.-2020: FBB04 + line.set_element( + './cac:Price/cbc:PriceAmount', + invoice_line.price.amount, + currencyID=invoice_line.price.amount.currency.code) + # DIAN 1.7.-2020: FBB04 line.set_element('./cac:Price/cbc:BaseQuantity', invoice_line.price.quantity, unitCode=invoice_line.quantity.code) for idx, charge in enumerate(invoice_line.allowance_charge): next_append_charge = idx > 0 - fexml.append_allowance_charge(line, index + 1, charge, append=next_append_charge) + fexml.append_allowance_charge( + line, index + 1, charge, append=next_append_charge) def set_allowance_charge(fexml, invoice): for idx, charge in enumerate(invoice.invoice_allowance_charge): @@ -656,29 +894,39 @@ class DIANInvoiceXML(fe.FeXML): fexml.placeholder_for('./ext:UBLExtensions') fexml.set_element('./cbc:UBLVersionID', 'UBL 2.1') - fexml.set_element('./cbc:CustomizationID', invoice.invoice_operation_type) + fexml.set_element( + './cbc:CustomizationID', + invoice.invoice_operation_type) fexml.placeholder_for('./cbc:ProfileID') fexml.placeholder_for('./cbc:ProfileExecutionID') fexml.set_element('./cbc:ID', invoice.invoice_ident) fexml.placeholder_for('./cbc:UUID') - fexml.set_element('./cbc:IssueDate', invoice.invoice_issue.strftime('%Y-%m-%d')) - #DIAN 1.7.-2020: FAD10 - fexml.set_element('./cbc:IssueTime', invoice.invoice_issue.strftime('%H:%M:%S-05:00')) - fexml.set_element('./cbc:%sTypeCode' % (fexml.tag_document()), - invoice.invoice_type_code, - listAgencyID='195', - listAgencyName='No matching global declaration available for the validation root', - listURI='http://www.dian.gov.co') + fexml.set_element( + './cbc:IssueDate', + invoice.invoice_issue.strftime('%Y-%m-%d')) + # DIAN 1.7.-2020: FAD10 + fexml.set_element( + './cbc:IssueTime', + invoice.invoice_issue.strftime('%H:%M:%S-05:00')) + fexml.set_element( + './cbc:%sTypeCode' % + (fexml.tag_document()), + invoice.invoice_type_code, + listAgencyID='195', + listAgencyName=( + 'No matching global declaration available for the ' + 'validation root'), + listURI='http://www.dian.gov.co') fexml.set_element('./cbc:DocumentCurrencyCode', 'COP') fexml.set_element('./cbc:LineCountNumeric', len(invoice.invoice_lines)) if fexml.tag_document() == 'Invoice': fexml.set_element('./cac:%sPeriod/cbc:StartDate' % ( fexml.tag_document()), - invoice.invoice_period_start.strftime('%Y-%m-%d')) + invoice.invoice_period_start.strftime('%Y-%m-%d')) fexml.set_element('./cac:%sPeriod/cbc:EndDate' % ( fexml.tag_document()), - invoice.invoice_period_end.strftime('%Y-%m-%d')) + invoice.invoice_period_end.strftime('%Y-%m-%d')) fexml.set_billing_reference(invoice) fexml.customize(invoice) fexml.set_supplier(invoice) diff --git a/facho/fe/form_xml/support_document.py b/facho/fe/form_xml/support_document.py index c425e22..30c03af 100644 --- a/facho/fe/form_xml/support_document.py +++ b/facho/fe/form_xml/support_document.py @@ -14,36 +14,45 @@ __all__ = ['DIANSupportDocumentXML'] class DIANSupportDocumentXML(fe.FeXML): """ DianSupportDocumentXML mapea objeto form.Invoice a XML segun - lo indicado para él Documento soporte en adquisiciones efectuadas con sujetos no obligados a expedir factura de venta o documento equivalente. + lo indicado para él Documento soporte en adquisiciones efectuadas con + sujetos no obligados a expedir factura de venta o documento equivalente. """ def __init__(self, invoice, tag_document='Invoice'): - super().__init__(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1') + super().__init__( + tag_document, + 'http://www.dian.gov.co/contratos/facturaelectronica/v1') # DIAN 1.1.-2021: DSAB03 # DIAN 1.1.-2021: NSAB03 self.placeholder_for( - './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceControl') + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:InvoiceControl') # DIAN 1.1.-2021: DSAB13 # DIAN 1.1.-2021: NSAB13 self.placeholder_for( - './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:InvoiceSource') + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:InvoiceSource') # DIAN 1.1.-2021: DSAB18 # DIAN 1.1.-2021: NSAB18 self.placeholder_for( - './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareProvider') + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:SoftwareProvider') # DIAN 1.1.-2021: DSAB27 # DIAN 1.1.-2021: NSAB27 self.placeholder_for( - './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:SoftwareSecurityCode') + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts' + ':DianExtensions/sts:SoftwareSecurityCode') # DIAN 1.1.-2021: DSAB30 DSAB31 # DIAN 1.1.-2021: NSAB30 NSAB31 self.placeholder_for( - './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/sts:DianExtensions/sts:AuthorizationProvider/sts:AuthorizationProviderID') + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent/' + 'sts:DianExtensions/sts:AuthorizationProvider/' + 'sts:AuthorizationProviderID') # ZE02 se requiere existencia para firmar # DIAN 1.1.-2021: DSAA02 DSAB01 @@ -52,7 +61,7 @@ class DIANSupportDocumentXML(fe.FeXML): './ext:UBLExtensions/ext:UBLExtension', append=True) # DIAN 1.1.-2021: DSAB02 # DIAN 1.1.-2021: NSAB02 - extcontent = ublextension.find_or_create_element( + ublextension.find_or_create_element( '/ext:UBLExtension/ext:ExtensionContent') self.attach_invoice(invoice) @@ -70,55 +79,65 @@ class DIANSupportDocumentXML(fe.FeXML): # DIAN 1.1.-2021: DSAJ07 DSAJ08 # DIAN 1.1.-2021: NSAJ07 NSAJ08 fexml.placeholder_for( - './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address') + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address') # DIAN 1.1.-2021: DSAJ09 # DIAN 1.1.-2021: NSAJ09 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:ID', + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:ID', invoice.invoice_supplier.address.city.code) # DIAN 1.1.-2021: DSAJ10 # DIAN 1.1.-2021: NSAJ10 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CityName', + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:CityName', invoice.invoice_supplier.address.city.name) # DIAN 1.1.-2021: DSAJ73 # DIAN 1.1.-2021: NSAJ73 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:PostalZone', + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:PostalZone', invoice.invoice_supplier.address.postalzone.code) # DIAN 1.1.-2021: DSAJ11 # DIAN 1.1.-2021: NSAJ11 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentity', + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:CountrySubentity', invoice.invoice_supplier.address.countrysubentity.name) # DIAN 1.1.-2021: DSAJ12 # DIAN 1.1.-2021: NSAJ12 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cbc:CountrySubentityCode', + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cbc:CountrySubentityCode', invoice.invoice_supplier.address.countrysubentity.code) # DIAN 1.1.-2021: NSAJ13 NSAJ14 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:AddressLine/cbc:Line', + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cac:AddressLine/cbc:Line', invoice.invoice_supplier.address.street) # DIAN 1.1.-2021: DSAJ15 DSAJ16 # DIAN 1.1.-2021: NSAJ15 NSAJ16 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:IdentificationCode', + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cac:Country/cbc:IdentificationCode', invoice.invoice_supplier.address.country.code) # DIAN 1.1.-2021: DSAJ17 # DIAN 1.1.-2021: NSAJ17 - fexml.set_element('./cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac:Address/cac:Country/cbc:Name', - invoice.invoice_supplier.address.country.name, - # DIAN 1.1.-2021: DSAJ18 - # # DIAN 1.1.-2021: NSAJ18 - languageID='es') + fexml.set_element( + './cac:AccountingSupplierParty/cac:Party/cac:PhysicalLocation/cac' + ':Address/cac:Country/cbc:Name', + invoice.invoice_supplier.address.country.name, + # DIAN 1.1.-2021: DSAJ18 + # # DIAN 1.1.-2021: NSAJ18 + languageID='es') supplier_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy() supplier_company_id_attrs.update( @@ -134,13 +153,15 @@ class DIANSupportDocumentXML(fe.FeXML): # DIAN 1.1.-2021: DSAJ20 # DIAN 1.1.-2021: NSAJ20 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName', + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc' + ':RegistrationName', invoice.invoice_supplier.legal_name) # DIAN 1.1.-2021: DSAJ21 # DIAN 1.1.-2021: NSAJ21 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc' + ':CompanyID', invoice.invoice_supplier.ident, # DIAN 1.1.-2021: DSAJ22 DSAJ23 DSAJ24 DSAJ25 # DIAN 1.1.-2021: NSAJ22 NSAJ23 NSAJ24 NSAJ25 @@ -149,25 +170,29 @@ class DIANSupportDocumentXML(fe.FeXML): # DIAN 1.1.-2021: DSAJ26 # DIAN 1.1.-2021: NSAJ26 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode', + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cbc' + ':TaxLevelCode', invoice.invoice_supplier.responsability_code, listName=invoice.invoice_supplier.responsability_regime_code) # DIAN 1.1.-2021: DSAJ39 # DIAN 1.1.-2021: NSAJ39 fexml.placeholder_for( - './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme') + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme') # DIAN 1.1.-2021: DSAJ40 # DIAN 1.1.-2021: NSAJ40 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID', + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme/cbc:ID', invoice.invoice_customer.tax_scheme.code) # DIAN 1.1.-2021: DSAJ41 # DIAN 1.1.-2021: NSAJ41 fexml.set_element( - './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name', + './cac:AccountingSupplierParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme/cbc:Name', invoice.invoice_customer.tax_scheme.name) def set_customer(fexml, invoice): @@ -193,7 +218,8 @@ class DIANSupportDocumentXML(fe.FeXML): # DIAN 1.1.-2021: DSAK20 # DIAN 1.1.-2021: NSAK20 fexml.set_element( - './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:RegistrationName', + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc' + ':RegistrationName', invoice.invoice_customer.legal_name) customer_company_id_attrs = fe.SCHEME_AGENCY_ATTRS.copy() @@ -205,7 +231,8 @@ class DIANSupportDocumentXML(fe.FeXML): # DIAN 1.1.-2021: DSAK21 # DIAN 1.1.-2021: NSAK21 fexml.set_element( - './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:CompanyID', + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc' + ':CompanyID', invoice.invoice_customer.ident, # DIAN 1.1.-2021: DSAK22 DSAK23 DSAK24 DSAK25 # DIAN 1.1.-2021: NSAK22 NSAK23 NSAK24 NSAK25 @@ -214,24 +241,28 @@ class DIANSupportDocumentXML(fe.FeXML): # DIAN 1.1.-2021: DSAK26 # DIAN 1.1.-2021: NSAK26 fexml.set_element( - './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc:TaxLevelCode', + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cbc' + ':TaxLevelCode', invoice.invoice_customer.responsability_code) # DIAN 1.1.-2021: DSAK39 # DIAN 1.1.-2021: NSAK39 fexml.placeholder_for( - './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme') + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme') # DIAN 1.1.-2021: DSAK40 # DIAN 1.1.-2021: NSAK40 fexml.set_element( - './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:ID', + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme/cbc:ID', invoice.invoice_customer.tax_scheme.code) # DIAN 1.1.-2021: DSAK41 # DIAN 1.1.-2021: NSAK41 fexml.set_element( - './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac:TaxScheme/cbc:Name', + './cac:AccountingCustomerParty/cac:Party/cac:PartyTaxScheme/cac' + ':TaxScheme/cbc:Name', invoice.invoice_customer.tax_scheme.name) def set_payment_mean(fexml, invoice): @@ -328,7 +359,8 @@ class DIANSupportDocumentXML(fe.FeXML): reference.uuid, schemeName=schemeName) fexml.set_element( - './cac:BillingReference/cac:InvoiceDocumentReference/cbc:IssueDate', + './cac:BillingReference/cac:InvoiceDocumentReference/' + 'cbc:IssueDate', reference.date.strftime("%Y-%m-%d")) def set_billing_reference(fexml, invoice): @@ -419,14 +451,17 @@ class DIANSupportDocumentXML(fe.FeXML): if percent_for[cod_impuesto]: line.set_element( - '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', + '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/' + 'cbc:Percent', percent_for[cod_impuesto]) line.set_element( - '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', + '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme' + '/cbc:ID', cod_impuesto) line.set_element( - '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', 'IVA') + '/cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme' + '/cbc:Name', 'IVA') # abstract method @@ -455,7 +490,8 @@ class DIANSupportDocumentXML(fe.FeXML): if subtotal.percent is not None: line.set_element( - './cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', + './cac:WithholdingTaxTotal/cac:TaxSubtotal/cac' + ':TaxCategory/cbc:Percent', '%0.2f' % round( subtotal.percent, @@ -464,10 +500,12 @@ class DIANSupportDocumentXML(fe.FeXML): if subtotal.scheme is not None: # DIAN 1.7.-2020: FAX15 line.set_element( - './cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', + './cac:WithholdingTaxTotal/cac:TaxSubtotal/cac' + ':TaxCategory/cac:TaxScheme/cbc:ID', subtotal.scheme.code) line.set_element( - './cac:WithholdingTaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', + './cac:WithholdingTaxTotal/cac:TaxSubtotal/cac' + ':TaxCategory/cac:TaxScheme/cbc:Name', subtotal.scheme.name) def set_invoice_line_tax(fexml, line, invoice_line): @@ -488,7 +526,8 @@ class DIANSupportDocumentXML(fe.FeXML): if subtotal.percent is not None: line.set_element( - './cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc:Percent', + './cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cbc' + ':Percent', '%0.2f' % round( subtotal.percent, @@ -497,10 +536,12 @@ class DIANSupportDocumentXML(fe.FeXML): if subtotal.scheme is not None: # DIAN 1.7.-2020: FAX15 line.set_element( - './cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:ID', + './cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac' + ':TaxScheme/cbc:ID', subtotal.scheme.code) line.set_element( - './cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac:TaxScheme/cbc:Name', + './cac:TaxTotal/cac:TaxSubtotal/cac:TaxCategory/cac' + ':TaxScheme/cbc:Name', subtotal.scheme.name) def set_invoice_lines(fexml, invoice): @@ -615,7 +656,9 @@ class DIANSupportDocumentXML(fe.FeXML): (fexml.tag_document()), invoice.invoice_type_code, listAgencyID='195', - listAgencyName='No matching global declaration available for the validation root', + listAgencyName=( + 'No matching global declaration available for the ' + 'validation root'), listURI='http://www.dian.gov.co') fexml.set_element('./cbc:LineCountNumeric', len(invoice.invoice_lines)) fexml.set_element( diff --git a/facho/fe/nomina/__init__.py b/facho/fe/nomina/__init__.py index bb66ad0..66dd76e 100644 --- a/facho/fe/nomina/__init__.py +++ b/facho/fe/nomina/__init__.py @@ -11,18 +11,88 @@ import hashlib from .. import fe -from .. import form from ..data.dian import codelist -from .devengado import * -from .deduccion import * -from .trabajador import * -from .empleador import * -from .pago import * -from .lugar import Lugar - from .amount import Amount -from .exception import * +from .deduccion import ( + Deduccion, + DeduccionFondoPension, + DeduccionSalud, +) +from .devengado import ( + Devengado, + DevengadoBasico, + DevengadoHoraExtra, + DevengadoHorasExtrasDiarias, + DevengadoHorasExtrasDiariasDominicalesYFestivos, + DevengadoHorasExtrasNocturnas, + DevengadoHorasExtrasNocturnasDominicalesYFestivos, + DevengadoHorasRecargoDiariasDominicalesYFestivos, + DevengadoHorasRecargoNocturno, + DevengadoHorasRecargoNocturnoDominicalesYFestivos, + DevengadoTransporte, +) +from .empleador import Empleador +from .exception import DIANNominaIndividualError +from .lugar import Lugar +from .pago import ( + FormaPago, + MetodoPago, + Pago, +) +from .trabajador import ( + LugarTrabajo, + SubTipoTrabajador, + TipoContrato, + TipoDocumento, + TipoTrabajador, + Trabajador, +) + +__all__ = [ + 'Amount', + 'Deduccion', + 'DeduccionFondoPension', + 'DeduccionSalud', + 'Devengado', + 'DevengadoBasico', + 'DevengadoHoraExtra', + 'DevengadoHorasExtrasDiarias', + 'DevengadoHorasExtrasDiariasDominicalesYFestivos', + 'DevengadoHorasExtrasNocturnas', + 'DevengadoHorasExtrasNocturnasDominicalesYFestivos', + 'DevengadoHorasRecargoDiariasDominicalesYFestivos', + 'DevengadoHorasRecargoNocturno', + 'DevengadoHorasRecargoNocturnoDominicalesYFestivos', + 'DevengadoTransporte', + 'DIANNominaIndividual', + 'DIANNominaIndividualDeAjuste', + 'DIANNominaXML', + 'DIANNominaIndividualError', + 'DianXMLExtensionSigner', + 'Empleador', + 'Fecha', + 'FechaPago', + 'FormaPago', + 'InformacionGeneral', + 'Lugar', + 'LugarTrabajo', + 'Metadata', + 'MetodoPago', + 'Novedad', + 'NumeroSecuencia', + 'Pago', + 'Periodo', + 'PeriodoNomina', + 'Proveedor', + 'SubTipoTrabajador', + 'TipoContrato', + 'TipoDocumento', + 'TipoMoneda', + 'TipoTrabajador', + 'Trabajador', +] + class Fecha: def __init__(self, fecha): @@ -46,6 +116,7 @@ class Fecha: def __str__(self): return self.value + class FechaPago(Fecha): def apply(self, fragment): fragment.set_element('./FechaPago', self.value) @@ -82,7 +153,8 @@ class NumeroSecuencia: # NIE011 Consecutivo=self.consecutivo, # NIE012 - Numero = numero) + Numero=numero) + @dataclass class Periodo: @@ -96,25 +168,28 @@ class Periodo: def __post_init__(self): self.fecha_ingreso = Fecha.cast(self.fecha_ingreso) - self.fecha_liquidacion_inicio = Fecha.cast(self.fecha_liquidacion_inicio) + self.fecha_liquidacion_inicio = Fecha.cast( + self.fecha_liquidacion_inicio) self.fecha_liquidacion_fin = Fecha.cast(self.fecha_liquidacion_fin) self.fecha_retiro = Fecha.cast(self.fecha_retiro, optional=True) - + def apply(self, fragment): fragment.set_attributes('./Periodo', - #NIE002 + # NIE002 FechaIngreso=self.fecha_ingreso, - #NIE003 + # NIE003 FechaRetiro=self.fecha_retiro, - #NIE004 - FechaLiquidacionInicio=self.fecha_liquidacion_inicio, - #NIE005 + # NIE004 + FechaLiquidacionInicio=( + self.fecha_liquidacion_inicio), + # NIE005 FechaLiquidacionFin=self.fecha_liquidacion_fin, - #NIE006 + # NIE006 TiempoLaborado=self.tiempo_laborado, - #NIE008 + # NIE008 FechaGen=self.fecha_generacion) - + + @dataclass class Proveedor: razon_social: str @@ -140,29 +215,36 @@ class Proveedor: def post_apply(self, fexml, scopexml, fragment): cune_xpath = scopexml.xpath_from_root('/InformacionGeneral') cune = fexml.get_element_attribute(cune_xpath, 'CUNE') - - ambiente = fexml.get_element_attribute(scopexml.xpath_from_root('/InformacionGeneral'), 'Ambiente') - codigo_qr = f"https://catalogo-vpfe.dian.gov.co/document/searchqr?documentkey={cune}" + + ambiente = fexml.get_element_attribute( + scopexml.xpath_from_root('/InformacionGeneral'), 'Ambiente') + codigo_qr = ( + f"https://catalogo-vpfe.dian.gov.co/document/" + f"searchqr?documentkey={cune}") if InformacionGeneral.AMBIENTE_PRUEBAS == ambiente: - codigo_qr = f"https://catalogo-vpfe-hab.dian.gov.co/document/searchqr?documentkey={cune}" + codigo_qr = ( + f"https://catalogo-vpfe-hab.dian.gov.co/document/" + f"searchqr?documentkey={cune}") elif ambiente is None: raise RuntimeError('fail to get InformacionGeneral/@Ambiente') - + scopexml.set_element('./CodigoQR', codigo_qr) # NIE020 software_code = self._software_security_code(fexml, scopexml) - fexml.set_attributes(scopexml.xpath_from_root('/ProveedorXML'), SoftwareSC=software_code) + fexml.set_attributes( + scopexml.xpath_from_root('/ProveedorXML'), + SoftwareSC=software_code) def _software_security_code(self, fexml, scopexml): - # 8.2 - numero = fexml.get_element_attribute(scopexml.xpath_from_root('/NumeroSecuenciaXML'), 'Numero') + numero = fexml.get_element_attribute( + scopexml.xpath_from_root('/NumeroSecuenciaXML'), 'Numero') if numero is None: raise RuntimeError('fallo obtener NumeroSequenciaXML/@Numero') - + id_software = self.software_id software_pin = self.software_pin @@ -172,7 +254,8 @@ class Proveedor: h = hashlib.sha384() h.update(code.encode('utf-8')) return h.hexdigest() - + + @dataclass class Metadata: novedad: Novedad @@ -181,18 +264,33 @@ class Metadata: lugar_generacion: Lugar proveedor: Proveedor - def apply(self, novedad, numero_secuencia_xml, lugar_generacion_xml, proveedor_xml): + def apply( + self, + novedad, + numero_secuencia_xml, + lugar_generacion_xml, + proveedor_xml): if novedad: self.novedad.apply(novedad) self.secuencia.apply(numero_secuencia_xml) - self.lugar_generacion.apply(lugar_generacion_xml, './LugarGeneracionXML') + self.lugar_generacion.apply( + lugar_generacion_xml, + './LugarGeneracionXML') self.proveedor.apply(proveedor_xml) - def post_apply(self, fexml, scopexml, novedad, numero_secuencia_xml, lugar_generacion_xml, proveedor_xml): + def post_apply( + self, + fexml, + scopexml, + novedad, + numero_secuencia_xml, + lugar_generacion_xml, + proveedor_xml): self.proveedor.post_apply(fexml, scopexml, proveedor_xml) if novedad: - self.novedad.post_apply(fexml, scopexml, proveedor_xml) - + self.novedad.post_apply(fexml, scopexml, proveedor_xml) + + @dataclass class PeriodoNomina: code: str @@ -203,6 +301,7 @@ class PeriodoNomina: raise ValueError("code [%s] not found" % (self.code)) self.name = codelist.PeriodoNomina[self.code]['name'] + @dataclass class TipoMoneda: code: str @@ -213,6 +312,7 @@ class TipoMoneda: raise ValueError("code [%s] not found" % (self.code)) self.name = codelist.TipoMoneda[self.code]['name'] + @dataclass class InformacionGeneral: @dataclass @@ -235,7 +335,7 @@ class InformacionGeneral: valor: str = '2' def __str__(self): - self.valor + self.valor # TABLA 5.5.7 @dataclass @@ -250,7 +350,7 @@ class InformacionGeneral: valor: str = '102' def __str__(self): - self.valor + self.valor @dataclass class TIPO_XML_AJUSTES(TIPO_XML): @@ -273,26 +373,26 @@ class InformacionGeneral: def apply(self, fragment, version): fragment.set_attributes('./InformacionGeneral', # NIE022 - Version = version, + Version=version, # NIE023 - Ambiente = self.tipo_ambiente.valor, + Ambiente=self.tipo_ambiente.valor, # NIE202 # TABLA 5.5.2 # TODO(bit4bit) solo NominaIndividual - TipoXML = self.tipo_xml.valor, + TipoXML=self.tipo_xml.valor, # NIE024 - CUNE = None, + CUNE=None, # NIE025 - EncripCUNE = 'CUNE-SHA384', + EncripCUNE='CUNE-SHA384', # NIE026 - FechaGen = self.fecha_generacion, + FechaGen=self.fecha_generacion, # NIE027 - HoraGen = self.hora_generacion, + HoraGen=self.hora_generacion, # NIE029 - PeriodoNomina = self.periodo_nomina.code, + PeriodoNomina=self.periodo_nomina.code, # NIE030 - TipoMoneda = self.tipo_moneda.code, - TRM = 0 + TipoMoneda=self.tipo_moneda.code, + TRM=0 # TODO(bit4bit) resto... # ..... ) @@ -321,24 +421,35 @@ class InformacionGeneral: h = hashlib.sha384() h.update(cune.encode('utf-8')) cune_hash = h.hexdigest() - + fragment.set_attributes( './InformacionGeneral', # NIE024 - CUNE = cune_hash + CUNE=cune_hash ) + class DianXMLExtensionSigner(fe.DianXMLExtensionSigner): def __init__(self, pkcs12_path, passphrase=None, localpolicy=True): - super().__init__(pkcs12_path, passphrase=passphrase, localpolicy=localpolicy) + super().__init__( + pkcs12_path, + passphrase=passphrase, + localpolicy=localpolicy) def _element_extension_content(self, fachoxml): - return fachoxml.builder.xpath(fachoxml.root, './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent') + return fachoxml.builder.xpath( + fachoxml.root, + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent') class DIANNominaXML: - def __init__(self, tag_document, xpath_ajuste=None, schemaLocation=None, namespace_ajuste=None): + def __init__( + self, + tag_document, + xpath_ajuste=None, + schemaLocation=None, + namespace_ajuste=None): self.informacion_general_version = None self.tag_document = tag_document @@ -346,21 +457,26 @@ class DIANNominaXML: if namespace_ajuste: self.fexml = fe.FeXML(tag_document, namespace_ajuste) else: - self.fexml = fe.FeXML(tag_document, 'dian:gov:co:facturaelectronica:NominaIndividual') + self.fexml = fe.FeXML( + tag_document, + 'dian:gov:co:facturaelectronica:NominaIndividual') self.fexml.root.set("SchemaLocation", "") self.fexml.root.set("schemaLocation", schemaLocation) # layout, la dian requiere que los elementos # esten ordenados segun el anexo tecnico - self.fexml.placeholder_for('./ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent') + self.fexml.placeholder_for( + './ext:UBLExtensions/ext:UBLExtension/ext:ExtensionContent') self.fexml.placeholder_for('./TipoNota', optional=True) self.root_fragment = self.fexml if xpath_ajuste is not None: self.root_fragment = self.fexml.fragment(xpath_ajuste) - self.root_fragment.placeholder_for('./ReemplazandoPredecesor', optional=True) - self.root_fragment.placeholder_for('./EliminandoPredecesor', optional=True) + self.root_fragment.placeholder_for( + './ReemplazandoPredecesor', optional=True) + self.root_fragment.placeholder_for( + './EliminandoPredecesor', optional=True) if not namespace_ajuste: self.root_fragment.placeholder_for('./Novedad', optional=False) self.root_fragment.placeholder_for('./Periodo') @@ -374,16 +490,20 @@ class DIANNominaXML: self.root_fragment.placeholder_for('./Pago') self.root_fragment.placeholder_for('./FechasPagos') self.root_fragment.placeholder_for('./Devengados/Basico') - self.root_fragment.placeholder_for('./Devengados/Transporte', optional=True) + self.root_fragment.placeholder_for( + './Devengados/Transporte', optional=True) if not namespace_ajuste: self.novedad = self.root_fragment.fragment('./Novedad') else: self.novedad = None - self.informacion_general_xml = self.root_fragment.fragment('./InformacionGeneral') + self.informacion_general_xml = self.root_fragment.fragment( + './InformacionGeneral') self.periodo_xml = self.root_fragment.fragment('./Periodo') self.fecha_pagos_xml = self.root_fragment.fragment('./FechasPagos') - self.numero_secuencia_xml = self.root_fragment.fragment('./NumeroSecuenciaXML') - self.lugar_generacion_xml = self.root_fragment.fragment('./LugarGeneracionXML') + self.numero_secuencia_xml = self.root_fragment.fragment( + './NumeroSecuenciaXML') + self.lugar_generacion_xml = self.root_fragment.fragment( + './LugarGeneracionXML') self.proveedor_xml = self.root_fragment.fragment('./ProveedorXML') self.empleador = self.root_fragment.fragment('./Empleador') self.trabajador = self.root_fragment.fragment('./Trabajador') @@ -398,14 +518,20 @@ class DIANNominaXML: if not isinstance(metadata, Metadata): raise ValueError('se espera tipo Metadata') self.metadata = metadata - - self.metadata.apply(self.novedad, self.numero_secuencia_xml, self.lugar_generacion_xml, self.proveedor_xml) - + + self.metadata.apply( + self.novedad, + self.numero_secuencia_xml, + self.lugar_generacion_xml, + self.proveedor_xml) + def asignar_informacion_general(self, general): if not isinstance(general, InformacionGeneral): raise ValueError('se espera tipo InformacionGeneral') self.informacion_general = general - self.informacion_general.apply(self.informacion_general_xml, self.informacion_general_version) + self.informacion_general.apply( + self.informacion_general_xml, + self.informacion_general_version) def asignar_periodo(self, periodo): if not isinstance(periodo, Periodo): @@ -435,7 +561,7 @@ class DIANNominaXML: if not isinstance(trabajador, Trabajador): raise ValueError('se espera tipo Trabajador') trabajador.apply(self.trabajador) - + def adicionar_devengado(self, devengado): if not isinstance(devengado, Devengado): raise ValueError('se espera tipo Devengado') @@ -482,7 +608,7 @@ class DIANNominaXML: self.fexml.xpath_from_root('/Devengados/Basico'), 'se requiere DevengadoBasico' ) - + check_element( self.fexml.xpath_from_root('/Deducciones/Salud'), 'se requiere DeduccionSalud' @@ -498,10 +624,13 @@ class DIANNominaXML: def informacion_general(self): xpath = self.root_fragment.xpath_from_root('/InformacionGeneral') return { - 'cune': self.fexml.get_element_attribute(cune_xpath, 'CUNE'), - 'fecha_generacion': self.fexml.get_element_attribute(xpath, 'FechaGen'), - 'numero': self.fexml.get_element_attribute(self.root_fragment('/NumeroSecuenciaXML', 'Numero')) - } + 'cune': self.fexml.get_element_attribute( + xpath, 'CUNE'), + 'fecha_generacion': self.fexml.get_element_attribute( + xpath, 'FechaGen'), + 'numero': self.fexml.get_element_attribute( + self.root_fragment( + '/NumeroSecuenciaXML', 'Numero'))} def toFachoXML(self): self._devengados_total() @@ -509,59 +638,78 @@ class DIANNominaXML: self._comprobante_total() if self.informacion_general is not None: - #TODO(bit4bit) acoplamiento temporal + # TODO(bit4bit) acoplamiento temporal # es importante el orden de ejecucion - self.informacion_general.post_apply(self.fexml, self.root_fragment, self.informacion_general_xml) + self.informacion_general.post_apply( + self.fexml, self.root_fragment, self.informacion_general_xml) if self.metadata is not None: - self.metadata.post_apply(self.fexml, self.root_fragment, self.novedad, self.numero_secuencia_xml, self.lugar_generacion_xml, self.proveedor_xml) + self.metadata.post_apply( + self.fexml, + self.root_fragment, + self.novedad, + self.numero_secuencia_xml, + self.lugar_generacion_xml, + self.proveedor_xml) return self.fexml def _comprobante_total(self): - devengados_total = self.root_fragment.get_element_text_or_attribute('./DevengadosTotal', '0.0') - deducciones_total = self.root_fragment.get_element_text_or_attribute('./DeduccionesTotal', '0.0') + devengados_total = self.root_fragment.get_element_text_or_attribute( + './DevengadosTotal', '0.0') + deducciones_total = self.root_fragment.get_element_text_or_attribute( + './DeduccionesTotal', '0.0') - comprobante_total = Amount(devengados_total) - Amount(deducciones_total) + comprobante_total = Amount(devengados_total) - \ + Amount(deducciones_total) - self.root_fragment.set_element('./ComprobanteTotal', str(round(comprobante_total, 2))) + self.root_fragment.set_element( + './ComprobanteTotal', str(round(comprobante_total, 2))) def _deducciones_total(self): xpaths = [ - self.root_fragment.xpath_from_root('/Deducciones/Salud/@Deduccion'), - self.root_fragment.xpath_from_root('/Deducciones/FondoPension/@Deduccion') - ] + self.root_fragment.xpath_from_root( + '/Deducciones/Salud/@Deduccion'), + self.root_fragment.xpath_from_root( + '/Deducciones/FondoPension/@Deduccion')] deducciones = map(lambda valor: Amount(valor), self._values_of_xpaths(xpaths)) deducciones_total = Amount(0.0) - + for deduccion in deducciones: deducciones_total += deduccion - self.root_fragment.set_element('./DeduccionesTotal', str(round(deducciones_total, 2))) + self.root_fragment.set_element( + './DeduccionesTotal', str(round(deducciones_total, 2))) def _devengados_total(self): xpaths = [ - self.root_fragment.xpath_from_root('/Devengados/Basico/@SueldoTrabajado'), - self.root_fragment.xpath_from_root('/Devengados/Transporte/@AuxilioTransporte'), - self.root_fragment.xpath_from_root('/Devengados/Transporte/@ViaticoManuAlojS'), - self.root_fragment.xpath_from_root('/Devengados/Transporte/@ViaticoManuAlojNS') - ] + self.root_fragment.xpath_from_root( + '/Devengados/Basico/@SueldoTrabajado'), + self.root_fragment.xpath_from_root( + '/Devengados/Transporte/@AuxilioTransporte'), + self.root_fragment.xpath_from_root( + '/Devengados/Transporte/@ViaticoManuAlojS'), + self.root_fragment.xpath_from_root( + '/Devengados/Transporte/@ViaticoManuAlojNS')] devengados = map(lambda valor: Amount(valor), self._values_of_xpaths(xpaths)) - + devengados_total = Amount(0.0) for devengado in devengados: devengados_total += devengado # TODO(bit4bit) nque valor va redondeado? # NIE186 - self.root_fragment.set_element('./Redondeo', str(round(0,2))) - self.root_fragment.set_element('./DevengadosTotal', str(round(devengados_total,2))) + self.root_fragment.set_element('./Redondeo', str(round(0, 2))) + self.root_fragment.set_element( + './DevengadosTotal', str(round(devengados_total, 2))) def _values_of_xpaths(self, xpaths): - xpaths_values_of_values = map(lambda val: self.fexml.get_element_text_or_attribute(val, multiple=True), xpaths) + xpaths_values_of_values = map( + lambda val: self.fexml.get_element_text_or_attribute( + val, multiple=True), xpaths) xpaths_values = [] # toda esta carreta para hacer un aplano de lista for xpath_values in xpaths_values_of_values: @@ -573,15 +721,22 @@ class DIANNominaXML: return filter(lambda val: val is not None, xpaths_values) + class DIANNominaIndividual(DIANNominaXML): def __init__(self): - schema = "dian:gov:co:facturaelectronica:NominaIndividual NominaIndividualElectronicaXSD.xsd" + schema = ( + "dian:gov:co:facturaelectronica:NominaIndividual" + " NominaIndividualElectronicaXSD.xsd" + ) super().__init__('NominaIndividual', schemaLocation=schema) - self.informacion_general_version = 'V1.0: Documento Soporte de Pago de Nómina Electrónica' + self.informacion_general_version = ( + 'V1.0: Documento Soporte de Pago de Nómina Electrónica') # TODO(bit4bit) confirmar que no tienen en comun con NominaIndividual + + class DIANNominaIndividualDeAjuste(DIANNominaXML): class Reemplazar(DIANNominaXML): @@ -593,31 +748,42 @@ class DIANNominaIndividualDeAjuste(DIANNominaXML): def apply(self, fragment): # NIAE214 - fragment.set_element('./TipoNota', '1') - fragment.set_element('./Reemplazar/ReemplazandoPredecesor', None, - # NIAE090 - NumeroPred = self.numero, - # NIAE191 - CUNEPred = self.cune, - # NIAE192 - FechaGenPred = self.fecha_generacion + fragment.set_element('./TipoNota', '1') + fragment.set_element( + './Reemplazar/ReemplazandoPredecesor', None, + # NIAE090 + NumeroPred=self.numero, + # NIAE191 + CUNEPred=self.cune, + # NIAE192 + FechaGenPred=self.fecha_generacion ) def __init__(self): - schema = "dian:gov:co:facturaelectronica:NominaIndividualDeAjuste NominaIndividualDeAjusteElectronicaXSD.xsd" - - super().__init__('NominaIndividualDeAjuste', './Reemplazar', schemaLocation=schema, namespace_ajuste='dian:gov:co:facturaelectronica:NominaIndividualDeAjuste') - - self.informacion_general_version = 'V1.0: Nota de Ajuste de Documento Soporte de Pago de Nómina Electrónica' + schema = ( + "dian:gov:co:facturaelectronica:NominaIndividualDeAjuste" + " NominaIndividualDeAjusteElectronicaXSD.xsd" + ) + + super().__init__( + 'NominaIndividualDeAjuste', + './Reemplazar', + schemaLocation=schema, + namespace_ajuste=( + 'dian:gov:co:facturaelectronica:' + 'NominaIndividualDeAjuste')) + + self.informacion_general_version = ( + 'V1.0: Nota de Ajuste de Documento Soporte de Pago de ' + 'Nómina Electrónica') def asignar_predecesor(self, predecesor): if not isinstance(predecesor, self.Predecesor): raise ValueError("se espera tipo Predecesor") predecesor.apply(self.fexml) - class Eliminar(DIANNominaXML): - + @dataclass class Predecesor: numero: str @@ -628,24 +794,34 @@ class DIANNominaIndividualDeAjuste(DIANNominaXML): fragment.set_element('./TipoNota', '2') fragment.set_element('./Eliminar/EliminandoPredecesor', None, # NIAE090 - NumeroPred = self.numero, + NumeroPred=self.numero, # NIAE191 - CUNEPred = self.cune, + CUNEPred=self.cune, # NIAE192 - FechaGenPred = self.fecha_generacion + FechaGenPred=self.fecha_generacion ) def __init__(self): - schema = "dian:gov:co:facturaelectronica:NominaIndividualDeAjuste NominaIndividualDeAjusteElectronicaXSD.xsd" - super().__init__('NominaIndividualDeAjuste', './Eliminar', schemaLocation=schema, namespace_ajuste='dian:gov:co:facturaelectronica:NominaIndividualDeAjuste') + schema = ( + "dian:gov:co:facturaelectronica:NominaIndividualDeAjuste" + " NominaIndividualDeAjusteElectronicaXSD.xsd" + ) + super().__init__( + 'NominaIndividualDeAjuste', + './Eliminar', + schemaLocation=schema, + namespace_ajuste=( + 'dian:gov:co:facturaelectronica:' + 'NominaIndividualDeAjuste')) - self.informacion_general_version = "V1.0: Nota de Ajuste de Documento Soporte de Pago de Nómina Electrónica" + self.informacion_general_version = ( + "V1.0: Nota de Ajuste de Documento Soporte de Pago de " + "Nómina Electrónica") def asignar_predecesor(self, predecesor): if not isinstance(predecesor, self.Predecesor): raise ValueError("se espera tipo Eliminar.Predecesor") predecesor.apply(self.fexml) - + def __init__(self): super().__init__('NominaIndividualDeAjuste') - diff --git a/facho/fe/nomina/amount.py b/facho/fe/nomina/amount.py index 20be8ec..00091fc 100644 --- a/facho/fe/nomina/amount.py +++ b/facho/fe/nomina/amount.py @@ -1,4 +1,5 @@ from .. import form + class Amount(form.Amount): pass diff --git a/facho/fe/nomina/deduccion/__init__.py b/facho/fe/nomina/deduccion/__init__.py index a030056..a686a6b 100644 --- a/facho/fe/nomina/deduccion/__init__.py +++ b/facho/fe/nomina/deduccion/__init__.py @@ -2,9 +2,9 @@ # al crear objetos de valor # se debe exportar en __all__ -from .deduccion import * -from .salud import * -from .fondo_pension import * +from .deduccion import Deduccion +from .salud import DeduccionSalud +from .fondo_pension import DeduccionFondoPension __all__ = [ 'Deduccion', diff --git a/facho/fe/nomina/deduccion/fondo_pension.py b/facho/fe/nomina/deduccion/fondo_pension.py index e9b32c8..a7f3bc8 100644 --- a/facho/fe/nomina/deduccion/fondo_pension.py +++ b/facho/fe/nomina/deduccion/fondo_pension.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from ..amount import Amount from .deduccion import Deduccion + @dataclass class DeduccionFondoPension(Deduccion): porcentaje: Amount @@ -10,9 +11,9 @@ class DeduccionFondoPension(Deduccion): def apply(self, fragment): fragment.set_element('./FondoPension', None, - append_ = True, + append_=True, # NIE164 - Porcentaje = str(round(self.porcentaje, 2)), + Porcentaje=str(round(self.porcentaje, 2)), # NIE166 - Deduccion = self.deduccion + Deduccion=self.deduccion ) diff --git a/facho/fe/nomina/deduccion/salud.py b/facho/fe/nomina/deduccion/salud.py index a075bc8..710efad 100644 --- a/facho/fe/nomina/deduccion/salud.py +++ b/facho/fe/nomina/deduccion/salud.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from ..amount import Amount from .deduccion import Deduccion + @dataclass class DeduccionSalud(Deduccion): porcentaje: Amount @@ -10,10 +11,9 @@ class DeduccionSalud(Deduccion): def apply(self, fragment): fragment.set_element('./Salud', None, - append_ = True, + append_=True, # NIE161 - Porcentaje = str(round(self.porcentaje, 2)), + Porcentaje=str(round(self.porcentaje, 2)), # NIE163 - Deduccion = self.deduccion + Deduccion=self.deduccion ) - diff --git a/facho/fe/nomina/departamento.py b/facho/fe/nomina/departamento.py index a7b9f09..3152a9a 100644 --- a/facho/fe/nomina/departamento.py +++ b/facho/fe/nomina/departamento.py @@ -1,4 +1,5 @@ from .. import form + class Departamento(form.CountrySubentity): pass diff --git a/facho/fe/nomina/devengado/__init__.py b/facho/fe/nomina/devengado/__init__.py index bf101c2..1fc8f0f 100644 --- a/facho/fe/nomina/devengado/__init__.py +++ b/facho/fe/nomina/devengado/__init__.py @@ -1,8 +1,17 @@ -from .basico import * -from .transporte import * -from .devengado import * -from .horas_extras import * +from .basico import DevengadoBasico +from .transporte import DevengadoTransporte +from .devengado import Devengado +from .horas_extras import ( + DevengadoHoraExtra, + DevengadoHorasExtrasDiarias, + DevengadoHorasExtrasNocturnas, + DevengadoHorasRecargoNocturno, + DevengadoHorasExtrasDiariasDominicalesYFestivos, + DevengadoHorasRecargoDiariasDominicalesYFestivos, + DevengadoHorasExtrasNocturnasDominicalesYFestivos, + DevengadoHorasRecargoNocturnoDominicalesYFestivos, +) __all__ = [ 'Devengado', diff --git a/facho/fe/nomina/devengado/basico.py b/facho/fe/nomina/devengado/basico.py index 55f88aa..300cfbf 100644 --- a/facho/fe/nomina/devengado/basico.py +++ b/facho/fe/nomina/devengado/basico.py @@ -11,10 +11,10 @@ class DevengadoBasico(Devengado): def apply(self, fragment): fragment.find_or_create_element('./Basico') - + fragment.set_attributes('/Basico', # NIE069 - DiasTrabajados = str(self.dias_trabajados), + DiasTrabajados=str(self.dias_trabajados), # NIE070 - SueldoTrabajado = round(self.sueldo_trabajado, 2) + SueldoTrabajado=round(self.sueldo_trabajado, 2) ) diff --git a/facho/fe/nomina/devengado/horas_extras.py b/facho/fe/nomina/devengado/horas_extras.py index c591781..e89c5e4 100644 --- a/facho/fe/nomina/devengado/horas_extras.py +++ b/facho/fe/nomina/devengado/horas_extras.py @@ -31,16 +31,17 @@ class DevengadoHoraExtra: @dataclass class DevengadoHorasExtrasDiarias(Devengado): horas_extras: list[DevengadoHoraExtra] - + def apply(self, fragment): hora_extra_xml = fragment.fragment('./HEDs') for hora_extra in self.horas_extras: hora_extra.apply('./HED', hora_extra_xml) + @dataclass class DevengadoHorasExtrasNocturnas(Devengado): horas_extras: list[DevengadoHoraExtra] - + def apply(self, fragment): hora_extra_xml = fragment.fragment('./HENs') for hora_extra in self.horas_extras: @@ -50,43 +51,47 @@ class DevengadoHorasExtrasNocturnas(Devengado): @dataclass class DevengadoHorasRecargoNocturno(Devengado): horas_extras: list[DevengadoHoraExtra] - + def apply(self, fragment): hora_extra_xml = fragment.fragment('./HRNs') for hora_extra in self.horas_extras: hora_extra.apply('./HRN', hora_extra_xml) + @dataclass class DevengadoHorasExtrasDiariasDominicalesYFestivos(Devengado): horas_extras: list[DevengadoHoraExtra] - + def apply(self, fragment): hora_extra_xml = fragment.fragment('./HEDDFs') for hora_extra in self.horas_extras: hora_extra.apply('./HEDDF', hora_extra_xml) + @dataclass class DevengadoHorasRecargoDiariasDominicalesYFestivos(Devengado): horas_extras: list[DevengadoHoraExtra] - + def apply(self, fragment): hora_extra_xml = fragment.fragment('./HRDDFs') for hora_extra in self.horas_extras: hora_extra.apply('./HRDDF', hora_extra_xml) + @dataclass class DevengadoHorasExtrasNocturnasDominicalesYFestivos(Devengado): horas_extras: list[DevengadoHoraExtra] - + def apply(self, fragment): hora_extra_xml = fragment.fragment('./HENDFs') for hora_extra in self.horas_extras: hora_extra.apply('./HENDF', hora_extra_xml) + @dataclass class DevengadoHorasRecargoNocturnoDominicalesYFestivos(Devengado): horas_extras: list[DevengadoHoraExtra] - + def apply(self, fragment): hora_extra_xml = fragment.fragment('./HRNDFs') for hora_extra in self.horas_extras: diff --git a/facho/fe/nomina/devengado/transporte.py b/facho/fe/nomina/devengado/transporte.py index a5c9dab..b1f1013 100644 --- a/facho/fe/nomina/devengado/transporte.py +++ b/facho/fe/nomina/devengado/transporte.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from ..amount import Amount from .devengado import Devengado + @dataclass class DevengadoTransporte(Devengado): auxilio_transporte: Amount = None @@ -11,11 +12,12 @@ class DevengadoTransporte(Devengado): def apply(self, fragment): fragment.set_element('./Transporte', None, - append_ = True, + append_=True, # NIE071 - AuxilioTransporte = self.auxilio_transporte, + AuxilioTransporte=self.auxilio_transporte, # NIE072 - ViaticoManuAlojS = self.viatico_manutencion, + ViaticoManuAlojS=self.viatico_manutencion, # NIE073 - ViaticoManuAlojNS = self.viatico_manutencion_no_salarial + ViaticoManuAlojNS=( + self.viatico_manutencion_no_salarial) ) diff --git a/facho/fe/nomina/empleador/__init__.py b/facho/fe/nomina/empleador/__init__.py index 2c651c8..12e34ce 100644 --- a/facho/fe/nomina/empleador/__init__.py +++ b/facho/fe/nomina/empleador/__init__.py @@ -4,6 +4,7 @@ from ..pais import Pais from ..departamento import Departamento from ..municipio import Municipio + @dataclass class Empleador: razon_social: str @@ -17,19 +18,17 @@ class Empleador: def apply(self, fragment): fragment.set_attributes('./Empleador', # NIE033 - NIT = self.nit, + NIT=self.nit, # NIE034 - DV = self.dv, + DV=self.dv, # NIE035 - Pais = self.pais.code, + Pais=self.pais.code, # NIE036 - DepartamentoEstado = self.departamento.code, + DepartamentoEstado=self.departamento.code, # NIE037 - MunicipioCiudad = self.municipio.code, + MunicipioCiudad=self.municipio.code, # NIE038 - Direccion = self.direccion, + Direccion=self.direccion, RazonSocial=self.razon_social ) - - diff --git a/facho/fe/nomina/hablitacion.py b/facho/fe/nomina/hablitacion.py index 681dc89..4cf23a2 100644 --- a/facho/fe/nomina/hablitacion.py +++ b/facho/fe/nomina/hablitacion.py @@ -3,6 +3,7 @@ import datetime from facho import fe + class Habilitacion: @dataclass @@ -16,19 +17,19 @@ class Habilitacion: self.metadata = metadata def generar(self, zipname, fecha): - nominas = [] - dianzip = fe.DianZIP(open(zipname, 'w')) + fe.DianZIP(open(zipname, 'w')) fechabase = datetime.datetime.now() consecutivo = 0 for _ in range(1, 11): consecutivo += 1 fechabase += datetime.timedelta(days=1) - nomina = self._crear_nomina_individual() + self._crear_nomina_individual() # pag 96 - nombre = "nie%010d%s%08x.xml" % (int(self.nit), fecha.strftime('%s'), consecutivo) - + "nie%010d%s%08x.xml" % ( + int(self.nit), fecha.strftime('%s'), consecutivo) + def _crear_nomina_individual_reemplazar(self, nomina, fechabase): metadata = self.metadata @@ -36,10 +37,15 @@ class Habilitacion: nomina_ajuste = fe.nomina.DIANNominaIndividualDeAjuste.Reemplazar() self._poblar_nomina(nomina_ajuste, metadata, fecha, prefijo='R') - informacion_general = nomina.informacion_general() - - - def _poblar_nomina(self, nomina, metadata, fecha, prefijo='N', consecutivo='0001'): + nomina.informacion_general() + + def _poblar_nomina( + self, + nomina, + metadata, + fecha, + prefijo='N', + consecutivo='0001'): nomina.asignar_fecha_pago(fecha) nomina.asignar_metadata(fe.nomina.Metadata( @@ -48,14 +54,14 @@ class Habilitacion: consecutivo=consecutivo ), lugar_generacion=fe.nomina.Lugar( - pais = fe.nomina.Pais( - code = 'CO' + pais=fe.nomina.Pais( + code='CO' ), - departamento = fe.nomina.Departamento( - code = '05' + departamento=fe.nomina.Departamento( + code='05' ), - municipio = fe.nomina.Municipio( - code = '05001' + municipio=fe.nomina.Municipio( + code='05001' ), ), proveedor=fe.nomina.Proveedor( @@ -72,14 +78,14 @@ class Habilitacion: fecha_liquidacion_fin=fecha, fecha_generacion=fecha, )) - + nomina.asignar_informacion_general(fe.nomina.InformacionGeneral( - fecha_generacion = fecha, - hora_generacion = '20:09:00-05:00', - tipo_ambiente = fe.nomina.InformacionGeneral.AMBIENTE_PRUEBAS, - software_pin = metadata.software_pin, - periodo_nomina = fe.nomina.PeriodoNomina(code='1'), - tipo_moneda = fe.nomina.TipoMoneda(code='COP') + fecha_generacion=fecha, + hora_generacion='20:09:00-05:00', + tipo_ambiente=fe.nomina.InformacionGeneral.AMBIENTE_PRUEBAS, + software_pin=metadata.software_pin, + periodo_nomina=fe.nomina.PeriodoNomina(code='1'), + tipo_moneda=fe.nomina.TipoMoneda(code='COP') )) nomina.asignar_pago(fe.nomina.Pago( @@ -91,53 +97,53 @@ class Habilitacion: ) )) nomina.asignar_empleador(fe.nomina.Empleador( - nit = metadata.nit, - dv = '0', - pais = fe.nomina.Pais( - code = 'CO' + nit=metadata.nit, + dv='0', + pais=fe.nomina.Pais( + code='CO' ), - departamento = fe.nomina.Departamento( - code = '05' + departamento=fe.nomina.Departamento( + code='05' ), - municipio = fe.nomina.Municipio( - code = '05001' + municipio=fe.nomina.Municipio( + code='05001' ), - direccion = 'calle etrivial' + direccion='calle etrivial' )) nomina.asignar_trabajador(fe.nomina.Trabajador( - tipo_contrato = fe.nomina.TipoContrato( - code = '1' + tipo_contrato=fe.nomina.TipoContrato( + code='1' ), - alto_riesgo = False, - tipo_documento = fe.nomina.TipoDocumento( - code = '11' + alto_riesgo=False, + tipo_documento=fe.nomina.TipoDocumento( + code='11' ), - primer_apellido = 'gnu', - segundo_apellido = 'emacs', - primer_nombre = 'facho', - lugar_trabajo = fe.nomina.LugarTrabajo( - pais = fe.nomina.Pais(code='CO'), - departamento = fe.nomina.Departamento(code='05'), - municipio = fe.nomina.Municipio(code='05001'), - direccion = 'calle facho' + primer_apellido='gnu', + segundo_apellido='emacs', + primer_nombre='facho', + lugar_trabajo=fe.nomina.LugarTrabajo( + pais=fe.nomina.Pais(code='CO'), + departamento=fe.nomina.Departamento(code='05'), + municipio=fe.nomina.Municipio(code='05001'), + direccion='calle facho' ), - numero_documento = metadata.nit, - tipo = fe.nomina.TipoTrabajador( - code = '01' + numero_documento=metadata.nit, + tipo=fe.nomina.TipoTrabajador( + code='01' ), - salario_integral = True, - sueldo = fe.nomina.Amount(1_500_000) + salario_integral=True, + sueldo=fe.nomina.Amount(1_500_000) )) - + nomina.adicionar_devengado(fe.nomina.DevengadoBasico( - dias_trabajados = 60, - sueldo_trabajado = fe.nomina.Amount(3_500_000) + dias_trabajados=60, + sueldo_trabajado=fe.nomina.Amount(3_500_000) )) nomina.adicionar_deduccion(fe.nomina.DeduccionSalud( - porcentaje = fe.nomina.Amount(19), - deduccion = fe.nomina.Amount(1_000_000) + porcentaje=fe.nomina.Amount(19), + deduccion=fe.nomina.Amount(1_000_000) )) nomina.adicionar_deduccion(fe.nomina.DeduccionFondoPension( @@ -152,4 +158,3 @@ class Habilitacion: nomina = fe.nomina.DIANNominaIndividual() self._poblar_nomina(nomina, metadata, fecha) - diff --git a/facho/fe/nomina/lugar.py b/facho/fe/nomina/lugar.py index e11a24e..635221e 100644 --- a/facho/fe/nomina/lugar.py +++ b/facho/fe/nomina/lugar.py @@ -5,6 +5,7 @@ from .departamento import Departamento from .municipio import Municipio from facho.fe.data.dian import codelist + @dataclass class Lugar: pais: Pais diff --git a/facho/fe/nomina/municipio.py b/facho/fe/nomina/municipio.py index b70e05c..98b0108 100644 --- a/facho/fe/nomina/municipio.py +++ b/facho/fe/nomina/municipio.py @@ -1,5 +1,5 @@ from .. import form + class Municipio(form.City): pass - diff --git a/facho/fe/nomina/pago/__init__.py b/facho/fe/nomina/pago/__init__.py index ca6cf5a..c8979cb 100644 --- a/facho/fe/nomina/pago/__init__.py +++ b/facho/fe/nomina/pago/__init__.py @@ -3,6 +3,7 @@ from dataclasses import dataclass from .forma_pago import FormaPago from .metodo_pago import MetodoPago + @dataclass class Pago: forma: FormaPago @@ -11,6 +12,6 @@ class Pago: def apply(self, fragment): fragment.set_attributes('./Pago', # NIE064 - Forma = self.forma.code, + Forma=self.forma.code, # NIE065 - Metodo = self.metodo.code) + Metodo=self.metodo.code) diff --git a/facho/fe/nomina/pago/forma_pago.py b/facho/fe/nomina/pago/forma_pago.py index 5b612db..bab5044 100644 --- a/facho/fe/nomina/pago/forma_pago.py +++ b/facho/fe/nomina/pago/forma_pago.py @@ -2,6 +2,7 @@ from dataclasses import dataclass from facho.fe.data.dian import codelist + @dataclass class FormaPago: code: str diff --git a/facho/fe/nomina/pago/metodo_pago.py b/facho/fe/nomina/pago/metodo_pago.py index b25a938..d0a3c27 100644 --- a/facho/fe/nomina/pago/metodo_pago.py +++ b/facho/fe/nomina/pago/metodo_pago.py @@ -2,6 +2,7 @@ from dataclasses import dataclass from facho.fe.data.dian import codelist + @dataclass class MetodoPago: code: str @@ -11,4 +12,3 @@ class MetodoPago: if self.code not in codelist.MediosPago: raise ValueError("code [%s] not found" % (self.code)) self.name = codelist.MediosPago[self.code]['name'] - diff --git a/facho/fe/nomina/pais.py b/facho/fe/nomina/pais.py index a42a612..f571393 100644 --- a/facho/fe/nomina/pais.py +++ b/facho/fe/nomina/pais.py @@ -1,4 +1,5 @@ from .. import form + class Pais(form.Country): pass diff --git a/facho/fe/nomina/trabajador/__init__.py b/facho/fe/nomina/trabajador/__init__.py index a262866..14a0a8e 100644 --- a/facho/fe/nomina/trabajador/__init__.py +++ b/facho/fe/nomina/trabajador/__init__.py @@ -2,12 +2,11 @@ from dataclasses import dataclass, field from ..amount import Amount -from .tipo_contrato import * -from .tipo_documento import * -from .lugar_trabajo import * -from .tipo_trabajador import * -from .sub_tipo_trabajador import * - +from .tipo_contrato import TipoContrato +from .tipo_documento import TipoDocumento +from .lugar_trabajo import LugarTrabajo +from .tipo_trabajador import TipoTrabajador +from .sub_tipo_trabajador import SubTipoTrabajador @dataclass @@ -29,45 +28,47 @@ class Trabajador: codigo_trabajador: str = None otros_nombres: str = None - sub_tipo: SubTipoTrabajador = field(default_factory=lambda: SubTipoTrabajador(code='00')) + sub_tipo: SubTipoTrabajador = field( + default_factory=lambda: SubTipoTrabajador(code="00") + ) def apply(self, fragment): - fragment.set_attributes('./Trabajador', - # NIE041 - TipoTrabajador = self.tipo.code, - # NIE042 - SubTipoTrabajador = self.sub_tipo.code, - # NIE043 - AltoRiesgoPension = str(self.alto_riesgo).lower(), - # NIE044 - TipoDocumento = self.tipo_documento.code, - # NIE045 - NumeroDocumento = self.numero_documento, - # NIE046 - PrimerApellido = self.primer_apellido, - # NIE047 - SegundoApellido = self.segundo_apellido, - # NIE048 - PrimerNombre = self.primer_nombre, - # NIE049 - OtrosNombres = self.otros_nombres, - # NIE050 - LugarTrabajoPais = self.lugar_trabajo.pais.code, - - # NIE051 - LugarTrabajoDepartamentoEstado = self.lugar_trabajo.departamento.code, - - # NIE052 - LugarTrabajoMunicipioCiudad = self.lugar_trabajo.municipio.code, - - # NIE053 - LugarTrabajoDireccion = self.lugar_trabajo.direccion, - # NIE056 - SalarioIntegral = str(self.salario_integral).lower(), - # NIE061 - TipoContrato = self.tipo_contrato.code, - # NIE062 - Sueldo = str(self.sueldo), - # NIE063 - CodigoTrabajador = self.codigo_trabajador - ) + fragment.set_attributes( + "./Trabajador", + # NIE041 + TipoTrabajador=self.tipo.code, + # NIE042 + SubTipoTrabajador=self.sub_tipo.code, + # NIE043 + AltoRiesgoPension=str(self.alto_riesgo).lower(), + # NIE044 + TipoDocumento=self.tipo_documento.code, + # NIE045 + NumeroDocumento=self.numero_documento, + # NIE046 + PrimerApellido=self.primer_apellido, + # NIE047 + SegundoApellido=self.segundo_apellido, + # NIE048 + PrimerNombre=self.primer_nombre, + # NIE049 + OtrosNombres=self.otros_nombres, + # NIE050 + LugarTrabajoPais=self.lugar_trabajo.pais.code, + # NIE051 + LugarTrabajoDepartamentoEstado=( + self.lugar_trabajo.departamento.code + ), + # NIE052 + LugarTrabajoMunicipioCiudad=self.lugar_trabajo.municipio.code, + # NIE053 + LugarTrabajoDireccion=self.lugar_trabajo.direccion, + # NIE056 + SalarioIntegral=str(self.salario_integral).lower(), + # NIE061 + TipoContrato=self.tipo_contrato.code, + # NIE062 + Sueldo=str(self.sueldo), + # NIE063 + CodigoTrabajador=self.codigo_trabajador, + ) diff --git a/facho/fe/nomina/trabajador/lugar_trabajo.py b/facho/fe/nomina/trabajador/lugar_trabajo.py index 58396d7..7882b59 100644 --- a/facho/fe/nomina/trabajador/lugar_trabajo.py +++ b/facho/fe/nomina/trabajador/lugar_trabajo.py @@ -1,10 +1,9 @@ from dataclasses import dataclass - -from . import * from ..pais import Pais from ..departamento import Departamento from ..municipio import Municipio + @dataclass class LugarTrabajo: pais: Pais diff --git a/facho/fe/nomina/trabajador/sub_tipo_trabajador.py b/facho/fe/nomina/trabajador/sub_tipo_trabajador.py index bdde174..d1a44ef 100644 --- a/facho/fe/nomina/trabajador/sub_tipo_trabajador.py +++ b/facho/fe/nomina/trabajador/sub_tipo_trabajador.py @@ -2,12 +2,13 @@ from dataclasses import dataclass from facho.fe.data.dian import codelist + @dataclass class SubTipoTrabajador: code: str - name: str = '' + name: str = "" def __post_init__(self): if self.code not in codelist.SubTipoTrabajador: raise ValueError("code [%s] not found" % (self.code)) - self.name = codelist.SubTipoTrabajador[self.code]['name'] + self.name = codelist.SubTipoTrabajador[self.code]["name"] diff --git a/facho/fe/nomina/trabajador/tipo_contrato.py b/facho/fe/nomina/trabajador/tipo_contrato.py index cc73d56..fb4d1e8 100644 --- a/facho/fe/nomina/trabajador/tipo_contrato.py +++ b/facho/fe/nomina/trabajador/tipo_contrato.py @@ -2,14 +2,13 @@ from dataclasses import dataclass from facho.fe.data.dian import codelist + @dataclass class TipoContrato: code: str - name: str = '' + name: str = "" def __post_init__(self): if self.code not in codelist.TipoContrato: raise ValueError("code [%s] not found" % (self.code)) - self.name = codelist.TipoContrato[self.code]['name'] - - + self.name = codelist.TipoContrato[self.code]["name"] diff --git a/facho/fe/nomina/trabajador/tipo_documento.py b/facho/fe/nomina/trabajador/tipo_documento.py index 6818e23..774c1cc 100644 --- a/facho/fe/nomina/trabajador/tipo_documento.py +++ b/facho/fe/nomina/trabajador/tipo_documento.py @@ -2,12 +2,13 @@ from dataclasses import dataclass from facho.fe.data.dian import codelist + @dataclass class TipoDocumento: code: str - name: str = '' + name: str = "" def __post_init__(self): if self.code not in codelist.TipoIdFiscal: raise ValueError("code [%s] not found" % (self.code)) - self.name = codelist.TipoIdFiscal[self.code]['name'] + self.name = codelist.TipoIdFiscal[self.code]["name"] diff --git a/facho/fe/nomina/trabajador/tipo_trabajador.py b/facho/fe/nomina/trabajador/tipo_trabajador.py index 3bdb533..79479c3 100644 --- a/facho/fe/nomina/trabajador/tipo_trabajador.py +++ b/facho/fe/nomina/trabajador/tipo_trabajador.py @@ -2,12 +2,13 @@ from dataclasses import dataclass from facho.fe.data.dian import codelist + @dataclass class TipoTrabajador: code: str - name: str = '' + name: str = "" def __post_init__(self): if self.code not in codelist.TipoTrabajador: raise ValueError("code [%s] not found" % (self.code)) - self.name = codelist.TipoTrabajador[self.code]['name'] + self.name = codelist.TipoTrabajador[self.code]["name"]