Apariencia
WL Aqua Intelligence v4 — fórmulas de scoring y KPIs
Referencia de todos los valores calculados que el sistema deriva del archivo WL_Aqua_Intelligence_v4_data_actualizada.xlsx (Winston Loaiza, analizado el 2026-08-03): semáforos por criterio, índice de salud, prioridad, recomendación, los KPIs del dashboard ejecutivo, el FCA por talla y el ranking de prioridades.
Es la evolución de la sábana ya documentada en Sábana de cálculos —mismas fórmulas de producción— más una capa nueva de scoring sobre un consolidado de 47 lagunas / 4 fincas. 9 hojas: Portada, Consolidado, 4 hojas por finca, Parámetros KPI, Dashboard, Prioridades.
Esta página es la fuente única de las fórmulas. Los
*-flow.mdde 09 · Dashboard, 10 · Parámetros KPI y 11 · Prioridades documentan contrato, RBAC y flujo de cliente, y enlazan acá en vez de repetir las fórmulas.
Procedencia y nivel de confianza
Distinguir de dónde sale cada regla importa: la primera lectura del xlsx se hizo con data_only=True (solo valores calculados) y llevó a inferir mal la regla de prioridad. Releído con data_only=False aparecieron las fórmulas reales.
| Origen | Qué cubre | Confianza |
|---|---|---|
Celdas AB2:AI2 del xlsx (data_only=False) | Los 5 semáforos, índice de salud, prioridad | Fórmula literal del socio |
Hoja Parámetros KPI del xlsx | Tramos, cortes y penalizaciones por defecto | Valores literales |
| Reconstrucción validada 47/47 | Recomendación (única columna sin fórmula) | Regla reconstruida, no enunciada por el socio |
| Decisión propia documentada en ADR | Trato de "sin dato", FCA por talla, agregados del dashboard | Decisión de implementación |
Verificación: las fórmulas se recalcularon sobre las 47 filas reales del archivo y coinciden 47/47 (kpi-scoring.spec.ts, fixture __fixtures__/wl-aqua-v4-consolidado.ts).
El .xlsx no vive en este repo: es un artefacto de planificación, en _planning/_referencia/ del monorepo.
Insumos: de dónde sale cada dato
Todo el scoring corre sobre la fila que ya arma la sábana semanal — no hay captura de campo nueva ni tablas propias. Estos son los insumos y su origen real (ReportesService.buildRow), a una fecha de corte reportDate:
| Insumo | Origen (Prisma) | Selección |
|---|---|---|
hectares | Pool.hectares | — |
sowingDate | Siembra.date, o Cycle.startDate si no hay siembra | del ciclo vigente al corte |
larvaeCount | Siembra.larvaeCount | — |
initialWeightG | Siembra.initialWeightG | — |
pesoActG | Sampling.avgWeightG | el muestreo más reciente con date ≤ reportDate |
pesoAntG | Sampling.avgWeightG | el más reciente con date ≤ inicio de semana |
pesoHace3SemanasG | Sampling.avgWeightG | el más reciente con date ≤ reportDate − 21 d |
survivalPct | Population.survivalPct | la última evaluación con date ≤ reportDate (sobrevivencia real, la del aguaje) |
raleoLb | Harvest.pounds | suma de type = 'raleo' y status = 'ejecutado' con date ≤ reportDate |
feedAccumKg | Feeding.kg | suma con date ≤ inicio de semana |
feedWeekKg | Feeding.kg | suma con inicio de semana < date ≤ reportDate |
factor | FeedingCurvePoint.factor | curva del ciclo (Cycle.feedingCurve) o la default de la organización, buscada por pesoActG |
Los derivados intermedios que consume el scoring (fcr, crecimientoDiaG, crecimiento3SemanasG, lbPorHaTotal) se calculan en computeWeeklyRow — sus fórmulas están en Sábana de cálculos.
Los 5 criterios
Orden canónico (el de las columnas AB:AF): FCA → supervivencia → crecimiento diario → crecimiento 3 semanas → carga.
| Criterio | Valor que evalúa | Dirección | Tramos |
|---|---|---|---|
fca | fcr de la fila | menor es mejor | por peso (4) |
supervivencia | survivalPct (real, del aguaje) | mayor es mejor | por peso (5) |
crecimientoDiario | crecimientoDiaG | mayor es mejor | plano |
crecimiento3Semanas | crecimiento3SemanasG | mayor es mejor | plano |
carga | lbPorHaTotal (incluye raleo) | menor es mejor | plano |
La dirección no es configurable: es semántica del dominio, no un umbral. Dejarla en BD permitiría invertir el significado de un criterio editando una fila.
Carga usa lb/ha total, no el actual — verificado contra las 8 lagunas del xlsx con raleo ≠ 0. Mide presión acumulada de producción por hectárea (incluyendo lo ya sacado en raleos), no biomasa en pie. Es contraintuitivo, pero es lo que hace el archivo del socio.
Los criterios planos se modelan como un solo tramo con maxWeightG = 9999 (UNBOUNDED_BRACKET_WEIGHT_G), para que el motor recorra los 5 criterios con el mismo código.
Cómo se resuelve el estado
El tramo vigente es el de menor maxWeightG que no supere pesoActG (los IF anidados del Excel); si el peso es null o excede todos, cae en el último.
menor es mejor (fca, carga)
valor ≤ greenCut → verde
valor ≤ yellowCut → amarillo
redCut = null → rojo
valor ≤ redCut → rojo · si no → critico
mayor es mejor (supervivencia, crecimientos)
valor ≥ greenCut → verde
valor ≥ yellowCut → amarillo
en otro caso → rojoSolo FCA define redCut, así que es el único criterio que alcanza critico; los otros cuatro tienen 3 niveles.
Tramos y penalizaciones por defecto
Valores exactos de la hoja Parámetros KPI, sembrados por organización (DEFAULT_KPI_THRESHOLDS). Cada organización los edita desde el admin: se leen siempre de la BD, no son constantes de negocio duras.
FCA (menor es mejor) — penalizaciones 0 / 12 / 25 / 35:
| Peso | Verde ≤ | Amarillo ≤ | Rojo ≤ | Crítico |
|---|---|---|---|---|
| ≤ 10 g | 1 | 1.1 | 1.2 | > 1.2 |
| ≤ 15 g | 1.15 | 1.2 | 1.2 | > 1.2 |
| ≤ 20 g | 1.15 | 1.25 | 1.25 | > 1.25 |
| > 20 g | 1.25 | 1.5 | 1.5 | > 1.5 |
Supervivencia % (mayor es mejor) — penalizaciones 0 / 15 / 30 / 30:
| Peso | Verde ≥ | Amarillo ≥ | Rojo |
|---|---|---|---|
| ≤ 5 g | 92 | 85 | < 85 |
| ≤ 10 g | 85 | 75 | < 75 |
| ≤ 15 g | 80 | 70 | < 70 |
| ≤ 20 g | 75 | 65 | < 65 |
| > 20 g | 70 | 60 | < 60 |
Criterios planos:
| Criterio | Verde | Amarillo | Rojo | Penalizaciones (V/A/R/C) |
|---|---|---|---|---|
| Crecimiento diario | ≥ 0.3 g/día | ≥ 0.2 | < 0.2 | 0 / 10 / 20 / 20 |
| Crecimiento 3 semanas | ≥ 2 g | ≥ 1.6 | < 1.6 | 0 / 8 / 15 / 15 |
| Carga (lb/ha total) | ≤ 3499 | ≤ 4499 | > 4499 | 0 / 7 / 15 / 15 |
En los tramos de FCA de ≤ 15 g, ≤ 20 g y > 20 g,
yellowCutyredCutson iguales, así que la banda "rojo" queda vacía: esas piscinas pasan de amarillo directo a crítico. Solo el tramo ≤ 10 g produce rojo. Es consecuencia literal de los valores de la hoja del socio, no un ajuste nuestro — está anotado en el backlog para confirmárselo.
"Sin dato"
Si el insumo es null, el criterio marca sin_dato y penaliza 0, en los cinco criterios.
⚠️ Es una diferencia deliberada con la hoja del socio: allí solo FCA y crecimiento 3 semanas tienen rama "Sin dato"; en supervivencia, crecimiento diario y carga un dato en blanco hace fallar la comparación y cae en Rojo, penalizando a una piscina por falta de datos. Ver ADR-0057.
Además, un 0 en crecimiento 3 semanas también es "sin dato" (KPI_ZERO_IS_MISSING). Viene de la fórmula del socio (AE) y tiene sentido de dominio: sin un muestreo distinto 21 días atrás, ambos pesos resuelven al mismo muestreo y la resta da 0 — eso significa "no hay ventana de 3 semanas", no "el camarón no creció".
Índice de salud
Celda AG:
indiceSalud = MAX(0, 100 − Σ penalización(estado) de los 5 criterios)Las penalizaciones salen de la configuración de cada criterio (tabla de arriba), editables por organización. sin_dato siempre penaliza 0.
Prioridad
Celda AH. No es función pura del índice: combina el índice con el conteo de semáforos. Una piscina con índice 78 puede ser "Alta" por tener un solo Rojo.
Urgente si índice < urgenteIndexBelow O (Rojos + Críticos) ≥ urgenteRedCount
Alta si índice < altaIndexBelow O (Rojos + Críticos) ≥ altaRedCount
Media si índice < mediaIndexBelow O Amarillos ≥ mediaYellowCount
Normal en cualquier otro casoLos 6 números son parte del KpiThresholdSet, editables desde el admin. Default del Excel v4: 60 / 3 / 75 / 1 / 90 / 2.
Recomendación
Celda AI — la única columna KPI sin fórmula en el xlsx (texto plano en las 47 filas). La regla se reconstruyó por inspección y reproduce 47/47: una frase fija por criterio en estado no-verde, en el orden canónico de columnas, unidas por ; , con la primera letra capitalizada y punto final.
| Criterio | Amarillo | Rojo / Crítico |
|---|---|---|
| FCA | vigilar fca | revisar conversión y ración |
| Supervivencia | vigilar supervivencia | auditar mortalidad |
| Crecimiento diario | ajustar estrategia de alimento | corregir crecimiento |
| Crecimiento 3 semanas | (sin frase) | revisar tendencia de 3 semanas |
| Carga | vigilar carga | evaluar carga y aireación |
Sin ningún criterio en alerta: "Mantener manejo y seguimiento semanal."
⚠️ Dos cosas no confirmadas por el socio: el tope de 3 frases se ejercita en una sola de las 47 filas (n = 1, laguna N7L05) — es la explicación más simple que reproduce ese texto, no una regla que él haya enunciado; y en crecimiento 3 semanas el amarillo no aporta frase porque en su hoja solo el rojo la genera. El catálogo es una constante editable en código (KPI_RECOMENDACION_FRASES), no vive en BD.
Dashboard ejecutivo (hoja Dashboard)
Agregados sobre las filas por piscina (computeDashboardKpis). Regla transversal: los promedios y conteos de scoring toman solo piscinas con ciclo — una piscina vacía evalúa sus 5 criterios en sin_dato y siempre da índice 100, así que incluirla inflaría el promedio.
| Valor | Fórmula | Sobre qué filas |
|---|---|---|
haTotal | Σ hectares | todas |
biomasaLb | Σ biomasaLb | todas |
lbPorHaActual | biomasaLb / haTotal | todas |
lbPorHaTotal | (biomasaLb + Σ raleoLb) / haTotal | todas |
survivalPct | Σ(survivalPct × larvaeCount) ÷ Σ larvaeCount — ponderado por cantidad sembrada | con ciclo, con ambos datos |
survivalEstimadaPct | misma ponderación, como fuente independiente | con ciclo, con ambos datos |
fcr | (Σ feedAccumTotalKg × 2.2046) ÷ (biomasaLb + Σ raleoLb) | todas |
crecimientoDiaG | promedio simple | con ciclo |
promedioIncrementosG | promedio simple | con ciclo |
densidadHa | Σ larvaeCount ÷ Σ hectares de las filas con siembra | con larvaeCount |
indiceSaludPromedio | promedio simple del índice | con ciclo |
prioridadCounts | conteo por nivel | con ciclo |
atencionPrioritariaCount | urgente + alta | con ciclo |
El FCA consolidado es alimento total ÷ biomasa total, no un promedio de los FCA por piscina ni una ponderación — confirmado por Winston el 2026-08-04, coincide con lo ya implementado. La sobrevivencia sí se pondera por cantidad sembrada: un promedio simple de porcentajes sobre-pesaría a las piscinas chicas.
El denominador de densidadHa son las filas con larvaeCount, no "todas las que tienen ciclo": hoy coincide, porque todo ciclo se crea junto con su siembra, pero así el KPI no se diluye si alguna vez existe un ciclo activo sin siembra.
Resumen por sector
Por sector: hectares y biomasaLb (sumas), fcr (misma fórmula del consolidado aplicada solo a sus filas), indiceSaludPromedio y atencionPrioritariaCount (solo filas con ciclo).
Top-5 piscinas
Las limit (default 5) piscinas de peor índice de salud, solo entre las que tienen ciclo. Desempate determinista: prioridad más severa primero, luego código de piscina.
FCA por talla
Alcance pedido por Winston el 2026-08-04, no está en el Excel v4. Agrupa las piscinas por el tramo vigente del criterio fca de la organización —el mismo que ya define su semáforo, vía findBracket: no hay tabla de tallas propia— y aplica dentro de cada grupo la misma fórmula del FCA consolidado.
Diferencia con el scoring: ahí un pesoActG nulo cae en el último tramo (un semáforo necesita algún valor); acá las filas sin peso van a un grupo Sin dato propio, para no inflar ni desinflar el último tramo con piscinas que no se pudieron tallar.
📌 Los grupos quedan acoplados a los umbrales de FCA: si un admin edita esos tramos, los grupos de talla cambian con ellos. Es el comportamiento buscado (ADR-0059), pero conviene confirmarlo con Winston la primera vez que use la pantalla.
Prioridades (hoja Prioridades)
Ranking operativo: las mismas filas por piscina, ordenadas por índice de salud ascendente (peor primero). No agrega tablas ni queries: reutiliza el mismo fetch y el mismo buildRow que la sábana y el dashboard. Entran solo piscinas con ciclo, por la misma razón que en los agregados.
Dónde vive cada cálculo
Archivo (camaroneras_backend/src/modules/) | Qué calcula |
|---|---|
reportes/kpi-scoring.ts | semáforos, índice de salud, prioridad, recomendación |
reportes/kpi-thresholds.defaults.ts | valores de la hoja Parámetros KPI (seed) |
reportes/weekly-report.calc.ts | derivados de la fila (fcr, crecimientos, lbPorHaTotal…) |
reportes/weekly-report.aggregate.ts | selección histórica de pesos e incrementos |
reportes/dashboard.calc.ts | agregados, resumen por sector, top-5, FCA por talla |
reportes/reportes.service.ts | buildRow: ata los insumos a Prisma; getWeekly / getDashboard / getPriorities |
Consumidores: GET /reports/weekly (reportes:ver), GET /reports/dashboard y GET /reports/priorities (ambos dashboard:ver), y el CRUD de umbrales GET/PATCH /kpi-thresholds (parametros-kpi:ver / editar).
Decisiones de negocio que fijan estas fórmulas
Confirmadas con Winston Loaiza (2026-08-03 / 04):
- Finca = Sector. El Excel consolida 4 fincas; acá son 4 sectores de la organización.
- La biomasa y el FCA usan la sobrevivencia REAL (prueba de campo / aguaje); la estimada es proyección. ⚠️ No dijo explícitamente cuál alimenta la biomasa: se asume la real, que es lo que el sistema ya hacía y lo coherente con su distinción.
- El FCA consolidado es alimento total ÷ biomasa total, no ponderado por biomasa.
- El número de ciclo se cuenta por laguna y un ciclo pertenece al año en que se sembró — ver ADR-0058.
Pendientes de confirmar
- El catálogo definitivo de frases de la recomendación, incluido el tope de 3 (n = 1).
- Que la banda "rojo" vacía en 3 de los 4 tramos de FCA sea intencional.
- Que el acoplamiento talla ↔ umbrales de FCA sea el comportamiento esperado en uso real.