Documentación pública

Fórmulas exactas de los 8 KPIs

Si un consultor, contador o auditor te pregunta cómo calcula Troost un KPI, esta página tiene la respuesta exacta. Compartido públicamente como compromiso de honestidad operativa.

# Fórmulas exactas de KPIs · Troost

**Fecha:** 2026-05-15
**Sprint:** 8

> Este documento define la fórmula exacta de cada uno de los 8 KPIs estándar que Troost calcula y muestra al usuario. Para credibilidad ante consultores, contadores y auditores.

Cada KPI incluye:
- **Fórmula**: definición matemática
- **Fuente de datos**: tabla(s) y campos
- **Frecuencia de cálculo**: cuándo se recalcula
- **RPC**: función SQL que lo implementa
- **Meta industria**: umbral de referencia
- **Caveat**: limitaciones / supuestos

---

## KPI 1 · CPK (Costo Por Kilómetro)

### Fórmula
```
CPK_real    = (gastos_fiscales + gastos_no_fiscales) / km_totales
CPK_fiscal  = gastos_fiscales / km_totales
```

### Fuente de datos
- `v_finanzas_unificadas` (vista) — agrega gastos por categoría (combustible, mantenimiento, sueldos, misc, no_fiscal)
- `cargas_combustible.km_odometro` — kilómetros derivados de `MAX(km_odometro) - MIN(km_odometro)` por unidad (requiere ≥2 cargas/unidad)

### Frecuencia
On-demand al cargar `/dashboard` o `/reportes`. Recálculo cada vez.

### RPC
`public.cpk_completo(empresa_id, desde, hasta)` → JSONB con `cpk_real`, `cpk_fiscal`, desglose por categoría.

### Meta industria
**$28 MXN/km o menos** (estándar consultor para flotas de carga general en México).

### Caveat
- Si en el período hay <2 cargas de combustible por unidad, los km de esa unidad no cuentan. Resultado: CPK puede estar subestimado.
- "No fiscal" incluye mordidas, propinas, regalos. Esto hace el CPK_real más estricto que estándares de competencia que solo usan CFDI.

---

## KPI 2 · Utilidad por viaje (margen)

### Fórmula
```
margen_pct = (ingresos - gastos) / ingresos * 100
```

A nivel **empresa** (todo el período).
A nivel **flete individual** (futuro, no implementado aún): mismo cálculo pero `ingresos = precio_flete` y `gastos = gastos atribuibles a ese flete vía v_finanzas_unificadas.relacionado_id`.

### Fuente de datos
- `kpis_dashboard` RPC retorna `margen_pct` empresa
- `top_unidades` RPC retorna `margen_pct` por unidad

### Frecuencia
On-demand.

### RPC
`public.kpis_dashboard(empresa_id, desde, hasta)` → field `margen_pct`.

### Meta industria
**15% o más** (margen bruto antes de depreciación y financiamiento).

### Caveat
- Hoy se mide a nivel empresa-mes. Por flete individual queda como gap (ver auditoría KPI).
- No considera depreciación contable de las unidades. Un transportista con flota vieja sin pagar puede mostrar margen mayor que uno con flota nueva en pagos.

---

## KPI 3 · Rentabilidad por unidad

### Fórmula
```
rentabilidad_pct_unidad = (ingresos_unidad - gastos_unidad) / ingresos_unidad * 100
```

Donde `gastos_unidad` se agrega de `v_finanzas_unificadas WHERE relacionado_tipo = 'unidad' AND relacionado_id = unidad.id`.

### Fuente de datos
- `top_unidades` RPC

### Frecuencia
On-demand al cargar `/reportes` tab "Por Unidad".

### RPC
`public.top_unidades(empresa_id, desde, hasta, criterio)` → array con `margen_pct` por unidad.

### Meta industria
**>20%** de unidades de la flota con margen >20% (un consultor diría "más del 50% de tu flota debe estar arriba del 20% de margen, idealmente todas").

### Caveat
- No reasigna gastos compartidos (sueldo de oficina, alquiler) — son `relacionado_tipo = 'empresa'`, no se cargan a unidades.
- Una unidad con poca actividad pero gastos fijos altos sale negativa. Eso es honesto.

---

## KPI 4 · Rendimiento de diésel (km/L)

### Fórmula
```
kml_unidad = (MAX(km_odometro) - MIN(km_odometro)) / SUM(litros)
```
Por unidad en el período. Promedio global ponderado por km totales.

### Fuente de datos
- `cargas_combustible.km_odometro`, `cargas_combustible.litros`

### Frecuencia
On-demand.

### RPC
`public.rendimiento_diesel(empresa_id, desde, hasta)` → JSONB con `kml_global` y `por_unidad`.

### Meta industria
**2.8 km/L o más** (flota de carga pesada diésel · estándar mexicano).

### Caveat
- Requiere ≥2 cargas/unidad para calcular delta odómetro. Una unidad con 1 carga no aparece.
- Si el operador olvida actualizar odómetro en una carga, el cálculo se distorsiona.
- No distingue tipo de combustible (diésel vs gasolina). Asume diésel.

---

## KPI 5 · Ralentí (% tiempo motor encendido sin movimiento)

