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):
| **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` |
| **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
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
⚠️ **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
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
estándar (ver sección 5).
......
......@@ -53,6 +53,7 @@ CREATE TABLE IF NOT EXISTS planilla.renta_quinta
cts numeric(15,2),
gratificacion_proporcional numeric(15,2),
gratificacion_julio_diciembre numeric(15,2),
utilidades numeric(15,2),
subsidio_enfermedad numeric(15,2),
licencia_goce_haber numeric(15,2),
subsidio_maternidad numeric(15,2),
......
......@@ -163,6 +163,9 @@ public class RentaQuinta {
@Column(name = "gratificacion_julio_diciembre", precision = 15, scale = 2)
private BigDecimal gratificacionJulioDiciembre;
@Column(name = "utilidades", precision = 15, scale = 2)
private BigDecimal utilidades;
/* VACACIONES Y LIQUIDACIÓN */
@Column(name = "cts", precision = 15, scale = 2)
......
......@@ -127,10 +127,21 @@ public class RentaQuintaMapper {
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) {
BigDecimal sueldoBruto = BigDecimal.ZERO;
BigDecimal horasExtras = BigDecimal.ZERO;
BigDecimal gratificaciones = BigDecimal.ZERO;
BigDecimal utilidades = BigDecimal.ZERO;
for (RentaQuinta r : historial) {
if (!filtro.test(r)) {
continue;
......@@ -167,9 +178,10 @@ public class RentaQuintaMapper {
.add(orZero(r.getBonificacionExtraordinaria()))
.add(orZero(r.getBonificacionExtraordinaria2()))
.add(orZero(r.getGratificacionJulioDiciembre()));
utilidades = utilidades.add(orZero(r.getUtilidades()));
}
BigDecimal total = sueldoBruto.add(horasExtras).add(gratificaciones);
return new BloqueIngresosDTO(sueldoBruto, horasExtras, gratificaciones, BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO, total);
BigDecimal total = sueldoBruto.add(horasExtras).add(gratificaciones).add(utilidades);
return new BloqueIngresosDTO(sueldoBruto, horasExtras, gratificaciones, utilidades, BigDecimal.ZERO, BigDecimal.ZERO, total);
}
private BigDecimal orZero(BigDecimal v) {
......
......@@ -61,7 +61,8 @@ public interface RentaQuintaRepository extends JpaRepository<RentaQuinta, Long>
+ coalesce(r.sumaGraciosa, 0)
+ coalesce(r.bonificacionExtraordinaria, 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). */
......@@ -72,9 +73,6 @@ public interface RentaQuintaRepository extends JpaRepository<RentaQuinta, Long>
Optional<RentaQuinta> findByCodperAndBoletaAnioAndBoletaMes(
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
* (historial real / retenido) y valores del propio mes de corte (planilla cargada /
......
......@@ -22,6 +22,8 @@ import so.planilla_be.rentaquinta.service.RentaQuintaService;
import java.math.BigDecimal;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
@Service
@Transactional(readOnly = true)
......@@ -104,62 +106,42 @@ public class RentaQuintaServiceImpl implements RentaQuintaService {
@Override
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(
"No existe boleta para documento %s, periodo %d-%02d".formatted(numeroDocumento, anio, mes)));
List<RentaQuinta> historialAnio = repository.historialAnualPorDocumento(numeroDocumento, anio);
int mesCorte = boleta.getBoletaMes();
int mesCorte = mes;
BigDecimal historialReal = sumaConceptos(historialAnio, r -> r.getBoletaMes() < mesCorte);
BigDecimal planillaCargada = sumaConceptos(historialAnio, r -> r.getBoletaMes().equals(mesCorte));
BigDecimal retenidoAnterior = historialAnio.stream()
.filter(r -> r.getBoletaMes() < mesCorte)
.map(r -> orZero(r.getRenta5taCategoria()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal historialReal = mapper.totalBrutoMes(historialAnio, r -> r.getBoletaMes() < mesCorte);
BigDecimal planillaCargada = mapper.totalBrutoMes(historialAnio, r -> r.getBoletaMes().equals(mesCorte));
BigDecimal retenidoAnterior = sumaMesCorte(historialAnio, r -> r.getBoletaMes() < mesCorte,
RentaQuinta::getRenta5taCategoria);
// 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(
boleta.getBoletaAnio(), mesCorte, historialReal, planillaCargada,
boleta.getSueldoBruto(), boleta.getBasico(), boleta.getAsignacionFamiliar(), retenidoAnterior));
anio, mesCorte, historialReal, planillaCargada,
sueldoBrutoMesCorte, basicoMesCorte, asignacionFamiliarMesCorte, retenidoAnterior));
return mapper.toDetalleCompleto(boleta, historialAnio, mesCorte, calculo);
}
/**
* Total bruto remunerativo de un mes: mismo criterio que {@code RentaQuintaRepository.CONCEPTOS_MES},
* aplicado en memoria sobre el historial anual ya cargado del trabajador.
*/
private BigDecimal sumaConceptos(List<RentaQuinta> historial, java.util.function.Predicate<RentaQuinta> filtro) {
private Predicate<RentaQuinta> esMesCorte(int mesCorte) {
return r -> r.getBoletaMes().equals(mesCorte);
}
private BigDecimal sumaMesCorte(List<RentaQuinta> historial, Predicate<RentaQuinta> filtro,
Function<RentaQuinta, BigDecimal> campo) {
return historial.stream()
.filter(filtro)
.map(r -> orZero(r.getBasico())
.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())))
.map(r -> orZero(campo.apply(r)))
.reduce(BigDecimal.ZERO, BigDecimal::add);
}
......
......@@ -28,6 +28,7 @@ create table renta_quinta
gratificacion_proporcional numeric(15, 2),
bonificacion_extraordinaria numeric(15, 2),
gratificacion_julio_diciembre numeric(15, 2),
utilidades numeric(15, 2),
bonificacion_extraordinaria_2 numeric(15, 2),
subsidio_enfermedad 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