Files
whatsapp/api/lab/get_metricas.php
T

188 lines
8.7 KiB
PHP

<?php
/**
* GET /api/lab/get_metricas.php
* Estadísticas de período para el módulo de reportes.
* Parámetros: desde (Y-m-d), hasta (Y-m-d)
*/
require_once __DIR__ . '/_helpers.php';
requireMethod('GET');
try {
$db = Database::getInstance();
$desde = $_GET['desde'] ?? date('Y-m-01');
$hasta = $_GET['hasta'] ?? date('Y-m-d');
// ── 1. Domicilios por día ────────────────────────────────────────────────
$porDia = $db->fetchAll(
"SELECT DATE(fecha_programada) AS dia,
COUNT(*) AS total,
SUM(estado = 'completado') AS completados,
SUM(estado = 'cancelado') AS cancelados,
SUM(estado = 'reprogramado') AS reprogramados
FROM lab_domicilios
WHERE fecha_programada BETWEEN ? AND ?
GROUP BY DATE(fecha_programada)
ORDER BY dia ASC",
[$desde, $hasta]
);
// ── 2. KPIs de tasa de éxito ────────────────────────────────────────────
$kpi = $db->fetch(
"SELECT COUNT(*) AS total,
SUM(estado = 'completado') AS completados,
SUM(estado = 'cancelado') AS cancelados,
SUM(estado = 'reprogramado') AS reprogramados,
SUM(estado NOT IN ('completado','cancelado','reprogramado')) AS en_curso
FROM lab_domicilios
WHERE fecha_programada BETWEEN ? AND ?",
[$desde, $hasta]
) ?: [];
// ── 3. Tiempo promedio en domicilio ──────────────────────────────────────
$tiempos = $db->fetch(
"SELECT ROUND(AVG(TIMESTAMPDIFF(MINUTE, hora_llegada, hora_salida)), 1) AS promedio_min,
MIN(TIMESTAMPDIFF(MINUTE, hora_llegada, hora_salida)) AS min_min,
MAX(TIMESTAMPDIFF(MINUTE, hora_llegada, hora_salida)) AS max_min,
COUNT(*) AS con_tiempos
FROM lab_domicilios
WHERE fecha_programada BETWEEN ? AND ?
AND hora_llegada IS NOT NULL AND hora_salida IS NOT NULL
AND hora_salida > hora_llegada",
[$desde, $hasta]
) ?: [];
// ── 4. Tiempo promedio desde agendamiento hasta realización ─────────────
$tiempoAgen = $db->fetch(
"SELECT ROUND(AVG(DATEDIFF(fecha_programada, DATE(created_at))), 1) AS dias_promedio,
MIN(DATEDIFF(fecha_programada, DATE(created_at))) AS dias_min,
MAX(DATEDIFF(fecha_programada, DATE(created_at))) AS dias_max
FROM lab_domicilios
WHERE fecha_programada BETWEEN ? AND ?
AND estado = 'completado'",
[$desde, $hasta]
) ?: [];
// ── 5. Ranking de enfermeras ────────────────────────────────────────────
$rankingEnf = $db->fetchAll(
"SELECT e.id, e.nombre_completo,
COUNT(a.id) AS asignados,
SUM(d.estado = 'completado') AS completados,
SUM(d.estado = 'cancelado') AS cancelados,
ROUND(SUM(d.estado='completado') / NULLIF(COUNT(a.id),0) * 100, 1) AS tasa_pct
FROM lab_asignaciones a
JOIN lab_enfermeras e ON e.id = a.enfermera_id
JOIN lab_domicilios d ON d.id = a.domicilio_id
WHERE d.fecha_programada BETWEEN ? AND ?
GROUP BY e.id, e.nombre_completo
ORDER BY completados DESC, asignados DESC",
[$desde, $hasta]
);
// ── 6. Financiero ────────────────────────────────────────────────────────
$financiero = $db->fetch(
"SELECT SUM(COALESCE(valor_domicilio,0) + COALESCE(valor_copago,0)) AS total_bruto,
SUM(CASE WHEN pago_estado='pagado' THEN COALESCE(valor_domicilio,0)+COALESCE(valor_copago,0) ELSE 0 END) AS cobrado,
SUM(CASE WHEN pago_estado='pendiente' THEN COALESCE(valor_domicilio,0)+COALESCE(valor_copago,0) ELSE 0 END) AS pendiente,
SUM(CASE WHEN pago_estado='exento' THEN COALESCE(valor_domicilio,0)+COALESCE(valor_copago,0) ELSE 0 END) AS exento,
COUNT(CASE WHEN pago_estado='pagado' THEN 1 END) AS n_pagados,
COUNT(CASE WHEN pago_estado='pendiente' THEN 1 END) AS n_pendientes,
COUNT(CASE WHEN pago_estado='exento' THEN 1 END) AS n_exentos
FROM lab_domicilios
WHERE fecha_programada BETWEEN ? AND ?",
[$desde, $hasta]
) ?: [];
// ── 7. Por modo de pago ─────────────────────────────────────────────────
$porModoPago = $db->fetchAll(
"SELECT COALESCE(pago_modo, 'no_especificado') AS modo,
COUNT(*) AS n,
SUM(COALESCE(valor_domicilio,0)+COALESCE(valor_copago,0)) AS total
FROM lab_domicilios
WHERE pago_estado = 'pagado'
AND fecha_programada BETWEEN ? AND ?
GROUP BY pago_modo
ORDER BY n DESC",
[$desde, $hasta]
);
// ── 8. Top zonas (barrio / ciudad) ──────────────────────────────────────
$topZonas = $db->fetchAll(
"SELECT COALESCE(NULLIF(TRIM(barrio),''), NULLIF(TRIM(ciudad),''), 'Sin datos') AS zona,
COUNT(*) AS n
FROM lab_domicilios
WHERE fecha_programada BETWEEN ? AND ?
GROUP BY zona
ORDER BY n DESC
LIMIT 10",
[$desde, $hasta]
);
// ── 9. Domicilios por tipo de cliente ────────────────────────────────────
$porTipoCliente = $db->fetchAll(
"SELECT COALESCE(tipo_cliente,'particular') AS tipo,
COALESCE(seguro_nombre,'') AS seguro,
COUNT(*) AS n
FROM lab_domicilios
WHERE fecha_programada BETWEEN ? AND ?
GROUP BY tipo, seguro
ORDER BY n DESC",
[$desde, $hasta]
);
// ── 10. Top exámenes solicitados (simple word-frequency) ─────────────────
// Traemos todos los valores y hacemos el conteo en PHP para no depender de
// funciones de texto avanzadas de MySQL que podrían variar según servidor.
$examRows = $db->fetchAll(
"SELECT examenes_solicitados FROM lab_domicilios
WHERE fecha_programada BETWEEN ? AND ?
AND examenes_solicitados IS NOT NULL AND examenes_solicitados != ''",
[$desde, $hasta]
);
$examConteo = [];
foreach ($examRows as $row) {
// Dividir por coma, punto y coma o salto de línea
$items = preg_split('/[,;\n]+/', $row['examenes_solicitados'] ?? '');
foreach ($items as $item) {
$item = trim($item);
if (strlen($item) > 2) {
$k = mb_strtolower($item);
$examConteo[$k] = ($examConteo[$k] ?? 0) + 1;
}
}
}
arsort($examConteo);
$topExamenes = array_slice(
array_map(fn($k, $v) => ['examen' => $k, 'n' => $v], array_keys($examConteo), $examConteo),
0, 10
);
// ── 11. Horas del día con más agendamientos ─────────────────────────────
$porHora = $db->fetchAll(
"SELECT HOUR(hora_programada) AS hora, COUNT(*) AS n
FROM lab_domicilios
WHERE fecha_programada BETWEEN ? AND ?
AND hora_programada IS NOT NULL
GROUP BY HOUR(hora_programada)
ORDER BY hora ASC",
[$desde, $hasta]
);
jsonOk([
'por_dia' => $porDia,
'kpi' => $kpi,
'tiempos' => $tiempos,
'tiempo_agendamiento' => $tiempoAgen,
'ranking_enfermeras' => $rankingEnf,
'financiero' => $financiero,
'por_modo_pago' => $porModoPago,
'top_zonas' => $topZonas,
'por_tipo_cliente' => $porTipoCliente,
'top_examenes' => $topExamenes,
'por_hora' => $porHora,
]);
} catch (Exception $e) {
error_log('[get_metricas] ' . $e->getMessage());
jsonError($e->getMessage(), 500);
}