317 lines
12 KiB
PHP
317 lines
12 KiB
PHP
<?php
|
|
/**
|
|
* OrdenMedica — Gestión de órdenes médicas recibidas por WhatsApp.
|
|
*
|
|
* Flujo de estados:
|
|
* pendiente → en_revision → autorizada → en_domicilio → completada
|
|
* └→ rechazada
|
|
*/
|
|
|
|
require_once __DIR__ . '/../../classes/Database.php';
|
|
require_once __DIR__ . '/ActividadAdmin.php';
|
|
|
|
class OrdenMedica {
|
|
|
|
// Estados válidos
|
|
const ESTADOS = [
|
|
'pendiente',
|
|
'en_revision',
|
|
'autorizada',
|
|
'rechazada',
|
|
'en_domicilio',
|
|
'completada',
|
|
];
|
|
|
|
private Database $db;
|
|
private ActividadAdmin $log;
|
|
|
|
public function __construct() {
|
|
$this->db = Database::getInstance();
|
|
$this->log = new ActividadAdmin();
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// CRUD
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Lista paginada de órdenes con filtros.
|
|
*
|
|
* @param array $filtros estado, paciente_id, desde, hasta, busqueda
|
|
*/
|
|
public function listar(array $filtros = [], int $pagina = 1, int $porPagina = 30): array {
|
|
$wheres = [];
|
|
$params = [];
|
|
|
|
if (!empty($filtros['estado'])) {
|
|
$wheres[] = 'o.estado = ?';
|
|
$params[] = $filtros['estado'];
|
|
}
|
|
if (!empty($filtros['paciente_id'])) {
|
|
$wheres[] = 'o.paciente_id = ?';
|
|
$params[] = $filtros['paciente_id'];
|
|
}
|
|
if (!empty($filtros['desde'])) {
|
|
$wheres[] = 'DATE(o.created_at) >= ?';
|
|
$params[] = $filtros['desde'];
|
|
}
|
|
if (!empty($filtros['hasta'])) {
|
|
$wheres[] = 'DATE(o.created_at) <= ?';
|
|
$params[] = $filtros['hasta'];
|
|
}
|
|
if (!empty($filtros['busqueda'])) {
|
|
$like = '%' . $filtros['busqueda'] . '%';
|
|
$wheres[] = '(p.nombre_completo LIKE ? OR p.numero_documento LIKE ?)';
|
|
$params[] = $like;
|
|
$params[] = $like;
|
|
}
|
|
|
|
$where = $wheres ? 'WHERE ' . implode(' AND ', $wheres) : '';
|
|
$offset = ($pagina - 1) * $porPagina;
|
|
|
|
$total = $this->db->fetch(
|
|
"SELECT COUNT(*) AS n
|
|
FROM lab_ordenes_medicas o
|
|
JOIN lab_pacientes p ON p.id = o.paciente_id
|
|
$where",
|
|
$params
|
|
)['n'] ?? 0;
|
|
|
|
$rows = $this->db->fetchAll("
|
|
SELECT
|
|
o.*,
|
|
p.nombre_completo AS paciente_nombre,
|
|
p.numero_documento,
|
|
p.telefono AS paciente_telefono,
|
|
rev.full_name AS revisada_por_nombre,
|
|
aut.full_name AS autorizada_por_nombre
|
|
FROM lab_ordenes_medicas o
|
|
JOIN lab_pacientes p ON p.id = o.paciente_id
|
|
LEFT JOIN admin_users rev ON rev.id = o.revisada_por
|
|
LEFT JOIN admin_users aut ON aut.id = o.autorizada_por
|
|
$where
|
|
ORDER BY o.created_at DESC
|
|
LIMIT ? OFFSET ?
|
|
", array_merge($params, [$porPagina, $offset]));
|
|
|
|
return [
|
|
'data' => $rows,
|
|
'total' => (int)$total,
|
|
'pagina' => $pagina,
|
|
'por_pagina' => $porPagina,
|
|
'paginas' => (int)ceil($total / $porPagina),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Obtener una orden por ID (con todos los datos relacionados).
|
|
*/
|
|
public function obtener(int $id): ?array {
|
|
$orden = $this->db->fetch("
|
|
SELECT
|
|
o.*,
|
|
p.nombre_completo AS paciente_nombre,
|
|
p.numero_documento,
|
|
p.telefono AS paciente_telefono,
|
|
p.eps AS paciente_eps,
|
|
p.user_id,
|
|
u.phone_number,
|
|
rev.full_name AS revisada_por_nombre,
|
|
aut.full_name AS autorizada_por_nombre,
|
|
c.local_file AS conv_local_file,
|
|
c.whatsapp_media_id AS conv_media_id
|
|
FROM lab_ordenes_medicas o
|
|
JOIN lab_pacientes p ON p.id = o.paciente_id
|
|
LEFT JOIN users u ON u.id = p.user_id
|
|
LEFT JOIN admin_users rev ON rev.id = o.revisada_por
|
|
LEFT JOIN admin_users aut ON aut.id = o.autorizada_por
|
|
LEFT JOIN conversations c ON c.id = o.conversation_id
|
|
WHERE o.id = ?
|
|
", [$id]);
|
|
|
|
if (!$orden) {
|
|
return null;
|
|
}
|
|
|
|
// Historial de cambios de estado
|
|
$orden['historial'] = $this->db->fetchAll("
|
|
SELECT
|
|
la.*,
|
|
au.full_name AS admin_nombre
|
|
FROM lab_autorizaciones la
|
|
LEFT JOIN admin_users au ON au.id = la.realizada_por
|
|
WHERE la.orden_id = ?
|
|
ORDER BY la.created_at ASC
|
|
", [$id]);
|
|
|
|
return $orden;
|
|
}
|
|
|
|
/**
|
|
* Crear nueva orden médica.
|
|
*/
|
|
public function crear(array $datos, ?int $adminId = null): int {
|
|
$campos = $this->filtrarCampos($datos);
|
|
$id = $this->db->insert('lab_ordenes_medicas', $campos);
|
|
|
|
// Registrar en historial
|
|
$this->registrarCambioEstado($id, null, 'pendiente', $adminId, 'Orden creada');
|
|
$this->log->registrar($adminId, 'ordenes', 'crear', $id, $campos);
|
|
|
|
return $id;
|
|
}
|
|
|
|
/**
|
|
* Actualizar datos de la orden (sin cambiar estado).
|
|
*/
|
|
public function actualizar(int $id, array $datos, ?int $adminId = null): bool {
|
|
$campos = $this->filtrarCampos($datos);
|
|
unset($campos['estado']); // el estado se cambia con cambiarEstado()
|
|
$ok = $this->db->update('lab_ordenes_medicas', $campos, 'id = ?', [$id]);
|
|
if ($ok) {
|
|
$this->registrarCambioEstado($id, null, null, $adminId, 'Datos actualizados', 'editada');
|
|
$this->log->registrar($adminId, 'ordenes', 'editar', $id, $campos);
|
|
}
|
|
return $ok > 0;
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Gestión de estados (flujo de revisión)
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Cambia el estado de la orden con trazabilidad completa.
|
|
*
|
|
* @param int $id
|
|
* @param string $nuevoEstado Debe estar en self::ESTADOS
|
|
* @param int $adminId
|
|
* @param string $comentario
|
|
*/
|
|
public function cambiarEstado(
|
|
int $id,
|
|
string $nuevoEstado,
|
|
int $adminId,
|
|
string $comentario = ''
|
|
): bool {
|
|
if (!in_array($nuevoEstado, self::ESTADOS)) {
|
|
throw new InvalidArgumentException("Estado inválido: $nuevoEstado");
|
|
}
|
|
|
|
$orden = $this->obtener($id);
|
|
if (!$orden) {
|
|
throw new RuntimeException("Orden #$id no encontrada");
|
|
}
|
|
|
|
$estadoAnterior = $orden['estado'];
|
|
$ahora = date('Y-m-d H:i:s');
|
|
|
|
$campos = ['estado' => $nuevoEstado];
|
|
|
|
// Campos adicionales según el nuevo estado
|
|
if ($nuevoEstado === 'en_revision') {
|
|
$campos['revisada_por'] = $adminId;
|
|
$campos['revisada_at'] = $ahora;
|
|
} elseif (in_array($nuevoEstado, ['autorizada', 'rechazada'])) {
|
|
$campos['autorizada_por'] = $adminId;
|
|
$campos['autorizada_at'] = $ahora;
|
|
$campos['comentario_revision'] = $comentario;
|
|
}
|
|
|
|
$ok = $this->db->update('lab_ordenes_medicas', $campos, 'id = ?', [$id]);
|
|
|
|
if ($ok) {
|
|
$this->registrarCambioEstado($id, $estadoAnterior, $nuevoEstado, $adminId, $comentario);
|
|
$this->log->registrar($adminId, 'ordenes', $nuevoEstado, $id, [
|
|
'estado_anterior' => $estadoAnterior,
|
|
'comentario' => $comentario,
|
|
]);
|
|
}
|
|
|
|
return $ok > 0;
|
|
}
|
|
|
|
// Alias de conveniencia
|
|
public function autorizar(int $id, int $adminId, string $comentario = ''): bool {
|
|
return $this->cambiarEstado($id, 'autorizada', $adminId, $comentario);
|
|
}
|
|
|
|
public function rechazar(int $id, int $adminId, string $motivo): bool {
|
|
return $this->cambiarEstado($id, 'rechazada', $adminId, $motivo);
|
|
}
|
|
|
|
public function iniciarRevision(int $id, int $adminId): bool {
|
|
return $this->cambiarEstado($id, 'en_revision', $adminId);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Estadísticas / Dashboard
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Conteo de órdenes por estado.
|
|
*/
|
|
public function contadorPorEstado(): array {
|
|
$rows = $this->db->fetchAll("
|
|
SELECT estado, COUNT(*) AS total
|
|
FROM lab_ordenes_medicas
|
|
GROUP BY estado
|
|
");
|
|
$result = array_fill_keys(self::ESTADOS, 0);
|
|
foreach ($rows as $row) {
|
|
$result[$row['estado']] = (int)$row['total'];
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Órdenes pendientes más antiguas (sin revisar).
|
|
*/
|
|
public function pendientesAntiguos(int $limit = 10): array {
|
|
return $this->db->fetchAll("
|
|
SELECT
|
|
o.*,
|
|
p.nombre_completo,
|
|
TIMESTAMPDIFF(HOUR, o.created_at, NOW()) AS horas_espera
|
|
FROM lab_ordenes_medicas o
|
|
JOIN lab_pacientes p ON p.id = o.paciente_id
|
|
WHERE o.estado = 'pendiente'
|
|
ORDER BY o.created_at ASC
|
|
LIMIT ?
|
|
", [$limit]);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
// Helpers privados
|
|
// ─────────────────────────────────────────────────────────────────────────
|
|
|
|
private function registrarCambioEstado(
|
|
int $ordenId,
|
|
?string $estadoAnterior,
|
|
?string $estadoNuevo,
|
|
?int $adminId,
|
|
string $comentario = '',
|
|
string $accion = ''
|
|
): void {
|
|
$accion = $accion ?: ($estadoNuevo ?? 'editada');
|
|
$this->db->insert('lab_autorizaciones', [
|
|
'orden_id' => $ordenId,
|
|
'accion' => $accion,
|
|
'estado_anterior'=> $estadoAnterior,
|
|
'estado_nuevo' => $estadoNuevo,
|
|
'realizada_por' => $adminId,
|
|
'comentario' => $comentario,
|
|
]);
|
|
}
|
|
|
|
private function filtrarCampos(array $datos): array {
|
|
$permitidos = [
|
|
'paciente_id', 'conversation_id', 'whatsapp_media_id', 'local_file',
|
|
'estado', 'medico_nombre', 'medico_registro', 'fecha_orden',
|
|
'diagnostico', 'examenes_solicitados', 'requiere_ayuno',
|
|
'horas_ayuno', 'indicaciones', 'revisada_por', 'revisada_at',
|
|
'autorizada_por', 'autorizada_at', 'comentario_revision', 'notas_admin',
|
|
];
|
|
return array_intersect_key($datos, array_flip($permitidos));
|
|
}
|
|
}
|