Files
whatsapp/classes/lab/Domicilio.php
T

298 lines
11 KiB
PHP

<?php
/**
* Domicilio — Gestión de servicios a domicilio del laboratorio.
*
* Flujo de estados:
* programado → confirmado → en_camino → en_domicilio → completado
* └→ cancelado / reprogramado
*/
require_once __DIR__ . '/../../classes/Database.php';
require_once __DIR__ . '/ActividadAdmin.php';
class Domicilio {
const ESTADOS = [
'programado',
'confirmado',
'en_camino',
'en_domicilio',
'completado',
'cancelado',
'reprogramado',
];
private Database $db;
private ActividadAdmin $log;
public function __construct() {
$this->db = Database::getInstance();
$this->log = new ActividadAdmin();
}
// ─────────────────────────────────────────────────────────────────────────
// CRUD
// ─────────────────────────────────────────────────────────────────────────
/**
* Lista de domicilios con filtros.
*
* @param array $filtros fecha, estado, enfermera_id, paciente_id
*/
public function listar(array $filtros = [], int $pagina = 1, int $porPagina = 30): array {
$wheres = [];
$params = [];
if (!empty($filtros['fecha'])) {
$wheres[] = 'd.fecha_programada = ?';
$params[] = $filtros['fecha'];
}
if (!empty($filtros['desde'])) {
$wheres[] = 'd.fecha_programada >= ?';
$params[] = $filtros['desde'];
}
if (!empty($filtros['hasta'])) {
$wheres[] = 'd.fecha_programada <= ?';
$params[] = $filtros['hasta'];
}
if (!empty($filtros['estado'])) {
$wheres[] = 'd.estado = ?';
$params[] = $filtros['estado'];
}
if (!empty($filtros['enfermera_id'])) {
$wheres[] = 'a.enfermera_id = ?';
$params[] = $filtros['enfermera_id'];
}
if (!empty($filtros['paciente_id'])) {
$wheres[] = 'd.paciente_id = ?';
$params[] = $filtros['paciente_id'];
}
$where = $wheres ? 'WHERE ' . implode(' AND ', $wheres) : '';
$offset = ($pagina - 1) * $porPagina;
$total = $this->db->fetch(
"SELECT COUNT(*) AS n
FROM lab_domicilios d
LEFT JOIN lab_asignaciones a ON a.domicilio_id = d.id
$where",
$params
)['n'] ?? 0;
$rows = $this->db->fetchAll("
SELECT
d.*,
p.nombre_completo AS paciente_nombre,
p.telefono AS paciente_telefono,
e.nombre_completo AS enfermera_nombre,
e.telefono AS enfermera_telefono,
a.enfermera_id AS enfermera_lab_id,
a.id AS asignacion_id,
a.estado AS asignacion_estado
FROM lab_domicilios d
JOIN lab_pacientes p ON p.id = d.paciente_id
LEFT JOIN lab_asignaciones a ON a.domicilio_id = d.id
LEFT JOIN lab_enfermeras e ON e.id = a.enfermera_id
$where
ORDER BY d.fecha_programada ASC, d.hora_programada ASC
LIMIT ? OFFSET ?
", array_merge($params, [$porPagina, $offset]));
return [
'data' => $rows,
'total' => (int)$total,
'pagina' => $pagina,
'por_pagina' => $porPagina,
'paginas' => (int)ceil($total / $porPagina),
];
}
/**
* Domicilio por ID con todos los datos relacionados.
*/
public function obtener(int $id): ?array {
$dom = $this->db->fetch("
SELECT
d.*,
p.nombre_completo AS paciente_nombre,
p.telefono AS paciente_telefono,
p.direccion AS paciente_direccion,
o.examenes_solicitados,
o.indicaciones AS indicaciones_orden,
o.local_file AS orden_local_file,
e.nombre_completo AS enfermera_nombre,
e.telefono AS enfermera_telefono,
a.enfermera_id AS enfermera_lab_id,
a.id AS asignacion_id,
a.estado AS asignacion_estado,
adm.full_name AS creado_por_nombre
FROM lab_domicilios d
JOIN lab_pacientes p ON p.id = d.paciente_id
LEFT JOIN lab_ordenes_medicas o ON o.id = d.orden_id
LEFT JOIN lab_asignaciones a ON a.domicilio_id = d.id
LEFT JOIN lab_enfermeras e ON e.id = a.enfermera_id
LEFT JOIN admin_users adm ON adm.id = d.creado_por
WHERE d.id = ?
", [$id]);
if (!$dom) return null;
// Todos los archivos de órdenes vinculados a este domicilio
$archivos = $this->db->fetchAll(
"SELECT id AS orden_id, local_file
FROM lab_ordenes_medicas
WHERE domicilio_id = ? AND local_file IS NOT NULL
ORDER BY id ASC",
[$id]
);
// Fallback para domicilios creados antes de la migración (usan orden_id FK)
if (empty($archivos) && !empty($dom['orden_local_file'])) {
$archivos = [['orden_id' => $dom['orden_id'], 'local_file' => $dom['orden_local_file']]];
}
$dom['archivos_ordenes'] = $archivos;
return $dom;
}
/**
* Crear nuevo domicilio.
*/
public function crear(array $datos, ?int $adminId = null): int {
$campos = $this->filtrarCampos($datos);
$campos['creado_por'] = $adminId;
// Actualizar orden médica si aplica
if (!empty($campos['orden_id'])) {
$this->db->update(
'lab_ordenes_medicas',
['estado' => 'en_domicilio'],
'id = ?', [$campos['orden_id']]
);
}
$id = (int)$this->db->insert('lab_domicilios', $campos);
$this->log->registrar($adminId, 'domicilios', 'crear', $id, $campos);
return $id;
}
/**
* Actualizar domicilio.
*/
public function actualizar(int $id, array $datos, ?int $adminId = null): bool {
$campos = $this->filtrarCampos($datos);
$ok = $this->db->update('lab_domicilios', $campos, 'id = ?', [$id]);
$this->log->registrar($adminId, 'domicilios', 'editar', $id, $campos);
return $ok > 0;
}
/**
* Cambiar estado del domicilio.
*/
public function cambiarEstado(
int $id,
string $nuevoEstado,
?int $adminId,
array $extras = []
): bool {
if (!in_array($nuevoEstado, self::ESTADOS)) {
throw new InvalidArgumentException("Estado inválido: $nuevoEstado");
}
$campos = array_merge(['estado' => $nuevoEstado], $extras);
$ok = $this->db->update('lab_domicilios', $campos, 'id = ?', [$id]);
$this->log->registrar($adminId, 'domicilios', $nuevoEstado, $id, $extras);
return $ok > 0;
}
// ─────────────────────────────────────────────────────────────────────────
// Agenda del día
// ─────────────────────────────────────────────────────────────────────────
/**
* Todos los domicilios de hoy (o la fecha indicada).
*/
public function agendaDia(string $fecha = ''): array {
$fecha = $fecha ?: date('Y-m-d');
return $this->listar(['fecha' => $fecha], 1, 200)['data'];
}
/**
* Domicilios sin enfermera asignada.
*/
public function sinAsignar(string $fecha = ''): array {
$fecha = $fecha ?: date('Y-m-d');
return $this->db->fetchAll("
SELECT
d.*,
p.nombre_completo AS paciente_nombre
FROM lab_domicilios d
JOIN lab_pacientes p ON p.id = d.paciente_id
LEFT JOIN lab_asignaciones a ON a.domicilio_id = d.id
WHERE d.fecha_programada = ?
AND a.id IS NULL
AND d.estado NOT IN ('cancelado','completado')
ORDER BY d.hora_programada ASC
", [$fecha]);
}
/**
* Estadísticas: conteo de domicilios por estado hoy.
*/
public function estadisticasHoy(): array {
$rows = $this->db->fetchAll("
SELECT estado, COUNT(*) AS total
FROM lab_domicilios
WHERE fecha_programada = CURDATE()
GROUP BY estado
");
$result = array_fill_keys(self::ESTADOS, 0);
foreach ($rows as $row) {
$result[$row['estado']] = (int)$row['total'];
}
return $result;
}
// ─────────────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────────────
private function filtrarCampos(array $datos): array {
$permitidos = [
'orden_id', 'paciente_id', 'direccion', 'ciudad', 'barrio',
'indicaciones_dir', 'fecha_programada', 'hora_programada',
'tipo_servicio', 'tipo_cliente', 'examenes_solicitados',
'estado', 'motivo_cancelacion',
'fecha_reprogramada', 'hora_llegada', 'hora_salida',
'observaciones', 'muestras_tomadas', 'notas_admin',
'seguro_nombre', 'autorizacion', 'copago_laboratorio',
'valor_domicilio', 'valor_copago',
// pago
'pago_estado', 'pago_modo', 'pago_monto', 'pago_fecha',
'pago_notas', 'pago_registrado_por',
];
$campos = array_intersect_key($datos, array_flip($permitidos));
// Campos FK que deben ser NULL cuando vienen vacíos o cero
foreach (['orden_id', 'paciente_id'] as $fk) {
if (array_key_exists($fk, $campos) && ($campos[$fk] === '' || $campos[$fk] === '0' || $campos[$fk] === 0)) {
$campos[$fk] = null;
}
}
// Normalizar campos de fecha/hora vacíos a NULL para evitar error SQL (MySQL strict)
foreach (['fecha_programada', 'fecha_reprogramada', 'hora_programada', 'hora_llegada', 'hora_salida', 'pago_fecha'] as $dt) {
if (array_key_exists($dt, $campos) && trim((string)$campos[$dt]) === '') {
$campos[$dt] = null;
}
}
// Normalizar campos numéricos opcionales vacíos a NULL para evitar "Incorrect decimal value: ''"
foreach (['valor_domicilio', 'valor_copago', 'copago_laboratorio', 'pago_monto'] as $num) {
if (array_key_exists($num, $campos) && trim((string)$campos[$num]) === '') {
$campos[$num] = null;
}
}
return $campos;
}
}