se confirma calculo de devengadostotal,deduccionestotal y comprobantetotal
FossilOrigin-Name: 6c297492215eac22da511abe720a3922432711c194f1bd3b5543f23ff789f58c
This commit is contained in:
		| @@ -98,10 +98,13 @@ class LXMLBuilder: | |||||||
|     def set_text(self, elem, text): |     def set_text(self, elem, text): | ||||||
|         elem.text = text |         elem.text = text | ||||||
|  |  | ||||||
|     def xpath(self, elem, xpath): |     def xpath(self, elem, xpath, multiple=False): | ||||||
|         elems = elem.xpath(xpath, namespaces=self.nsmap) |         elems = elem.xpath(xpath, namespaces=self.nsmap) | ||||||
|         if elems: |         if elems: | ||||||
|             return elems[0] |             if multiple: | ||||||
|  |                 return elems | ||||||
|  |             else: | ||||||
|  |                 return elems[0] | ||||||
|  |  | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
| @@ -351,32 +354,63 @@ class FachoXML: | |||||||
|                 self.builder.set_attribute(elem, k, str(v)) |                 self.builder.set_attribute(elem, k, str(v)) | ||||||
|         return self |         return self | ||||||
|  |  | ||||||
|     def get_element_attribute(self, xpath, attribute): |     def get_element_attribute(self, xpath, attribute, multiple=False): | ||||||
|         elem = self.get_element(xpath) |         elem = self.get_element(xpath, multiple=multiple) | ||||||
|  |  | ||||||
|         if elem is None: |         if elem is None: | ||||||
|             raise ValueError("xpath %s not found" % (xpath)) |             raise ValueError("xpath %s not found" % (xpath)) | ||||||
|  |  | ||||||
|         return self.builder.get_attribute(elem, attribute) |         if multiple: | ||||||
|  |             vals = [] | ||||||
|  |             for e in elem: | ||||||
|  |                 vals.append(self.builder.get_attribute(e, attribute)) | ||||||
|  |             return vals | ||||||
|  |         else: | ||||||
|  |             return self.builder.get_attribute(elem, attribute) | ||||||
|  |  | ||||||
|     def get_element(self, xpath): |     def get_element(self, xpath, multiple=False): | ||||||
|         xpath = self.fragment_prefix + self._path_xpath_for(xpath) |         xpath = self.fragment_prefix + self._path_xpath_for(xpath) | ||||||
|         return self.builder.xpath(self.root, xpath) |         return self.builder.xpath(self.root, xpath, multiple=multiple) | ||||||
|  |  | ||||||
|     def get_element_text(self, xpath, format_=str): |     def get_element_text(self, xpath, format_=str, multiple=False): | ||||||
|         xpath = self.fragment_prefix + self._path_xpath_for(xpath) |         xpath = self.fragment_prefix + self._path_xpath_for(xpath) | ||||||
|         elem = self.builder.xpath(self.root, xpath) |         elem = self.builder.xpath(self.root, xpath, multiple=multiple) | ||||||
|         text = self.builder.get_text(elem) |         if multiple: | ||||||
|         return format_(text) |             vals = [] | ||||||
|  |             for e in elem: | ||||||
|  |                 text = self.builder.get_text(e) | ||||||
|  |                 if text is not None: | ||||||
|  |                     vals.append(format_(text)) | ||||||
|  |             return vals | ||||||
|  |         else: | ||||||
|  |             text = self.builder.get_text(elem) | ||||||
|  |             if text is None: | ||||||
|  |                 return None | ||||||
|  |             return format_(text) | ||||||
|  |  | ||||||
|     def get_element_text_or_attribute(self, xpath): |     def get_element_text_or_attribute(self, xpath, default=None, multiple=False): | ||||||
|         parts = xpath.split('/') |         parts = xpath.split('/') | ||||||
|         is_attribute =  parts[-1].startswith('@') |         is_attribute =  parts[-1].startswith('@') | ||||||
|         if is_attribute: |         if is_attribute: | ||||||
|             attribute_name = parts.pop(-1).lstrip('@') |             attribute_name = parts.pop(-1).lstrip('@') | ||||||
|             element_path = "/".join(parts) |             element_path = "/".join(parts) | ||||||
|             return self.get_element_attribute(element_path, attribute_name) |             try: | ||||||
|  |                 val = self.get_element_attribute(element_path, attribute_name, multiple=multiple) | ||||||
|  |                 if val is None: | ||||||
|  |                     return default | ||||||
|  |                 return val | ||||||
|  |             except KeyError: | ||||||
|  |                 return default | ||||||
|  |             except ValueError: | ||||||
|  |                 return default | ||||||
|         else: |         else: | ||||||
|             return self.get_element_text(xpath) |             try: | ||||||
|  |                 val = self.get_element_text(xpath, multiple=multiple) | ||||||
|  |                 if val is None: | ||||||
|  |                     return default | ||||||
|  |                 return val | ||||||
|  |             except ValueError: | ||||||
|  |                 return default | ||||||
|  |  | ||||||
|     def exist_element(self, xpath): |     def exist_element(self, xpath): | ||||||
|         elem = self.get_element(xpath) |         elem = self.get_element(xpath) | ||||||
|   | |||||||
| @@ -204,33 +204,66 @@ class DIANNominaIndividual: | |||||||
|         return errors |         return errors | ||||||
|  |  | ||||||
|     def toFachoXML(self): |     def toFachoXML(self): | ||||||
|  |         self._devengados_total() | ||||||
|  |         self._deducciones_total() | ||||||
|  |         self._comprobante_total() | ||||||
|  |          | ||||||
|         if self.informacion_general is not None: |         if self.informacion_general is not None: | ||||||
|             #TODO(bit4bit) acoplamiento temporal |             #TODO(bit4bit) acoplamiento temporal | ||||||
|             # es importante el orden de ejecucion |             # es importante el orden de ejecucion | ||||||
|             self._devengados_total() |  | ||||||
|             self._deducciones_total() |  | ||||||
|             self._comprobante_total() |  | ||||||
|          |  | ||||||
|             self.informacion_general.post_apply(self.fexml, self.informacion_general_xml) |             self.informacion_general.post_apply(self.fexml, self.informacion_general_xml) | ||||||
|  |  | ||||||
|         return self.fexml |         return self.fexml | ||||||
|  |  | ||||||
|     def _comprobante_total(self): |     def _comprobante_total(self): | ||||||
|         # TODO |         devengados_total = self.fexml.get_element_text_or_attribute('/fe:NominaIndividual/DevengadosTotal', '0.0') | ||||||
|         self.fexml.set_element('/fe:NominaIndividual/ComprobanteTotal', '2500000.00') |         deducciones_total = self.fexml.get_element_text_or_attribute('/fe:NominaIndividual/DeduccionesTotal', '0.0') | ||||||
|  |  | ||||||
|  |         comprobante_total = Amount(devengados_total) - Amount(deducciones_total) | ||||||
|  |  | ||||||
|  |         self.fexml.set_element('/fe:NominaIndividual/ComprobanteTotal', str(round(comprobante_total, 2))) | ||||||
|  |  | ||||||
|     def _deducciones_total(self): |     def _deducciones_total(self): | ||||||
|         # TODO |         xpaths = [ | ||||||
|         self.fexml.set_element('/fe:NominaIndividual/DeduccionesTotal', '1000000.00') |             '/fe:NominaIndividual/Deducciones/Salud/@Deduccion', | ||||||
|  |             '/fe:NominaIndividual/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.fexml.set_element('/fe:NominaIndividual/DeduccionesTotal', str(round(deducciones_total, 2))) | ||||||
|  |  | ||||||
|     def _devengados_total(self): |     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' | ||||||
|  |         ] | ||||||
|         devengados = map(lambda valor: Amount(valor), |         devengados = map(lambda valor: Amount(valor), | ||||||
|                          [ |                          self._values_of_xpaths(xpaths)) | ||||||
|                              self.fexml.get_element_attribute('/fe:NominaIndividual/Devengados/Basico', 'SueldoTrabajado') |          | ||||||
|                          ] |  | ||||||
|                          ) |  | ||||||
|         devengados_total = Amount(0.0) |         devengados_total = Amount(0.0) | ||||||
|         for devengado in devengados: |         for devengado in devengados: | ||||||
|             devengados_total += devengado |             devengados_total += devengado | ||||||
|         self.fexml.set_element('/fe:NominaIndividual/DevengadosTotal', round(devengados_total,2)) |              | ||||||
|  |         self.fexml.set_element('/fe:NominaIndividual/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 = [] | ||||||
|  |         # toda esta carreta para hacer un aplano de lista | ||||||
|  |         for xpath_values in xpaths_values_of_values: | ||||||
|  |             if xpath_values is None: | ||||||
|  |                 continue | ||||||
|  |  | ||||||
|  |             for xpath_value in xpath_values: | ||||||
|  |                 xpaths_values.append(xpath_value) | ||||||
|  |  | ||||||
|  |         return filter(lambda val: val is not None, xpaths_values) | ||||||
|   | |||||||
| @@ -32,6 +32,41 @@ def test_adicionar_devengado_transporte(): | |||||||
|  |  | ||||||
|     assert xml.get_element_attribute('/fe:NominaIndividual/Devengados/Transporte', 'AuxilioTransporte') == '2000000.0' |     assert xml.get_element_attribute('/fe:NominaIndividual/Devengados/Transporte', 'AuxilioTransporte') == '2000000.0' | ||||||
|  |  | ||||||
|  | def test_adicionar_devengado_comprobante_total(): | ||||||
|  |     nomina = fe.nomina.DIANNominaIndividual() | ||||||
|  |  | ||||||
|  |     nomina.adicionar_devengado(fe.nomina.DevengadoBasico( | ||||||
|  |         dias_trabajados = 60, | ||||||
|  |         sueldo_trabajado = fe.nomina.Amount(2_000_000) | ||||||
|  |     )) | ||||||
|  |  | ||||||
|  |     nomina.adicionar_deduccion(fe.nomina.DeduccionSalud( | ||||||
|  |         porcentaje = fe.nomina.Amount(19), | ||||||
|  |         deduccion = fe.nomina.Amount(1_000_000) | ||||||
|  |     )) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     xml = nomina.toFachoXML() | ||||||
|  |  | ||||||
|  |     assert xml.get_element_text('/fe:NominaIndividual/ComprobanteTotal') == '1000000.00' | ||||||
|  |  | ||||||
|  | def test_adicionar_devengado_comprobante_total_cero(): | ||||||
|  |     nomina = fe.nomina.DIANNominaIndividual() | ||||||
|  |  | ||||||
|  |     nomina.adicionar_devengado(fe.nomina.DevengadoBasico( | ||||||
|  |         dias_trabajados = 60, | ||||||
|  |         sueldo_trabajado = fe.nomina.Amount(1_000_000) | ||||||
|  |     )) | ||||||
|  |  | ||||||
|  |     nomina.adicionar_deduccion(fe.nomina.DeduccionSalud( | ||||||
|  |         porcentaje = fe.nomina.Amount(19), | ||||||
|  |         deduccion = fe.nomina.Amount(1_000_000) | ||||||
|  |     )) | ||||||
|  |  | ||||||
|  |     xml = nomina.toFachoXML() | ||||||
|  |  | ||||||
|  |     assert xml.get_element_text('/fe:NominaIndividual/ComprobanteTotal') == '0.00' | ||||||
|  |  | ||||||
| def test_adicionar_devengado_transporte_muchos(): | def test_adicionar_devengado_transporte_muchos(): | ||||||
|     nomina = fe.nomina.DIANNominaIndividual() |     nomina = fe.nomina.DIANNominaIndividual() | ||||||
|  |  | ||||||
| @@ -45,12 +80,16 @@ def test_adicionar_devengado_transporte_muchos(): | |||||||
|  |  | ||||||
|     xml = nomina.toFachoXML() |     xml = nomina.toFachoXML() | ||||||
|     print(xml) |     print(xml) | ||||||
|     assert str(xml) == """<NominaIndividual xmlns:facho="http://git.disroot.org/Etrivial/facho" xmlns="http://www.dian.gov.co/contratos/facturaelectronica/v1" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cdt="urn:DocumentInformation:names:specification:ubl:colombia:schema:xsd:DocumentInformationAggregateComponents-1" xmlns:clm54217="urn:un:unece:uncefact:codelist:specification:54217:2001" xmlns:clmIANAMIMEMediaType="urn:un:unece:uncefact:codelist:specification:IANAMIMEMediaType:2003" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns:qdt="urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2" xmlns:sts="dian:gov:co:facturaelectronica:Structures-2-1" xmlns:udt="urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:sig="http://www.w3.org/2000/09/xmldsig#"><NumeroSecuenciaXML/><InformacionGeneral/><Empleador/><Trabajador/><Devengados><Basico/><Transporte AuxilioTransporte="2000000.0"/><Transporte AuxilioTransporte="3000000.0"/></Devengados><Deducciones/></NominaIndividual>""" |     assert xml.get_element_text('/fe:NominaIndividual/DevengadosTotal') == '5000000.00' | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_adicionar_deduccion_salud(): | def test_adicionar_deduccion_salud(): | ||||||
|     nomina = fe.nomina.DIANNominaIndividual() |     nomina = fe.nomina.DIANNominaIndividual() | ||||||
|  |  | ||||||
|  |     nomina.adicionar_devengado(fe.nomina.DevengadoBasico( | ||||||
|  |         dias_trabajados = 60, | ||||||
|  |         sueldo_trabajado = fe.nomina.Amount(1000) | ||||||
|  |     )) | ||||||
|  |  | ||||||
|     nomina.adicionar_deduccion(fe.nomina.DeduccionSalud( |     nomina.adicionar_deduccion(fe.nomina.DeduccionSalud( | ||||||
|         porcentaje = fe.nomina.Amount(19), |         porcentaje = fe.nomina.Amount(19), | ||||||
|         deduccion = fe.nomina.Amount(1000) |         deduccion = fe.nomina.Amount(1000) | ||||||
| @@ -58,7 +97,7 @@ def test_adicionar_deduccion_salud(): | |||||||
|  |  | ||||||
|     xml = nomina.toFachoXML() |     xml = nomina.toFachoXML() | ||||||
|     print(xml) |     print(xml) | ||||||
|     assert str(xml) == """<NominaIndividual xmlns:facho="http://git.disroot.org/Etrivial/facho" xmlns="http://www.dian.gov.co/contratos/facturaelectronica/v1" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cdt="urn:DocumentInformation:names:specification:ubl:colombia:schema:xsd:DocumentInformationAggregateComponents-1" xmlns:clm54217="urn:un:unece:uncefact:codelist:specification:54217:2001" xmlns:clmIANAMIMEMediaType="urn:un:unece:uncefact:codelist:specification:IANAMIMEMediaType:2003" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2" xmlns:qdt="urn:oasis:names:specification:ubl:schema:xsd:QualifiedDatatypes-2" xmlns:sts="dian:gov:co:facturaelectronica:Structures-2-1" xmlns:udt="urn:un:unece:uncefact:data:specification:UnqualifiedDataTypesSchemaModule:2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:sig="http://www.w3.org/2000/09/xmldsig#"><NumeroSecuenciaXML/><InformacionGeneral/><Empleador/><Trabajador/><Devengados><Basico/></Devengados><Deducciones><Salud Porcentaje="19.0" Deduccion="1000.0"/></Deducciones></NominaIndividual>""" |     assert xml.get_element_text('/fe:NominaIndividual/DeduccionesTotal') == '1000.00' | ||||||
|  |  | ||||||
| def test_nomina_obligatorios_segun_anexo_tecnico(): | def test_nomina_obligatorios_segun_anexo_tecnico(): | ||||||
|     nomina = fe.nomina.DIANNominaIndividual() |     nomina = fe.nomina.DIANNominaIndividual() | ||||||
| @@ -98,6 +137,11 @@ def test_nomina_cune(): | |||||||
|         sueldo_trabajado = fe.nomina.Amount(3_500_000) |         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) | ||||||
|  |     )) | ||||||
|  |  | ||||||
|     xml = nomina.toFachoXML() |     xml = nomina.toFachoXML() | ||||||
|     assert xml.get_element_attribute('/fe:NominaIndividual/InformacionGeneral', 'CUNE') == '16560dc8956122e84ffb743c817fe7d494e058a44d9ca3fa4c234c268b4f766003253fbee7ea4af9682dd57210f3bac2' |     assert xml.get_element_attribute('/fe:NominaIndividual/InformacionGeneral', 'CUNE') == '16560dc8956122e84ffb743c817fe7d494e058a44d9ca3fa4c234c268b4f766003253fbee7ea4af9682dd57210f3bac2' | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user