Saltar a contenido

Tablero — captura y actividad por hora (entrega 3, Q3)

Dos endpoints nuevos para la pantalla de promociones del Tablero: el embudo ofrecido→aplicado por beneficio, y la actividad de hoy desglosada por hora. Complementan al Tablero de entrega 2 (GET /api/admin/tablero) y al Panel de resultados (GET /api/admin/tablero/resultados) — los tres viven bajo /api/admin/tablero/**, leídos por ambos roles (INV-S04), pero cada uno tiene su propio controller/service/DTO, deliberadamente separados para no tocar las piezas ya probadas de entregas anteriores. Fuente: ACCEPTANCE.md §Entrega 3 y el código en backend/src/main/java/com/tipre/beneficioscenter/auditoria/ del repositorio del sistema.

Embudo de captura por beneficio

GET /api/admin/tablero/captura?desde&hasta

Cruza consulta_beneficio (lo ofrecido) con resultado_pago (lo aplicado) para el embudo completo y un ranking por beneficio.

Parámetros

Parámetro Tipo Obligatorio Descripción
desde fecha ISO (YYYY-MM-DD) no Si se omite junto con hasta, el rango es hoy. Si solo se pasa uno de los dos, el otro toma el mismo valor.
hasta fecha ISO (YYYY-MM-DD) no Igual que desde.

Rango máximo: 90 días (hasta - desde <= 90, límite inclusive). Igual convención que GET /api/admin/tablero y GET /api/admin/tablero/resultados.

Response 200

{
  "desde": "2026-09-13",
  "hasta": "2026-09-13",
  "embudo": {
    "consultas": 120,
    "conBeneficioOfrecido": 45,
    "pagosAprobados": 38,
    "beneficiosAplicados": 30
  },
  "porBeneficio": [
    {
      "benefitId": 3,
      "description": "Aniversario DINO 15%",
      "ofrecido": 20,
      "aplicado": 18,
      "efectividadPct": 90.00,
      "descuentoEntregado": 270000.00
    }
  ]
}
Campo Descripción
embudo.consultas Total de POST /resolver en el rango.
embudo.conBeneficioOfrecido Consultas distintas con al menos una fila en consulta_beneficio — la tabla autoritativa de lo ofrecido, no el CSV denormalizado de consulta.beneficios_aplicados.
embudo.pagosAprobados Filas de resultado_pago con estado=APROBADO en el rango.
embudo.beneficiosAplicados De esos pagos aprobados, cuántos tienen un benefitId asociado.
porBeneficio[].benefitId / .description Identificación del beneficio; description cae a "(unknown)" si el beneficio ya no existe en el catálogo.
porBeneficio[].ofrecido Veces que consulta_beneficio registró este beneficio en el rango.
porBeneficio[].aplicado Veces que un resultado_pago APROBADO trae este benefitId.
porBeneficio[].efectividadPct aplicado / ofrecido × 100, escala 2, HALF_UP; 0.00 si ofrecido es cero (nunca divide por cero).
porBeneficio[].descuentoEntregado Suma de descuentoAplicado de los pagos aprobados de ese beneficio.

porBeneficio es la unión de los beneficios ofrecidos y aplicados en el rango — un beneficio ofrecido pero nunca aplicado igual aparece, con aplicado: 0; ordenado por aplicado descendente y luego ofrecido descendente.

Errores

Status Cuándo field
400 Rango mayor a 90 días hasta

Sin datos en el rango: 200 con el embudo en cero y porBeneficio vacío (nunca un error).

Actividad por hora

GET /api/admin/tablero/por-hora

Sin parámetros — siempre devuelve el día de hoy, zona America/Argentina/Cordoba. No sigue el selector de rango de fechas del resto del Tablero.

Response 200

{
  "fecha": "2026-09-13",
  "horas": [
    { "hora": 0, "consultas": 0, "aplicados": 0 },
    { "hora": 14, "consultas": 12, "aplicados": 9 }
  ]
}
Campo Descripción
fecha Fecha de hoy en Córdoba.
horas[] Siempre 24 entradas, hora de 0 a 23 ascendente, sin huecos aunque no haya datos.
horas[].consultas POST /resolver recibidos en esa hora local.
horas[].aplicados Filas de resultado_pago con estado=APROBADO recibidas en esa hora local.

Por qué el bucketing es en Java, no en SQL

recibida_en/recibido_en se guardan como Instant (UTC). La hora del bucket es la hora local en Córdoba (UTC-3 fijo, sin horario de verano) — agrupar por hora en SQL no sería portable ni correcto entre H2 y SQL Server para un corte en hora local. El servicio trae los Instant del día (una consulta por repositorio, no una por hora) y los agrupa convirtiendo cada uno a America/Argentina/Cordoba y leyendo la hora. Esto también resuelve bien el límite del día: un registro a las 01:00 Córdoba es 04:00 UTC (un día calendario distinto en UTC), pero cae en el bucket hora: 1, no hora: 4.

No hay parámetro de rango que pueda fallar validación — esta ruta no tiene respuesta 400.

Dónde sigue