- Crear nuevo rol Coordinador con permisos específicos de gestión - Modificar Auth.php para soportar isCoordinador() y requireCoordinador() - Actualizar User.php con método getUsuariosGestion() para incluir coordinadores - Corregir Asignacion.php para que getAyudantesPorOrden() incluya coordinadores - Crear panel especial para coordinadores en coordinador.php - Implementar restricciones granulares en usuarios.php • Coordinadores no pueden ver/editar/desactivar administradores • No pueden crear otros administradores (se convierte a coordinador) • Solo pueden gestionar ayudantes y otros coordinadores - Actualizar navbar para mostrar rol específico con badges - Mejorar ayudante.php para que coordinadores puedan usar navbar completo - Añadir secciones especiales de gestión para coordinadores - Actualizar todos los PDFs y bot de Telegram para incluir coordinadores - Mantener retrocompatibilidad con usuarios y administradores existentes Permisos Coordinador: ✅ Ver/editar usuarios y ayudantes ✅ Gestionar turnos y orden de rotación ✅ Generar turnos automáticamente ✅ Exportar PDFs y usar bot de Telegram ❌ Acceder a configuración general, logs, webhook ❌ Administrar otros administradores
394 lines
15 KiB
PHP
Executable File
394 lines
15 KiB
PHP
Executable File
<?php
|
|
|
|
require_once __DIR__ . '/Database.php';
|
|
|
|
class Asignacion {
|
|
private $db;
|
|
|
|
public function __construct() {
|
|
$this->db = Database::getInstance()->getConnection();
|
|
}
|
|
|
|
public function getDb() {
|
|
return $this->db;
|
|
}
|
|
|
|
public function getAsignacionActual() {
|
|
$hoy = new DateTime();
|
|
$diaSemana = (int)$hoy->format('w');
|
|
$domingo = clone $hoy;
|
|
$domingo->modify('-' . $diaSemana . ' days');
|
|
$currentWeekStart = $domingo->format('Y-m-d');
|
|
|
|
$stmt = $this->db->prepare("
|
|
SELECT u.*, a.semana_inicio
|
|
FROM asignaciones_turnos a
|
|
JOIN users u ON a.user_id = u.id
|
|
WHERE a.semana_inicio = ?
|
|
");
|
|
$stmt->execute([$currentWeekStart]);
|
|
return $stmt->fetch();
|
|
}
|
|
|
|
public function getAsignacionPorSemana($semanaInicio) {
|
|
$stmt = $this->db->prepare("
|
|
SELECT u.*, a.semana_inicio, a.semana_fin, a.id as asignacion_id
|
|
FROM asignaciones_turnos a
|
|
JOIN users u ON a.user_id = u.id
|
|
WHERE a.semana_inicio = ?
|
|
ORDER BY a.orden_turno ASC
|
|
LIMIT 1
|
|
");
|
|
$stmt->execute([$semanaInicio]);
|
|
return $stmt->fetch();
|
|
}
|
|
|
|
public function getTodasAsignaciones() {
|
|
$stmt = $this->db->query("
|
|
SELECT u.nombre, a.*
|
|
FROM asignaciones_turnos a
|
|
JOIN users u ON a.user_id = u.id
|
|
ORDER BY a.semana_inicio DESC
|
|
LIMIT 20
|
|
");
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
public function asignar($userId, $semanaInicio) {
|
|
$semanaFin = date('Y-m-d', strtotime('+5 days', strtotime($semanaInicio))); // Domingo a viernes
|
|
$ordenTurno = $this->getOrdenTurno($userId);
|
|
$stmt = $this->db->prepare("
|
|
INSERT INTO asignaciones_turnos (user_id, semana_inicio, semana_fin, orden_turno)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE user_id = VALUES(user_id), semana_fin = VALUES(semana_fin), orden_turno = VALUES(orden_turno)
|
|
");
|
|
return $stmt->execute([$userId, $semanaInicio, $semanaFin, $ordenTurno]);
|
|
}
|
|
|
|
private function getOrdenTurno($userId) {
|
|
$stmt = $this->db->prepare("SELECT orden FROM rotacion_orden WHERE user_id = ? AND activo = 1");
|
|
$stmt->execute([$userId]);
|
|
$result = $stmt->fetch();
|
|
return $result ? $result['orden'] : 999;
|
|
}
|
|
|
|
public function getProximaPersona($userIdActual = null) {
|
|
$sql = "SELECT * FROM users WHERE rol = 'ayudante' AND activo = 1 ORDER BY id";
|
|
if ($userIdActual) {
|
|
$stmt = $this->db->query($sql);
|
|
$ayudantes = $stmt->fetchAll();
|
|
|
|
$encontrado = false;
|
|
foreach ($ayudantes as $a) {
|
|
if ($encontrado) return $a;
|
|
if ($a['id'] == $userIdActual) $encontrado = true;
|
|
}
|
|
return $ayudantes[0] ?? null;
|
|
}
|
|
$stmt = $this->db->query($sql);
|
|
return $stmt->fetch();
|
|
}
|
|
|
|
public function asignarMasivo($userIds, $semanaInicio, $rotacionAutomatica = false) {
|
|
$resultados = [];
|
|
$errores = [];
|
|
|
|
foreach ($userIds as $index => $userId) {
|
|
try {
|
|
$semanaFin = date('Y-m-d', strtotime('+5 days', strtotime($semanaInicio))); // Domingo a viernes
|
|
$ordenTurno = $this->getOrdenTurno($userId);
|
|
|
|
$stmt = $this->db->prepare("
|
|
INSERT INTO asignaciones_turnos (user_id, semana_inicio, semana_fin, orden_turno)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE user_id = VALUES(user_id), semana_fin = VALUES(semana_fin), orden_turno = VALUES(orden_turno)
|
|
");
|
|
|
|
$success = $stmt->execute([$userId, $semanaInicio, $semanaFin, $ordenTurno]);
|
|
|
|
if ($success) {
|
|
$resultados[] = [
|
|
'user_id' => $userId,
|
|
'semana' => $semanaInicio,
|
|
'rotar_siguiente' => $rotacionAutomatica && $index === count($userIds) - 1
|
|
];
|
|
|
|
// Si es el último usuario y se activó rotación automática
|
|
if ($rotacionAutomatica && $index === count($userIds) - 1) {
|
|
$this->asignarSiguienteSemana($userId, $semanaInicio);
|
|
}
|
|
} else {
|
|
$errores[] = "Error al asignar usuario ID: $userId";
|
|
}
|
|
} catch (Exception $e) {
|
|
$errores[] = "Error usuario ID $userId: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
return [
|
|
'success' => count($resultados),
|
|
'errors' => $errores,
|
|
'resultados' => $resultados
|
|
];
|
|
}
|
|
|
|
private function asignarSiguienteSemana($ultimoUserId, $semanaActual) {
|
|
$siguienteSemana = date('Y-m-d', strtotime('+1 week', strtotime($semanaActual)));
|
|
$siguientePersona = $this->getProximaPersona($ultimoUserId);
|
|
|
|
if ($siguientePersona) {
|
|
$semanaFin = date('Y-m-d', strtotime('+5 days', strtotime($siguienteSemana))); // Domingo a viernes
|
|
$ordenTurno = $this->getOrdenTurno($siguientePersona['id']);
|
|
|
|
$stmt = $this->db->prepare("
|
|
INSERT INTO asignaciones_turnos (user_id, semana_inicio, semana_fin, orden_turno)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE user_id = VALUES(user_id), semana_fin = VALUES(semana_fin), orden_turno = VALUES(orden_turno)
|
|
");
|
|
|
|
return $stmt->execute([$siguientePersona['id'], $siguienteSemana, $semanaFin, $ordenTurno]);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function asignarSemanasFuturasAutomaticas($semanasFuturas = 12) {
|
|
$resultados = [];
|
|
$errores = [];
|
|
|
|
// Obtener todos los ayudantes en orden de rotación
|
|
$ayudantesOrdenados = $this->getAyudantesPorOrden();
|
|
|
|
if (empty($ayudantesOrdenados)) {
|
|
return ['success' => 0, 'errors' => ['No hay ayudantes configurados'], 'resultados' => []];
|
|
}
|
|
|
|
// Encontrar la última semana asignada
|
|
$ultimaAsignacion = $this->getUltimaAsignacion();
|
|
$semanaActual = $ultimaAsignacion
|
|
? date('Y-m-d', strtotime('+1 week', strtotime($ultimaAsignacion['semana_inicio'])))
|
|
: date('Y-m-d', strtotime('last sunday', strtotime('today'))); // Empezar desde domingo
|
|
|
|
// Determinar el siguiente ayudante en el ciclo
|
|
$indiceActual = 0;
|
|
if ($ultimaAsignacion) {
|
|
$indiceActual = $this->findIndiceSiguiente($ultimaAsignacion['user_id'], $ayudantesOrdenados);
|
|
}
|
|
|
|
// Asignar semanas futuras
|
|
for ($i = 0; $i < $semanasFuturas; $i++) {
|
|
$semanaInicio = date('Y-m-d', strtotime("+$i weeks", strtotime($semanaActual)));
|
|
$semanaFin = date('Y-m-d', strtotime('+5 days', strtotime($semanaInicio))); // Domingo a viernes
|
|
|
|
// Seleccionar ayudante usando ciclo cíclico
|
|
$ayudanteIndex = ($indiceActual + $i) % count($ayudantesOrdenados);
|
|
$ayudante = $ayudantesOrdenados[$ayudanteIndex];
|
|
|
|
try {
|
|
$ordenTurno = $this->getOrdenTurno($ayudante['id']);
|
|
|
|
$stmt = $this->db->prepare("
|
|
INSERT INTO asignaciones_turnos (user_id, semana_inicio, semana_fin, orden_turno)
|
|
VALUES (?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE user_id = VALUES(user_id), semana_fin = VALUES(semana_fin), orden_turno = VALUES(orden_turno)
|
|
");
|
|
|
|
$success = $stmt->execute([$ayudante['id'], $semanaInicio, $semanaFin, $ordenTurno]);
|
|
|
|
if ($success) {
|
|
$resultados[] = [
|
|
'semana' => $semanaInicio,
|
|
'usuario' => $ayudante['nombre'],
|
|
'orden' => $ordenTurno
|
|
];
|
|
} else {
|
|
$errores[] = "Error al asignar semana $semanaInicio a {$ayudante['nombre']}";
|
|
}
|
|
} catch (Exception $e) {
|
|
$errores[] = "Error semana $semanaInicio: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
return [
|
|
'success' => count($resultados),
|
|
'errors' => $errores,
|
|
'resultados' => $resultados
|
|
];
|
|
}
|
|
|
|
public function getAyudantesPorOrden() {
|
|
$stmt = $this->db->query("
|
|
SELECT u.*, ro.orden
|
|
FROM users u
|
|
LEFT JOIN rotacion_orden ro ON u.id = ro.user_id AND ro.activo = 1
|
|
WHERE (u.rol = 'ayudante' OR u.rol = 'coordinador') AND u.activo = 1
|
|
ORDER BY COALESCE(ro.orden, 999), u.nombre
|
|
");
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
private function getUltimaAsignacion() {
|
|
$stmt = $this->db->query("
|
|
SELECT a.*, u.nombre
|
|
FROM asignaciones_turnos a
|
|
JOIN users u ON a.user_id = u.id
|
|
WHERE a.semana_inicio <= CURDATE()
|
|
ORDER BY a.semana_inicio DESC
|
|
LIMIT 1
|
|
");
|
|
return $stmt->fetch();
|
|
}
|
|
|
|
private function findIndiceSiguiente($ultimoUserId, $ayudantesOrdenados) {
|
|
foreach ($ayudantesOrdenados as $index => $ayudante) {
|
|
if ($ayudante['id'] == $ultimoUserId) {
|
|
return ($index + 1) % count($ayudantesOrdenados);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public function recalcularAsignaciones($semanasFuturas = 20) {
|
|
$resultados = [];
|
|
$errores = [];
|
|
|
|
// Obtener todos los ayudantes en orden de rotación
|
|
$ayudantesOrdenados = $this->getAyudantesPorOrden();
|
|
|
|
if (empty($ayudantesOrdenados)) {
|
|
return ['success' => 0, 'errors' => ['No hay ayudantes configurados'], 'resultados' => []];
|
|
}
|
|
|
|
// Encontrar la última asignación existente (histórica)
|
|
$stmt = $this->db->query("
|
|
SELECT a.*, u.nombre
|
|
FROM asignaciones_turnos a
|
|
JOIN users u ON a.user_id = u.id
|
|
ORDER BY a.semana_inicio DESC
|
|
LIMIT 1
|
|
");
|
|
$ultimaAsignacion = $stmt->fetch();
|
|
|
|
// Determinar desde dónde empezar a recalcular
|
|
if ($ultimaAsignacion) {
|
|
$semanaInicio = date('Y-m-d', strtotime('+1 week', strtotime($ultimaAsignacion['semana_inicio'])));
|
|
|
|
// Encontrar posición del último usuario en el nuevo orden
|
|
$indiceInicial = 0;
|
|
foreach ($ayudantesOrdenados as $index => $ayudante) {
|
|
if ($ayudante['id'] == $ultimaAsignacion['user_id']) {
|
|
$indiceInicial = ($index + 1) % count($ayudantesOrdenados);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// No hay asignaciones, empezar desde el próximo domingo
|
|
$hoy = new DateTime();
|
|
$diaSemana = (int)$hoy->format('w');
|
|
$domingo = clone $hoy;
|
|
$domingo->modify('-' . $diaSemana . ' days');
|
|
$semanaInicio = $domingo->format('Y-m-d');
|
|
$indiceInicial = 0;
|
|
}
|
|
|
|
// Eliminar asignaciones futuras
|
|
$stmt = $this->db->prepare("DELETE FROM asignaciones_turnos WHERE semana_inicio >= ?");
|
|
$stmt->execute([$semanaInicio]);
|
|
|
|
// Generar nuevas asignaciones
|
|
for ($i = 0; $i < $semanasFuturas; $i++) {
|
|
$siguienteDomingo = new DateTime($semanaInicio);
|
|
$siguienteDomingo->modify("+{$i} weeks");
|
|
|
|
$fechaInicio = $siguienteDomingo->format('Y-m-d');
|
|
$fechaFin = $siguienteDomingo->modify('+5 days')->format('Y-m-d');
|
|
|
|
$ayudanteIndex = ($indiceInicial + $i) % count($ayudantesOrdenados);
|
|
$ayudante = $ayudantesOrdenados[$ayudanteIndex];
|
|
|
|
try {
|
|
$stmt = $this->db->prepare("
|
|
INSERT INTO asignaciones_turnos (user_id, semana_inicio, semana_fin, orden_turno)
|
|
VALUES (?, ?, ?, ?)
|
|
");
|
|
|
|
$success = $stmt->execute([
|
|
$ayudante['id'],
|
|
$fechaInicio,
|
|
$fechaFin,
|
|
$ayudante['orden']
|
|
]);
|
|
|
|
if ($success) {
|
|
$resultados[] = [
|
|
'semana' => $fechaInicio,
|
|
'usuario' => $ayudante['nombre'],
|
|
'orden' => $ayudante['orden']
|
|
];
|
|
} else {
|
|
$errores[] = "Error al asignar semana $fechaInicio a {$ayudante['nombre']}";
|
|
}
|
|
} catch (Exception $e) {
|
|
$errores[] = "Error semana $fechaInicio: " . $e->getMessage();
|
|
}
|
|
}
|
|
|
|
return [
|
|
'success' => count($resultados),
|
|
'errors' => $errores,
|
|
'resultados' => $resultados
|
|
];
|
|
}
|
|
|
|
public function inicializarOrdenRotacion() {
|
|
$ayudantes = $this->getAyudantesPorOrden();
|
|
$errores = [];
|
|
$actualizados = 0;
|
|
|
|
foreach ($ayudantes as $index => $ayudante) {
|
|
if ($ayudante['orden'] === null) {
|
|
try {
|
|
$stmt = $this->db->prepare("
|
|
INSERT INTO rotacion_orden (user_id, orden, activo)
|
|
VALUES (?, ?, 1)
|
|
ON DUPLICATE KEY UPDATE orden = VALUES(orden), activo = 1
|
|
");
|
|
$stmt->execute([$ayudante['id'], $index + 1]);
|
|
$actualizados++;
|
|
} catch (Exception $e) {
|
|
$errores[] = "Error con {$ayudante['nombre']}: " . $e->getMessage();
|
|
}
|
|
}
|
|
}
|
|
|
|
return [
|
|
'actualizados' => $actualizados,
|
|
'errores' => $errores
|
|
];
|
|
}
|
|
|
|
public function getAsignacionesPorRango($semanaInicio, $semanaFin) {
|
|
$stmt = $this->db->prepare("
|
|
SELECT u.*, a.semana_inicio, a.semana_fin, a.orden_turno
|
|
FROM asignaciones_turnos a
|
|
JOIN users u ON a.user_id = u.id
|
|
WHERE a.semana_inicio >= ? AND a.semana_inicio <= ?
|
|
ORDER BY a.semana_inicio, u.nombre
|
|
");
|
|
$stmt->execute([$semanaInicio, $semanaFin]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
public function getTodasAsignacionesPorSemana($semanaInicio) {
|
|
$stmt = $this->db->prepare("
|
|
SELECT u.*, a.semana_inicio, a.orden_turno
|
|
FROM asignaciones_turnos a
|
|
JOIN users u ON a.user_id = u.id
|
|
WHERE a.semana_inicio = ?
|
|
ORDER BY a.orden_turno
|
|
");
|
|
$stmt->execute([$semanaInicio]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
}
|