Commit 574e9a9f by Vanesa Puma Yamo

Merge remote-tracking branch 'origin/master'

parents 6a427564 b23c9206
...@@ -14,7 +14,7 @@ mensual (columnas Ene..Dic + Total Anual): ...@@ -14,7 +14,7 @@ mensual (columnas Ene..Dic + Total Anual):
| **1. Ingresos fijos** | `basico`, `asignacion_familiar`, `movilidad` | | **1. Ingresos fijos** | `basico`, `asignacion_familiar`, `movilidad` |
| **2. Extras y variables** | `horas_extras_25` + `horas_extras_35`, `apoyo_salud` + `asignacion_estudios` + `bono_desempeno` (apoyo y asignaciones), `dominical`, `canasta_navidad`+`vale_consumo`+`obsequios`, `bono_extraordinario`+`bono_productividad`+`bono_aniversario`+`reintegro` (bonos), `suma_graciosa` | | **2. Extras y variables** | `horas_extras_25` + `horas_extras_35`, `apoyo_salud` + `asignacion_estudios` + `bono_desempeno` (apoyo y asignaciones), `dominical`, `canasta_navidad`+`vale_consumo`+`obsequios`, `bono_extraordinario`+`bono_productividad`+`bono_aniversario`+`reintegro` (bonos), `suma_graciosa` |
| **3. Vacaciones y liquidación** | `vacaciones_truncas` (compra de vacaciones), `vacaciones_gozadas_rem_vaca` (vacaciones truncas), `gratificacion_proporcional` (gratificación trunca), `bonificacion_extraordinaria` (bonificación extraordinaria trunca) | | **3. Vacaciones y liquidación** | `vacaciones_truncas` (compra de vacaciones), `vacaciones_gozadas_rem_vaca` (vacaciones truncas), `gratificacion_proporcional` (gratificación trunca), `bonificacion_extraordinaria` (bonificación extraordinaria trunca) |
| **4. Anuales excepcionales** | `gratificacion_julio_diciembre` (gratificación), utilidades (fila "UTILIDADES", sin columna dedicada aún en la tabla) | | **4. Anuales excepcionales** | `gratificacion_julio_diciembre` (gratificación), `utilidades` (fila "UTILIDADES") |
Cada bloque tiene una fila de subtotal (`Subtotal Ingresos Fijos`, `Subtotal Extras y Cada bloque tiene una fila de subtotal (`Subtotal Ingresos Fijos`, `Subtotal Extras y
Variables`, `Subtotal Vacaciones y Liquidación`, `Subtotal Anuales Excepcionales`). La suma de Variables`, `Subtotal Vacaciones y Liquidación`, `Subtotal Anuales Excepcionales`). La suma de
......
...@@ -168,7 +168,9 @@ GET /api/v1/renta-quinta/detalle-completo?numeroDocumento=09742695&anio=2026&mes ...@@ -168,7 +168,9 @@ GET /api/v1/renta-quinta/detalle-completo?numeroDocumento=09742695&anio=2026&mes
⚠️ **Pendiente / limitación conocida**: en el Excel de referencia, "Utilidades Distribuidas" y ⚠️ **Pendiente / limitación conocida**: en el Excel de referencia, "Utilidades Distribuidas" y
"Otros Empleadores" son datos que no existen en `planilla.renta_quinta` (el segundo, además, se "Otros Empleadores" son datos que no existen en `planilla.renta_quinta` (el segundo, además, se
edita manualmente en el Excel). Mientras no se defina una fuente de datos para ellos, el API edita manualmente en el Excel). Mientras no se defina una fuente de datos para ellos, el API
**siempre devuelve 0.00** en esos campos. **siempre devuelve 0.00** en esos campos. `utilidades` sí sale de la columna homónima de la
tabla y suma al `totalBrutoRemunerativo` del bloque; en la columna de proyección va 0.00 (no se
proyectan utilidades futuras).
Si no existe boleta para ese `numeroDocumento` + `anio` + `mes`: **404** con el cuerpo de error Si no existe boleta para ese `numeroDocumento` + `anio` + `mes`: **404** con el cuerpo de error
estándar (ver sección 5). estándar (ver sección 5).
......
...@@ -53,6 +53,7 @@ CREATE TABLE IF NOT EXISTS planilla.renta_quinta ...@@ -53,6 +53,7 @@ CREATE TABLE IF NOT EXISTS planilla.renta_quinta
cts numeric(15,2), cts numeric(15,2),
gratificacion_proporcional numeric(15,2), gratificacion_proporcional numeric(15,2),
gratificacion_julio_diciembre numeric(15,2), gratificacion_julio_diciembre numeric(15,2),
utilidades numeric(15,2),
subsidio_enfermedad numeric(15,2), subsidio_enfermedad numeric(15,2),
licencia_goce_haber numeric(15,2), licencia_goce_haber numeric(15,2),
subsidio_maternidad numeric(15,2), subsidio_maternidad numeric(15,2),
......
...@@ -163,6 +163,9 @@ public class RentaQuinta { ...@@ -163,6 +163,9 @@ public class RentaQuinta {
@Column(name = "gratificacion_julio_diciembre", precision = 15, scale = 2) @Column(name = "gratificacion_julio_diciembre", precision = 15, scale = 2)
private BigDecimal gratificacionJulioDiciembre; private BigDecimal gratificacionJulioDiciembre;
@Column(name = "utilidades", precision = 15, scale = 2)
private BigDecimal utilidades;
/* VACACIONES Y LIQUIDACIÓN */ /* VACACIONES Y LIQUIDACIÓN */
@Column(name = "cts", precision = 15, scale = 2) @Column(name = "cts", precision = 15, scale = 2)
......
...@@ -127,10 +127,21 @@ public class RentaQuintaMapper { ...@@ -127,10 +127,21 @@ public class RentaQuintaMapper {
return new RentaQuintaDetalleCompletoDTO(toDetalle(boleta), registro, parametros, liquidacion, escala, control); return new RentaQuintaDetalleCompletoDTO(toDetalle(boleta), registro, parametros, liquidacion, escala, control);
} }
/**
* Total bruto remunerativo de los meses que cumplen el filtro: mismo criterio que
* {@code RentaQuintaRepository.CONCEPTOS_MES}, aplicado en memoria sobre el historial anual
* ya cargado. Lo usa el service para que el cálculo de la boleta y el de la grilla partan de
* la misma base.
*/
public BigDecimal totalBrutoMes(List<RentaQuinta> historial, Predicate<RentaQuinta> filtro) {
return bloque(historial, filtro).totalBrutoRemunerativo();
}
private BloqueIngresosDTO bloque(List<RentaQuinta> historial, Predicate<RentaQuinta> filtro) { private BloqueIngresosDTO bloque(List<RentaQuinta> historial, Predicate<RentaQuinta> filtro) {
BigDecimal sueldoBruto = BigDecimal.ZERO; BigDecimal sueldoBruto = BigDecimal.ZERO;
BigDecimal horasExtras = BigDecimal.ZERO; BigDecimal horasExtras = BigDecimal.ZERO;
BigDecimal gratificaciones = BigDecimal.ZERO; BigDecimal gratificaciones = BigDecimal.ZERO;
BigDecimal utilidades = BigDecimal.ZERO;
for (RentaQuinta r : historial) { for (RentaQuinta r : historial) {
if (!filtro.test(r)) { if (!filtro.test(r)) {
continue; continue;
...@@ -167,9 +178,10 @@ public class RentaQuintaMapper { ...@@ -167,9 +178,10 @@ public class RentaQuintaMapper {
.add(orZero(r.getBonificacionExtraordinaria())) .add(orZero(r.getBonificacionExtraordinaria()))
.add(orZero(r.getBonificacionExtraordinaria2())) .add(orZero(r.getBonificacionExtraordinaria2()))
.add(orZero(r.getGratificacionJulioDiciembre())); .add(orZero(r.getGratificacionJulioDiciembre()));
utilidades = utilidades.add(orZero(r.getUtilidades()));
} }
BigDecimal total = sueldoBruto.add(horasExtras).add(gratificaciones); BigDecimal total = sueldoBruto.add(horasExtras).add(gratificaciones).add(utilidades);
return new BloqueIngresosDTO(sueldoBruto, horasExtras, gratificaciones, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, total); return new BloqueIngresosDTO(sueldoBruto, horasExtras, gratificaciones, utilidades, BigDecimal.ZERO, BigDecimal.ZERO, total);
} }
private BigDecimal orZero(BigDecimal v) { private BigDecimal orZero(BigDecimal v) {
......
...@@ -61,7 +61,8 @@ public interface RentaQuintaRepository extends JpaRepository<RentaQuinta, Long> ...@@ -61,7 +61,8 @@ public interface RentaQuintaRepository extends JpaRepository<RentaQuinta, Long>
+ coalesce(r.sumaGraciosa, 0) + coalesce(r.sumaGraciosa, 0)
+ coalesce(r.bonificacionExtraordinaria, 0) + coalesce(r.bonificacionExtraordinaria, 0)
+ coalesce(r.bonificacionExtraordinaria2, 0) + coalesce(r.bonificacionExtraordinaria2, 0)
+ coalesce(r.gratificacionJulioDiciembre, 0)) + coalesce(r.gratificacionJulioDiciembre, 0)
+ coalesce(r.utilidades, 0))
"""; """;
/** Meses de un trabajador en un año, ordenados cronológicamente (historial anual). */ /** Meses de un trabajador en un año, ordenados cronológicamente (historial anual). */
...@@ -72,9 +73,6 @@ public interface RentaQuintaRepository extends JpaRepository<RentaQuinta, Long> ...@@ -72,9 +73,6 @@ public interface RentaQuintaRepository extends JpaRepository<RentaQuinta, Long>
Optional<RentaQuinta> findByCodperAndBoletaAnioAndBoletaMes( Optional<RentaQuinta> findByCodperAndBoletaAnioAndBoletaMes(
Integer codper, Integer boletaAnio, Integer boletaMes); Integer codper, Integer boletaAnio, Integer boletaMes);
Optional<RentaQuinta> findByNumeroDocumentoAndBoletaAnioAndBoletaMes(
String numeroDocumento, Integer boletaAnio, Integer boletaMes);
/** /**
* Agregado anual por trabajador hasta el mes de corte: acumulado de meses previos * Agregado anual por trabajador hasta el mes de corte: acumulado de meses previos
* (historial real / retenido) y valores del propio mes de corte (planilla cargada / * (historial real / retenido) y valores del propio mes de corte (planilla cargada /
......
...@@ -22,6 +22,8 @@ import so.planilla_be.rentaquinta.service.RentaQuintaService; ...@@ -22,6 +22,8 @@ import so.planilla_be.rentaquinta.service.RentaQuintaService;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
@Service @Service
@Transactional(readOnly = true) @Transactional(readOnly = true)
...@@ -104,62 +106,42 @@ public class RentaQuintaServiceImpl implements RentaQuintaService { ...@@ -104,62 +106,42 @@ public class RentaQuintaServiceImpl implements RentaQuintaService {
@Override @Override
public RentaQuintaDetalleCompletoDTO obtenerDetalleCompleto(String numeroDocumento, Integer anio, Integer mes) { public RentaQuintaDetalleCompletoDTO obtenerDetalleCompleto(String numeroDocumento, Integer anio, Integer mes) {
RentaQuinta boleta = repository.findByNumeroDocumentoAndBoletaAnioAndBoletaMes(numeroDocumento, anio, mes) List<RentaQuinta> historialAnio = repository.historialAnualPorDocumento(numeroDocumento, anio);
RentaQuinta boleta = historialAnio.stream()
.filter(r -> r.getBoletaMes().equals(mes))
.findFirst()
.orElseThrow(() -> new RentaQuintaNoEncontradaException( .orElseThrow(() -> new RentaQuintaNoEncontradaException(
"No existe boleta para documento %s, periodo %d-%02d".formatted(numeroDocumento, anio, mes))); "No existe boleta para documento %s, periodo %d-%02d".formatted(numeroDocumento, anio, mes)));
List<RentaQuinta> historialAnio = repository.historialAnualPorDocumento(numeroDocumento, anio); int mesCorte = mes;
int mesCorte = boleta.getBoletaMes();
BigDecimal historialReal = sumaConceptos(historialAnio, r -> r.getBoletaMes() < mesCorte); BigDecimal historialReal = mapper.totalBrutoMes(historialAnio, r -> r.getBoletaMes() < mesCorte);
BigDecimal planillaCargada = sumaConceptos(historialAnio, r -> r.getBoletaMes().equals(mesCorte)); BigDecimal planillaCargada = mapper.totalBrutoMes(historialAnio, r -> r.getBoletaMes().equals(mesCorte));
BigDecimal retenidoAnterior = historialAnio.stream() BigDecimal retenidoAnterior = sumaMesCorte(historialAnio, r -> r.getBoletaMes() < mesCorte,
.filter(r -> r.getBoletaMes() < mesCorte) RentaQuinta::getRenta5taCategoria);
.map(r -> orZero(r.getRenta5taCategoria()))
.reduce(BigDecimal.ZERO, BigDecimal::add); // La grilla agrega todas las filas del documento en el mes de corte (una persona puede
// tener varios codper/tipo de planilla); la base de proyección aquí debe hacer lo mismo.
BigDecimal sueldoBrutoMesCorte = sumaMesCorte(historialAnio, esMesCorte(mesCorte), RentaQuinta::getSueldoBruto);
BigDecimal basicoMesCorte = sumaMesCorte(historialAnio, esMesCorte(mesCorte), RentaQuinta::getBasico);
BigDecimal asignacionFamiliarMesCorte =
sumaMesCorte(historialAnio, esMesCorte(mesCorte), RentaQuinta::getAsignacionFamiliar);
CalculoRentaQuinta calculo = calculadora.calcular(new DatosCalculo( CalculoRentaQuinta calculo = calculadora.calcular(new DatosCalculo(
boleta.getBoletaAnio(), mesCorte, historialReal, planillaCargada, anio, mesCorte, historialReal, planillaCargada,
boleta.getSueldoBruto(), boleta.getBasico(), boleta.getAsignacionFamiliar(), retenidoAnterior)); sueldoBrutoMesCorte, basicoMesCorte, asignacionFamiliarMesCorte, retenidoAnterior));
return mapper.toDetalleCompleto(boleta, historialAnio, mesCorte, calculo); return mapper.toDetalleCompleto(boleta, historialAnio, mesCorte, calculo);
} }
/** private Predicate<RentaQuinta> esMesCorte(int mesCorte) {
* Total bruto remunerativo de un mes: mismo criterio que {@code RentaQuintaRepository.CONCEPTOS_MES}, return r -> r.getBoletaMes().equals(mesCorte);
* aplicado en memoria sobre el historial anual ya cargado del trabajador. }
*/
private BigDecimal sumaConceptos(List<RentaQuinta> historial, java.util.function.Predicate<RentaQuinta> filtro) { private BigDecimal sumaMesCorte(List<RentaQuinta> historial, Predicate<RentaQuinta> filtro,
Function<RentaQuinta, BigDecimal> campo) {
return historial.stream() return historial.stream()
.filter(filtro) .filter(filtro)
.map(r -> orZero(r.getBasico()) .map(r -> orZero(campo.apply(r)))
.add(orZero(r.getVacacionesGozadasRemVaca()))
.add(orZero(r.getLicenciaGoceHaber()))
.add(orZero(r.getLicenciaSaludFamiliarLey30012()))
.add(orZero(r.getAsignacionFamiliar()))
.add(orZero(r.getMovilidad()))
.add(orZero(r.getHorasExtras25()))
.add(orZero(r.getHorasExtras35()))
.add(orZero(r.getDominical()))
.add(orZero(r.getBonoExtraordinario()))
.add(orZero(r.getBonoProductividad()))
.add(orZero(r.getBonoDesempeno()))
.add(orZero(r.getBonoAniversario()))
.add(orZero(r.getApoyoSalud()))
.add(orZero(r.getAsignacionEstudios()))
.add(orZero(r.getAsignacionFallecimientoFamiliar()))
.add(orZero(r.getReintegro()))
.add(orZero(r.getCanastaNavidad()))
.add(orZero(r.getValeConsumo()))
.add(orZero(r.getObsequios()))
.add(orZero(r.getCanastaNavidad2()))
.add(orZero(r.getValeConsumoDescuento()))
.add(orZero(r.getCompensacionVacacional()))
.add(orZero(r.getGratificacionProporcional()))
.add(orZero(r.getVacacionesTruncas()))
.add(orZero(r.getSumaGraciosa()))
.add(orZero(r.getBonificacionExtraordinaria()))
.add(orZero(r.getBonificacionExtraordinaria2()))
.add(orZero(r.getGratificacionJulioDiciembre())))
.reduce(BigDecimal.ZERO, BigDecimal::add); .reduce(BigDecimal.ZERO, BigDecimal::add);
} }
......
...@@ -28,6 +28,7 @@ create table renta_quinta ...@@ -28,6 +28,7 @@ create table renta_quinta
gratificacion_proporcional numeric(15, 2), gratificacion_proporcional numeric(15, 2),
bonificacion_extraordinaria numeric(15, 2), bonificacion_extraordinaria numeric(15, 2),
gratificacion_julio_diciembre numeric(15, 2), gratificacion_julio_diciembre numeric(15, 2),
utilidades numeric(15, 2),
bonificacion_extraordinaria_2 numeric(15, 2), bonificacion_extraordinaria_2 numeric(15, 2),
subsidio_enfermedad numeric(15, 2), subsidio_enfermedad numeric(15, 2),
bono_productividad numeric(15, 2), bono_productividad numeric(15, 2),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment