se adiciona comando validate-nominaindividual para validacion usando XSD de caja de herramientas

FossilOrigin-Name: e2f075b294e19cd447ad6e1e83ef7f00c8b863718abf35fcfccbb182097e300d
This commit is contained in:
bit4bit 2021-11-23 01:18:28 +00:00
parent 0c28eea6e2
commit 6d02ad0bf5
7 changed files with 65 additions and 9 deletions

View File

@ -200,6 +200,18 @@ def validate_invoice(invoice_path):
XSD.validate(content, XSD.UBLInvoice)
@click.command()
@click.argument('nomina_path')
def validate_nominaindividual(nomina_path):
from facho.fe.data.dian import XSD
content = open(nomina_path, 'r').read()
content = content.replace(
'xmlns="http://www.dian.gov.co/contratos/facturaelectronica/v1"',
'xmlns="dian:gov:co:facturaelectronica:NominaIndividual"',
)
XSD.validate(content, XSD.NominaIndividual)
@click.command()
@click.option('--private-key', type=click.Path(exists=True))
@click.option('--passphrase')
@ -327,7 +339,6 @@ def soap_send_nomina_sync(private_key, public_key, habilitacion, password, filen
if habilitacion:
req = dian.Habilitacion.SendNominaSync
resp = client.request(req(
filename,
open(zipfile, 'rb').read()
))
print(resp)
@ -372,3 +383,4 @@ main.add_command(sign_xml)
main.add_command(sign_verify_xml)
main.add_command(generate_nomina)
main.add_command(soap_send_nomina_sync)
main.add_command(validate_nominaindividual)

View File

@ -177,7 +177,6 @@ class GetStatusZip(SOAPService):
@dataclass
class SendNominaSync(SOAPService):
fileName: str
contentFile: bytes
def get_wsdl(self):

View File

@ -9,5 +9,8 @@ def path_for_xsd(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'))
def validate(xml, schema):
schema.validate(xml)

View File

@ -34,7 +34,31 @@ class NumeroSecuencia:
# NIE012
Numero = self.numero)
@dataclass
class Periodo:
fecha_ingreso: str
fecha_liquidacion_inicio: str
fecha_liquidacion_fin: str
fecha_generacion: str
tiempo_laborado: int = 1
fecha_retiro: str = None
def apply(self, fragment):
fragment.set_attributes('./Periodo',
#NIE002
FechaIngreso=self.fecha_ingreso,
#NIE003
FechaRetiro=self.fecha_retiro,
#NIE004
FechaLiquidacionInicio=self.fecha_liquidacion_inicio,
#NIE005
FechaLiquidacionFin=self.fecha_liquidacion_fin,
#NIE006
TiempoLaborado=self.tiempo_laborado,
#NIE008
FechaGen=self.fecha_generacion)
@dataclass
class Proveedor:
nit: str
@ -60,8 +84,7 @@ class Proveedor:
# TODO(bit4bit) https://catalogovpfehab.dian.gov.co/document/searchqr?documentkey=CUNE para habilitacion
# https://catalogovpfe.dian.gov.co/document/searchqr?documentkey=CUNE
codigo_qr = f"https://catalogovpfe.dian.gov.co/document/searchqr?documentkey={cune}"
fragment.set_attributes('./ProveedorXML',
CodigoQR=codigo_qr)
fexml.set_element('./CodigoQR', codigo_qr)
@dataclass
class Metadata:
@ -184,10 +207,13 @@ class DianXMLExtensionSigner(fe.DianXMLExtensionSigner):
class DIANNominaXML:
def __init__(self, tag_document, xpath_ajuste=None):
def __init__(self, tag_document, xpath_ajuste=None,schemaLocation=None):
self.tag_document = tag_document
self.fexml = fe.FeXML(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1')
if schemaLocation is not None:
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')
@ -203,15 +229,19 @@ class DIANNominaXML:
self.root_fragment.placeholder_for('./NumeroSecuenciaXML')
self.root_fragment.placeholder_for('./LugarGeneracionXML')
self.root_fragment.placeholder_for('./ProveedorXML')
self.root_fragment.placeholder_for('./CodigoQR')
self.root_fragment.placeholder_for('./InformacionGeneral')
self.root_fragment.placeholder_for('./Empleador')
self.root_fragment.placeholder_for('./Trabajador')
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.informacion_general_xml = self.root_fragment.fragment('./InformacionGeneral')
self.periodo_xml = self.root_fragment.fragment('./Periodo')
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')
@ -236,11 +266,20 @@ class DIANNominaXML:
self.informacion_general = general
self.informacion_general.apply(self.informacion_general_xml)
def asignar_periodo(self, periodo):
if not isinstance(periodo, Periodo):
raise ValueError('se espera tipo Periodo')
periodo.apply(self.periodo_xml)
def asignar_pago(self, pago):
if not isinstance(pago, Pago):
raise ValueError('se espera tipo Pago')
pago.apply(self.pago_xml)
def asignar_fecha_pago(self, fecha):
self.fexml.set_element('./FechasPagos/FechaPago', fecha)
def asignar_empleador(self, empleador):
if not isinstance(empleador, Empleador):
raise ValueError('se espera tipo Empleador')
@ -381,8 +420,9 @@ class DIANNominaXML:
class DIANNominaIndividual(DIANNominaXML):
def __init__(self):
super().__init__('NominaIndividual')
schema = "dian:gov:co:facturaelectronica:NominaIndividual NominaIndividualElectronicaXSD.xsd"
super().__init__('NominaIndividual', schemaLocation=schema)
# TODO(bit4bit) confirmar que no tienen en comun con NominaIndividual
class DIANNominaIndividualDeAjuste(DIANNominaXML):

View File

@ -21,4 +21,5 @@ class Lugar:
fragment.set_attributes(root,
Pais=self.pais.code,
DepartamentoEstado=self.departamento.code,
MunicipioCiudad=self.municipio.code)
MunicipioCiudad=self.municipio.code,
Idioma=self.idioma)

View File

@ -55,7 +55,7 @@ class Trabajador:
LugarTrabajoPais = self.lugar_trabajo.pais.code,
# NIE051
LugarTrabajoDepartamentoEstadoEstado = self.lugar_trabajo.departamento.code,
LugarTrabajoDepartamentoEstado = self.lugar_trabajo.departamento.code,
# NIE052
LugarTrabajoMunicipioCiudad = self.lugar_trabajo.municipio.code,

View File

@ -19,7 +19,8 @@ requirements = ['Click>=6.0',
'xmlsig>=0.1.3',
'xades>=0.2.1',
'mock==2.0.0',
'xmlsec>=1.3.8']
'xmlsec>=1.3.8',
'xmlschema>=1.8']
setup_requirements = ['pytest-runner', ]