se confirma calculo de devengadostotal,deduccionestotal y comprobantetotal
FossilOrigin-Name: 6c297492215eac22da511abe720a3922432711c194f1bd3b5543f23ff789f58c
This commit is contained in:
		| @@ -98,9 +98,12 @@ class LXMLBuilder: | ||||
|     def set_text(self, elem, text): | ||||
|         elem.text = text | ||||
|  | ||||
|     def xpath(self, elem, xpath): | ||||
|     def xpath(self, elem, xpath, multiple=False): | ||||
|         elems = elem.xpath(xpath, namespaces=self.nsmap) | ||||
|         if elems: | ||||
|             if multiple: | ||||
|                 return elems | ||||
|             else: | ||||
|                 return elems[0] | ||||
|  | ||||
|         return None | ||||
| @@ -351,32 +354,63 @@ class FachoXML: | ||||
|                 self.builder.set_attribute(elem, k, str(v)) | ||||
|         return self | ||||
|  | ||||
|     def get_element_attribute(self, xpath, attribute): | ||||
|         elem = self.get_element(xpath) | ||||
|     def get_element_attribute(self, xpath, attribute, multiple=False): | ||||
|         elem = self.get_element(xpath, multiple=multiple) | ||||
|  | ||||
|         if elem is None: | ||||
|             raise ValueError("xpath %s not found" % (xpath)) | ||||
|  | ||||
|         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) | ||||
|         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) | ||||
|         elem = self.builder.xpath(self.root, xpath) | ||||
|         elem = self.builder.xpath(self.root, xpath, multiple=multiple) | ||||
|         if multiple: | ||||
|             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('/') | ||||
|         is_attribute =  parts[-1].startswith('@') | ||||
|         if is_attribute: | ||||
|             attribute_name = parts.pop(-1).lstrip('@') | ||||
|             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: | ||||
|             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): | ||||
|         elem = self.get_element(xpath) | ||||
|   | ||||
| @@ -204,33 +204,66 @@ class DIANNominaIndividual: | ||||
|         return errors | ||||
|  | ||||
|     def toFachoXML(self): | ||||
|         if self.informacion_general is not None: | ||||
|             #TODO(bit4bit) acoplamiento temporal | ||||
|             # es importante el orden de ejecucion | ||||
|         self._devengados_total() | ||||
|         self._deducciones_total() | ||||
|         self._comprobante_total() | ||||
|          | ||||
|         if self.informacion_general is not None: | ||||
|             #TODO(bit4bit) acoplamiento temporal | ||||
|             # es importante el orden de ejecucion | ||||
|  | ||||
|             self.informacion_general.post_apply(self.fexml, self.informacion_general_xml) | ||||
|  | ||||
|         return self.fexml | ||||
|  | ||||
|     def _comprobante_total(self): | ||||
|         # TODO | ||||
|         self.fexml.set_element('/fe:NominaIndividual/ComprobanteTotal', '2500000.00') | ||||
|         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') | ||||
|  | ||||
|         comprobante_total = Amount(devengados_total) - Amount(deducciones_total) | ||||
|  | ||||
|         self.fexml.set_element('/fe:NominaIndividual/ComprobanteTotal', str(round(comprobante_total, 2))) | ||||
|  | ||||
|     def _deducciones_total(self): | ||||
|         # TODO | ||||
|         self.fexml.set_element('/fe:NominaIndividual/DeduccionesTotal', '1000000.00') | ||||
|         xpaths = [ | ||||
|             '/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): | ||||
|         devengados = map(lambda valor: Amount(valor), | ||||
|                          [ | ||||
|                              self.fexml.get_element_attribute('/fe:NominaIndividual/Devengados/Basico', 'SueldoTrabajado') | ||||
|         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), | ||||
|                          self._values_of_xpaths(xpaths)) | ||||
|          | ||||
|         devengados_total = Amount(0.0) | ||||
|         for devengado in devengados: | ||||
|             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' | ||||
|  | ||||
| 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(): | ||||
|     nomina = fe.nomina.DIANNominaIndividual() | ||||
|  | ||||
| @@ -45,12 +80,16 @@ def test_adicionar_devengado_transporte_muchos(): | ||||
|  | ||||
|     xml = nomina.toFachoXML() | ||||
|     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(): | ||||
|     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( | ||||
|         porcentaje = fe.nomina.Amount(19), | ||||
|         deduccion = fe.nomina.Amount(1000) | ||||
| @@ -58,7 +97,7 @@ def test_adicionar_deduccion_salud(): | ||||
|  | ||||
|     xml = nomina.toFachoXML() | ||||
|     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(): | ||||
|     nomina = fe.nomina.DIANNominaIndividual() | ||||
| @@ -98,6 +137,11 @@ def test_nomina_cune(): | ||||
|         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() | ||||
|     assert xml.get_element_attribute('/fe:NominaIndividual/InformacionGeneral', 'CUNE') == '16560dc8956122e84ffb743c817fe7d494e058a44d9ca3fa4c234c268b4f766003253fbee7ea4af9682dd57210f3bac2' | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user