188 lines
8.7 KiB
PHP
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);
|
|
}
|