### Fórmula
```
ralenti_pct = tiempo_motor_encendido_sin_movimiento / tiempo_motor_encendido_total * 100
```

### Fuente de datos
**No implementado.** Requiere:
- Campo `posiciones_gps.ignicion_motor BOOLEAN` (NO existe hoy)
- O hardware OBD-II que reporte estado de ignición

### Frecuencia
N/A (no implementado).

### RPC
N/A. Pendiente: `ralenti_por_unidad(empresa, desde, hasta)`.

### Meta industria
**<5%** del tiempo en operación.

### Caveat
- Sin hardware específico (kit GPS+Cámara $1,500 MXN/unidad disponible en `/hardware`) no se puede calcular con precisión.
- Aproximación posible (sin hardware): unidad estática >15 min mientras flete está `en_transito` cuenta como ralentí presunto. Es proxy débil — no se publica como KPI sin el kit.

---

## KPI 6 · Kilómetros vacíos (% deadhead)

### Fórmula
```
km_vacios_pct = km_sin_carga / km_totales * 100
```

### Fuente de datos
**No implementado.** Requiere:
- Captura explícita de `fletes.km_retorno_vacio` o
- Inferencia: para cada par de fletes consecutivos de la misma unidad, km entre destino_flete_n y origen_flete_n+1 son vacíos. Hoy no se calcula automáticamente.

### Frecuencia
N/A.

### RPC
N/A. Pendiente: `km_vacios_por_unidad(empresa, desde, hasta)`.

### Meta industria
**<20%** del kilometraje total.

### Caveat
- Schema requiere agregar columnas: `fletes.km_cargado NUMERIC` y `fletes.km_retorno_vacio NUMERIC`.
- Marketplace de retornos vacíos (incluido en plan Pro) reduce este KPI naturalmente.

---

## KPI 7 · Entregas a tiempo

### Fórmula
```
pct_a_tiempo = COUNT(fletes entregados ON TIME) / COUNT(fletes entregados) * 100

donde "ON TIME" = fecha_entrega_real <= fecha_entrega_estimada
```

### Fuente de datos
- `fletes.fecha_entrega_real`, `fletes.fecha_entrega_estimada`, `fletes.estado = 'entregado'`

### Frecuencia
On-demand.

### RPC
- `public.top_operadores(...)` retorna `pct_a_tiempo` por operador
- `public.kpis_industria_consolidado(...)` retorna `entregas_a_tiempo` global empresa

### Meta industria
**>95%** (cliente shipper típico exige >95%).

### Caveat
- Fletes sin `fecha_entrega_estimada` (que el dispatcher olvidó capturar) se excluyen del cálculo. Recomendación: el form de creación de flete debe requerir fecha estimada.
- Definición de "a tiempo" usa la fecha COMPROMETIDA al cliente. Si el dispatcher infla la ETA para "siempre llegar a tiempo", se distorsiona. La buena práctica es no permitir editar `fecha_entrega_estimada` después de que el flete entra en estado `confirmado` — pendiente de implementar.

---

## KPI 8 · Fallas mecánicas

### Fórmula
```
tasa_fallas_pct = (servicios_correctivos + servicios_emergencia) / fletes_periodo * 100
```

### Fuente de datos
- `servicios_mantenimiento.tipo_servicio IN ('correctivo','emergencia')` (Sprint 8 · nueva columna)
- `fletes` count del período

### Frecuencia
On-demand.

### RPC
`public.tasa_fallas_mecanicas(empresa_id, desde, hasta)` → JSONB con `tasa_pct`, conteos por tipo.

### Meta industria
**<2%** (lo controlable por el transportista — mantenimiento preventivo bien hecho lo logra).

### Caveat
- Distingue preventivo (planeado · servicio en taller) vs correctivo (falla detectada · arreglo necesario) vs emergencia (la unidad se quedó parada).
- Solo cuenta correctivos + emergencias contra fletes totales. Si la unidad estuvo parada 5 días por falla, no se penaliza adicionalmente.
- Pre-Sprint 8 todos los servicios eran "preventivos" por default. Para datos históricos previos, el founder debe corregir la categorización a mano.

---

## Validación cruzada

El bloque `BloqueKPIsIndustria` en `/dashboard` consume la RPC `kpis_industria_consolidado` que internamente llama a las RPCs específicas de cada KPI. Si en algún momento un KPI muestra un valor que el founder considera incorrecto, debe verificarse:

1. Que los datos de origen están presentes (cargas de combustible, fletes con `fecha_entrega_real`, servicios con `tipo_servicio` correcto, etc.)
2. Que el período seleccionado tiene actividad suficiente
3. Que no hay registros con `soft_delete_at NOT NULL` que el cliente no esperaba

---

## Referencias industria

- **CESVI México** — costo promedio incidente vehicular
- **AMESIS** — recuperación 95.5% con monitoreo activo · tiempos respuesta 45 min foráneo / 28 min urbano
- **CANACAR · ANTP** — costos operativos transporte carga México
- **Overhaul** — Reporte Anual de Robo de Carga
- **BBVA-AMPIP** — encuesta de parques industriales

---

Querétaro · Mayo 2026

← Volver a developers