From 3ec31c36b46710ba3032bca89e41f0d6e5535d61 Mon Sep 17 00:00:00 2001 From: bit4bit Date: Sat, 6 Nov 2021 21:26:51 +0000 Subject: [PATCH] se confirma calculo de devengadostotal,deduccionestotal y comprobantetotal FossilOrigin-Name: 6c297492215eac22da511abe720a3922432711c194f1bd3b5543f23ff789f58c --- facho/facho.py | 62 ++++++++++++++++++++++++++++--------- facho/fe/nomina/__init__.py | 59 +++++++++++++++++++++++++++-------- tests/test_nomina.py | 50 ++++++++++++++++++++++++++++-- 3 files changed, 141 insertions(+), 30 deletions(-) diff --git a/facho/facho.py b/facho/facho.py index fe769f2..d87eef0 100644 --- a/facho/facho.py +++ b/facho/facho.py @@ -98,10 +98,13 @@ 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: - return elems[0] + 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)) - 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) - 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) - text = self.builder.get_text(elem) - return format_(text) + 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) diff --git a/facho/fe/nomina/__init__.py b/facho/fe/nomina/__init__.py index 9ec385a..f52db51 100644 --- a/facho/fe/nomina/__init__.py +++ b/facho/fe/nomina/__init__.py @@ -204,33 +204,66 @@ class DIANNominaIndividual: return errors def toFachoXML(self): + 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._devengados_total() - self._deducciones_total() - self._comprobante_total() - + 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): + 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.fexml.get_element_attribute('/fe:NominaIndividual/Devengados/Basico', 'SueldoTrabajado') - ] - ) + 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) diff --git a/tests/test_nomina.py b/tests/test_nomina.py index 5881d1e..e65893a 100644 --- a/tests/test_nomina.py +++ b/tests/test_nomina.py @@ -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) == """""" - + 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) == """""" + 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'