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.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 { return $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, e.nombre_completo AS enfermera_nombre, e.telefono AS enfermera_telefono, 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]) ?: null; } /** * 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; } } return $campos; } }