312 lines
12 KiB
PHP
312 lines
12 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.email AS paciente_email,
|
|
p.fecha_nacimiento AS paciente_fecha_nacimiento,
|
|
p.tipo_documento AS paciente_tipo_documento,
|
|
p.numero_documento AS paciente_numero_documento,
|
|
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;
|
|
|
|
// Servicios extras registrados por el enfermero
|
|
$dom['servicios_extra'] = $this->db->fetchAll(
|
|
"SELECT id, descripcion, tipo, notas, requiere_pago, valor, created_at
|
|
FROM lab_servicios_extra
|
|
WHERE domicilio_id = ?
|
|
ORDER BY created_at ASC",
|
|
[$id]
|
|
);
|
|
|
|
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;
|
|
}
|
|
}
|