From bb16b5e968c93129bccf17da71184585260f388d Mon Sep 17 00:00:00 2001 From: bit4bit Date: Wed, 10 Nov 2021 01:08:39 +0000 Subject: [PATCH] se extendie NominaIndividualDeAjuste de NominaIndividual FossilOrigin-Name: 10b3af439bee9b932e4f5067bec0768af60e514f595e00ecb1bdf7c88c39afc1 --- facho/facho.py | 11 ++++++ facho/fe/nomina/__init__.py | 72 ++++++++++++++++++++++--------------- tests/test_facho.py | 14 ++++++++ 3 files changed, 69 insertions(+), 28 deletions(-) diff --git a/facho/facho.py b/facho/facho.py index 384f986..caa2a42 100644 --- a/facho/facho.py +++ b/facho/facho.py @@ -455,5 +455,16 @@ class FachoXML: def tostring(self, **kw): return self.builder.tostring(self.root, **kw) + def xpath_from_root(self, xpath): + nsmap = {} + ns = '' + + if isinstance(self.nsmap, dict): + nsmap = dict(map(reversed, self.nsmap.items())) + ns = nsmap[etree.QName(self.root).namespace] + ':' + + new_xpath = '/' + ns + etree.QName(self.root).localname + '/' + xpath.lstrip('/') + return new_xpath + def __str__(self): return self.tostring() diff --git a/facho/fe/nomina/__init__.py b/facho/fe/nomina/__init__.py index 2563ee3..6810f28 100644 --- a/facho/fe/nomina/__init__.py +++ b/facho/fe/nomina/__init__.py @@ -69,17 +69,17 @@ class InformacionGeneral: # generar cune # ver 8.1.1.1 xpaths = [ - '/fe:NominaIndividual/NumeroSecuenciaXML/@Numero', - '/fe:NominaIndividual/InformacionGeneral/@FechaGen', - '/fe:NominaIndividual/InformacionGeneral/@HoraGen', - '/fe:NominaIndividual/DevengadosTotal', - '/fe:NominaIndividual/DeduccionesTotal', - '/fe:NominaIndividual/ComprobanteTotal', - '/fe:NominaIndividual/Empleador/@NIT', - '/fe:NominaIndividual/Trabajador/@NumeroDocumento', - '/fe:NominaIndividual/InformacionGeneral/@TipoXML', + fexml.xpath_from_root('/NumeroSecuenciaXML/@Numero'), + fexml.xpath_from_root('/InformacionGeneral/@FechaGen'), + fexml.xpath_from_root('/InformacionGeneral/@HoraGen'), + fexml.xpath_from_root('/DevengadosTotal'), + fexml.xpath_from_root('/DeduccionesTotal'), + fexml.xpath_from_root('/ComprobanteTotal'), + fexml.xpath_from_root('/Empleador/@NIT'), + fexml.xpath_from_root('/Trabajador/@NumeroDocumento'), + fexml.xpath_from_root('/InformacionGeneral/@TipoXML'), tuple([self.software_pin]), - '/fe:NominaIndividual/InformacionGeneral/@Ambiente' + fexml.xpath_from_root('/InformacionGeneral/@Ambiente') ] campos = fexml.get_elements_text_or_attributes(xpaths) @@ -111,9 +111,10 @@ class Trabajador: fragment.set_attributes('./Trabajador', NumeroDocumento = self.numero_documento) -class DIANNominaIndividual: - def __init__(self): - self.fexml = fe.FeXML('NominaIndividual', 'http://www.dian.gov.co/contratos/facturaelectronica/v1') +class DIANNominaXML: + def __init__(self, tag_document): + self.tag_document = tag_document + self.fexml = fe.FeXML(tag_document, 'http://www.dian.gov.co/contratos/facturaelectronica/v1') # layout, la dian requiere que los elementos # esten ordenados segun el anexo tecnico @@ -187,20 +188,23 @@ class DIANNominaIndividual: if elem.get(key, None) is None: return errors.append(err) - check_attribute('/fe:NominaIndividual/Periodo', 'FechaIngreso', 'se requiere Periodo') + check_attribute( + self.fexml.xpath_from_root('/Periodo'), + 'FechaIngreso', + 'se requiere Periodo') check_element( - '/fe:NominaIndividual/Devengados/Basico', + self.fexml.xpath_from_root('/Devengados/Basico'), 'se requiere DevengadoBasico' ) check_element( - '/fe:NominaIndividual/Deducciones/Salud', + self.fexml.xpath_from_root('/Deducciones/Salud'), 'se requiere DeduccionSalud' ) check_element( - '/fe:NominaIndividual/Deducciones/FondoPension', + self.fexml.xpath_from_root('/Deducciones/FondoPension'), 'se requiere DeduccionFondoPension' ) @@ -220,17 +224,17 @@ class DIANNominaIndividual: return self.fexml def _comprobante_total(self): - devengados_total = self.fexml.get_element_text_or_attribute('/fe:NominaIndividual/DevengadosTotal', '0.0') - deducciones_total = self.fexml.get_element_text_or_attribute('/fe:NominaIndividual/DeduccionesTotal', '0.0') + devengados_total = self.fexml.get_element_text_or_attribute(self.fexml.xpath_from_root('/DevengadosTotal'), '0.0') + deducciones_total = self.fexml.get_element_text_or_attribute(self.fexml.xpath_from_root('/DeduccionesTotal'), '0.0') comprobante_total = Amount(devengados_total) - Amount(deducciones_total) - self.fexml.set_element('/fe:NominaIndividual/ComprobanteTotal', str(round(comprobante_total, 2))) + self.fexml.set_element(self.fexml.xpath_from_root('/ComprobanteTotal'), str(round(comprobante_total, 2))) def _deducciones_total(self): xpaths = [ - '/fe:NominaIndividual/Deducciones/Salud/@Deduccion', - '/fe:NominaIndividual/Deducciones/FondoPension/@Deduccion' + self.fexml.xpath_from_root('/Deducciones/Salud/@Deduccion'), + self.fexml.xpath_from_root('/Deducciones/FondoPension/@Deduccion') ] deducciones = map(lambda valor: Amount(valor), self._values_of_xpaths(xpaths)) @@ -240,14 +244,14 @@ class DIANNominaIndividual: for deduccion in deducciones: deducciones_total += deduccion - self.fexml.set_element('/fe:NominaIndividual/DeduccionesTotal', str(round(deducciones_total, 2))) + self.fexml.set_element(f'/fe:{self.tag_document}/DeduccionesTotal', str(round(deducciones_total, 2))) def _devengados_total(self): xpaths = [ - '/fe:NominaIndividual/Devengados/Basico/@SueldoTrabajado', - '/fe:NominaIndividual/Devengados/Transporte/@AuxilioTransporte', - '/fe:NominaIndividual/Devengados/Transporte/@ViaticoManuAlojS', - '/fe:NominaIndividual/Devengados/Transporte/@ViaticoManuAlojNS' + self.fexml.xpath_from_root('/Devengados/Basico/@SueldoTrabajado'), + self.fexml.xpath_from_root('/Devengados/Transporte/@AuxilioTransporte'), + self.fexml.xpath_from_root('/Devengados/Transporte/@ViaticoManuAlojS'), + self.fexml.xpath_from_root('/Devengados/Transporte/@ViaticoManuAlojNS') ] devengados = map(lambda valor: Amount(valor), self._values_of_xpaths(xpaths)) @@ -256,7 +260,7 @@ class DIANNominaIndividual: for devengado in devengados: devengados_total += devengado - self.fexml.set_element('/fe:NominaIndividual/DevengadosTotal', str(round(devengados_total,2))) + self.fexml.set_element(self.fexml.xpath_from_root('/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) @@ -270,3 +274,15 @@ class DIANNominaIndividual: xpaths_values.append(xpath_value) return filter(lambda val: val is not None, xpaths_values) + +class DIANNominaIndividual(DIANNominaXML): + + def __init__(self): + super().__init__('NominaIndividual') + + +# TODO(bit4bit) confirmar que no tienen en comun con NominaIndividual +class DIANNominaIndividualDeAjuste(DIANNominaXML): + + def __init__(self): + super().__init__('NominaIndividualDeAjuste') diff --git a/tests/test_facho.py b/tests/test_facho.py index 2a574b7..af6eaa0 100644 --- a/tests/test_facho.py +++ b/tests/test_facho.py @@ -370,3 +370,17 @@ def test_facho_xml_query_element_text_or_attribute(): assert xml.get_element_text_or_attribute('/root/A') == 'contenido' assert xml.get_element_text_or_attribute('/root/A/@clave') == 'valor' + +def test_facho_xml_build_xml_absolute(): + xml = facho.FachoXML('root') + + xpath = xml.xpath_from_root('/A') + assert xpath == '/root/A' + + +def test_facho_xml_build_xml_absolute_namespace(): + xml = facho.FachoXML('{%s}root' % ('http://www.dian.gov.co/contratos/facturaelectronica/v1'), + nsmap={'fe': 'http://www.dian.gov.co/contratos/facturaelectronica/v1'}) + + xpath = xml.xpath_from_root('/A') + assert xpath == '/fe:root/A'