237 lines
13 KiB
PHP
237 lines
13 KiB
PHP
<?php
|
|
/**
|
|
* OficioController.php — Controlador de oficios (acciones POST/GET)
|
|
*/
|
|
declare(strict_types=1);
|
|
require_once __DIR__ . '/../config/config.php';
|
|
require_once __DIR__ . '/../controllers/AuthController.php';
|
|
require_once __DIR__ . '/../models/Oficio.php';
|
|
require_once __DIR__ . '/../models/Usuario.php';
|
|
|
|
AuthController::requerirAuth();
|
|
|
|
$action = $_GET['action'] ?? $_POST['action'] ?? '';
|
|
$model = new OficioModel();
|
|
$esAdmin = AuthController::esAdmin();
|
|
$userId = (int)$_SESSION['usuario_id'];
|
|
|
|
switch ($action) {
|
|
|
|
// ── CREAR ──────────────────────────────────────────────────────────────
|
|
case 'crear':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') redirect(APP_URL.'/views/oficios/crear.php');
|
|
verificarCsrf();
|
|
|
|
$datos = [
|
|
'numero_oficio' => clean($_POST['numero_oficio'] ?? ''),
|
|
'tipo' => clean($_POST['tipo'] ?? 'recibido'),
|
|
'remitente' => clean($_POST['remitente'] ?? ''),
|
|
'destinatario' => clean($_POST['destinatario'] ?? ''),
|
|
'asunto' => clean($_POST['asunto'] ?? ''),
|
|
'descripcion' => clean($_POST['descripcion'] ?? ''),
|
|
'fecha_recepcion' => clean($_POST['fecha_recepcion'] ?? ''),
|
|
'fecha_vencimiento' => clean($_POST['fecha_vencimiento'] ?? ''),
|
|
'prioridad' => clean($_POST['prioridad'] ?? 'media'),
|
|
'estado' => clean($_POST['estado'] ?? 'recibido'),
|
|
'responsable_id' => (int)($_POST['responsable_id'] ?? 0),
|
|
'es_confidencial' => isset($_POST['es_confidencial']) ? 1 : 0,
|
|
'etiquetas' => $_POST['etiquetas'] ?? [],
|
|
];
|
|
|
|
$errores = validarOficio($datos, $model);
|
|
|
|
if (!empty($errores)) {
|
|
$_SESSION['errores_form'] = $errores;
|
|
$_SESSION['datos_form'] = $datos;
|
|
redirect(APP_URL.'/views/oficios/crear.php');
|
|
}
|
|
|
|
$id = $model->crear($datos, $userId);
|
|
|
|
// Subir adjuntos
|
|
subirAdjuntos($id, $userId);
|
|
|
|
// Crear notificación al responsable
|
|
if ($datos['responsable_id']) {
|
|
crearNotificacion($datos['responsable_id'], $id, 'sistema',
|
|
'Nuevo oficio asignado',
|
|
"Se te ha asignado el oficio {$datos['numero_oficio']}: {$datos['asunto']}");
|
|
}
|
|
|
|
logActividad($userId, 'crear_oficio', 'oficios', "Oficio #{$datos['numero_oficio']} creado.");
|
|
redirect(APP_URL.'/views/oficios/detalle.php?id='.$id.'&success='.urlencode('Oficio creado exitosamente.'));
|
|
|
|
// ── ACTUALIZAR ─────────────────────────────────────────────────────────
|
|
case 'actualizar':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') redirect(APP_URL.'/views/oficios/lista.php');
|
|
verificarCsrf();
|
|
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
if (!$id) redirect(APP_URL.'/views/oficios/lista.php');
|
|
|
|
$datos = [
|
|
'numero_oficio' => clean($_POST['numero_oficio'] ?? ''),
|
|
'tipo' => clean($_POST['tipo'] ?? 'recibido'),
|
|
'remitente' => clean($_POST['remitente'] ?? ''),
|
|
'destinatario' => clean($_POST['destinatario'] ?? ''),
|
|
'asunto' => clean($_POST['asunto'] ?? ''),
|
|
'descripcion' => clean($_POST['descripcion'] ?? ''),
|
|
'fecha_recepcion' => clean($_POST['fecha_recepcion'] ?? ''),
|
|
'fecha_vencimiento' => clean($_POST['fecha_vencimiento'] ?? ''),
|
|
'prioridad' => clean($_POST['prioridad'] ?? 'media'),
|
|
'estado' => clean($_POST['estado'] ?? 'recibido'),
|
|
'responsable_id' => (int)($_POST['responsable_id'] ?? 0),
|
|
'es_confidencial' => isset($_POST['es_confidencial']) ? 1 : 0,
|
|
'etiquetas' => $_POST['etiquetas'] ?? [],
|
|
];
|
|
|
|
$errores = validarOficio($datos, $model, $id);
|
|
|
|
if (!empty($errores)) {
|
|
$_SESSION['errores_form'] = $errores;
|
|
$_SESSION['datos_form'] = $datos;
|
|
redirect(APP_URL.'/views/oficios/editar.php?id='.$id);
|
|
}
|
|
|
|
$model->actualizar($id, $datos, $userId);
|
|
subirAdjuntos($id, $userId);
|
|
logActividad($userId, 'editar_oficio', 'oficios', "Oficio #$id actualizado.");
|
|
redirect(APP_URL.'/views/oficios/detalle.php?id='.$id.'&success='.urlencode('Oficio actualizado exitosamente.'));
|
|
|
|
// ── ELIMINAR (lógico) ──────────────────────────────────────────────────
|
|
case 'eliminar':
|
|
verificarCsrf();
|
|
$id = (int)($_GET['id'] ?? $_POST['id'] ?? 0);
|
|
if (!$id) redirect(APP_URL.'/views/oficios/lista.php');
|
|
$model->eliminarLogico($id, $userId);
|
|
logActividad($userId, 'eliminar_oficio', 'oficios', "Oficio #$id movido a papelera.");
|
|
redirect(APP_URL.'/views/oficios/lista.php?success='.urlencode('Oficio movido a la papelera.'));
|
|
|
|
// ── ELIMINAR FÍSICO (solo admin) ───────────────────────────────────────
|
|
case 'eliminar_fisico':
|
|
AuthController::requerirAdmin();
|
|
verificarCsrf();
|
|
$id = (int)($_GET['id'] ?? $_POST['id'] ?? 0);
|
|
if (!$id) redirect(APP_URL.'/views/oficios/papelera.php');
|
|
$model->eliminarFisico($id);
|
|
logActividad($userId, 'eliminar_fisico_oficio', 'oficios', "Oficio #$id eliminado permanentemente.");
|
|
redirect(APP_URL.'/views/oficios/papelera.php?success='.urlencode('Oficio eliminado permanentemente.'));
|
|
|
|
// ── RESTAURAR ─────────────────────────────────────────────────────────
|
|
case 'restaurar':
|
|
AuthController::requerirAdmin();
|
|
verificarCsrf();
|
|
$id = (int)($_GET['id'] ?? $_POST['id'] ?? 0);
|
|
if (!$id) redirect(APP_URL.'/views/oficios/papelera.php');
|
|
$model->restaurar($id, $userId);
|
|
logActividad($userId, 'restaurar_oficio', 'oficios', "Oficio #$id restaurado.");
|
|
redirect(APP_URL.'/views/oficios/papelera.php?success='.urlencode('Oficio restaurado exitosamente.'));
|
|
|
|
// ── DERIVAR ───────────────────────────────────────────────────────────
|
|
case 'derivar':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') redirect(APP_URL.'/views/oficios/lista.php');
|
|
verificarCsrf();
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
$nuevoResponsable= (int)($_POST['nuevo_responsable'] ?? 0);
|
|
$comentario = clean($_POST['comentario_derivacion'] ?? '');
|
|
|
|
if (!$id || !$nuevoResponsable) redirect(APP_URL.'/views/oficios/detalle.php?id='.$id.'&error='.urlencode('Datos incompletos para la derivación.'));
|
|
|
|
$model->derivar($id, $nuevoResponsable, $userId, $comentario);
|
|
crearNotificacion($nuevoResponsable, $id, 'derivacion',
|
|
'Oficio derivado hacia ti',
|
|
"Se te ha derivado el oficio: $comentario");
|
|
logActividad($userId, 'derivar_oficio', 'oficios', "Oficio #$id derivado a usuario #$nuevoResponsable.");
|
|
redirect(APP_URL.'/views/oficios/detalle.php?id='.$id.'&success='.urlencode('Oficio derivado exitosamente.'));
|
|
|
|
// ── COMENTARIO ────────────────────────────────────────────────────────
|
|
case 'comentar':
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') redirect(APP_URL.'/views/oficios/lista.php');
|
|
verificarCsrf();
|
|
$id = (int)($_POST['oficio_id'] ?? 0);
|
|
$texto = clean($_POST['comentario'] ?? '');
|
|
$privado = isset($_POST['es_privado']);
|
|
if (!$id || strlen($texto) < 2) redirect(APP_URL.'/views/oficios/detalle.php?id='.$id.'&error='.urlencode('El comentario no puede estar vacío.'));
|
|
$model->agregarComentario($id, $userId, $texto, $privado);
|
|
redirect(APP_URL.'/views/oficios/detalle.php?id='.$id.'&success='.urlencode('Comentario agregado.'));
|
|
|
|
// ── GENERAR NÚMERO ────────────────────────────────────────────────────
|
|
case 'gen_numero':
|
|
$tipo = clean($_GET['tipo'] ?? 'recibido');
|
|
jsonResponse(['numero' => $model->generarNumero($tipo)]);
|
|
|
|
// ── MARCAR NOTIFICACIÓN LEÍDA ─────────────────────────────────────────
|
|
case 'marcar_notif':
|
|
$nId = (int)($_GET['id'] ?? 0);
|
|
if ($nId) {
|
|
$db = getDB();
|
|
$db->prepare("UPDATE notificaciones SET leida=1,leida_at=NOW() WHERE id=? AND usuario_id=?")
|
|
->execute([$nId, $userId]);
|
|
}
|
|
redirect(APP_URL.'/views/oficios/detalle.php?id='.($_GET['oficio'] ?? 0));
|
|
|
|
// ── PDF ───────────────────────────────────────────────────────────────
|
|
case 'pdf':
|
|
$id = (int)($_GET['id'] ?? 0);
|
|
$datos = $model->buscarPorId($id);
|
|
if (!$dados = $datos) redirect(APP_URL.'/views/oficios/lista.php');
|
|
require_once __DIR__ . '/../controllers/ReporteController.php';
|
|
ReporteController::generarPdfOficio($datos);
|
|
break;
|
|
|
|
default:
|
|
redirect(APP_URL.'/views/oficios/lista.php');
|
|
}
|
|
|
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
|
|
function validarOficio(array $datos, OficioModel $model, ?int $excepto = null): array {
|
|
$errores = [];
|
|
if (empty($datos['numero_oficio'])) $errores[] = 'El número de oficio es requerido.';
|
|
elseif ($model->existeNumero($datos['numero_oficio'], $excepto)) $errores[] = 'El número de oficio ya existe en el sistema.';
|
|
if (empty($datos['remitente'])) $errores[] = 'El remitente es requerido.';
|
|
if (empty($datos['destinatario'])) $errores[] = 'El destinatario es requerido.';
|
|
if (empty($datos['asunto'])) $errores[] = 'El asunto es requerido.';
|
|
if (empty($datos['fecha_recepcion'])) $errores[] = 'La fecha de recepción es requerida.';
|
|
if (!in_array($datos['tipo'], ['recibido','enviado'])) $errores[] = 'Tipo inválido.';
|
|
if (!in_array($datos['prioridad'], ['alta','media','baja'])) $errores[] = 'Prioridad inválida.';
|
|
if (!in_array($datos['estado'], ['recibido','en_proceso','respondido','vencido','archivado'])) $errores[] = 'Estado inválido.';
|
|
return $errores;
|
|
}
|
|
|
|
function subirAdjuntos(int $oficioId, int $userId): void {
|
|
if (empty($_FILES['adjuntos']['name'][0])) return;
|
|
$db = getDB();
|
|
$count = count($_FILES['adjuntos']['name']);
|
|
for ($i = 0; $i < $count; $i++) {
|
|
if ($_FILES['adjuntos']['error'][$i] !== UPLOAD_ERR_OK) continue;
|
|
$originalName = $_FILES['adjuntos']['name'][$i];
|
|
$ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
|
|
if (!in_array($ext, UPLOAD_ALLOWED_TYPES)) continue;
|
|
if ($_FILES['adjuntos']['size'][$i] > UPLOAD_MAX_SIZE) continue;
|
|
$safeName = date('Ymd_His') . '_' . bin2hex(random_bytes(6)) . '.' . $ext;
|
|
$destDir = UPLOAD_PATH . 'oficios/' . $oficioId . '/';
|
|
if (!is_dir($destDir)) mkdir($destDir, 0755, true);
|
|
if (move_uploaded_file($_FILES['adjuntos']['tmp_name'][$i], $destDir . $safeName)) {
|
|
$db->prepare(
|
|
"INSERT INTO documentos_adjuntos (oficio_id,nombre_original,nombre_archivo,ruta,tipo_mime,tamanio,subido_por)
|
|
VALUES (?,?,?,?,?,?,?)"
|
|
)->execute([$oficioId, $originalName, $safeName, 'oficios/'.$oficioId.'/'.$safeName, $_FILES['adjuntos']['type'][$i], $_FILES['adjuntos']['size'][$i], $userId]);
|
|
}
|
|
}
|
|
}
|
|
|
|
function crearNotificacion(int $usuarioId, ?int $oficioId, string $tipo, string $titulo, string $mensaje): void {
|
|
$db = getDB();
|
|
$db->prepare(
|
|
"INSERT INTO notificaciones (usuario_id,oficio_id,tipo,titulo,mensaje) VALUES (?,?,?,?,?)"
|
|
)->execute([$usuarioId, $oficioId, $tipo, $titulo, $mensaje]);
|
|
}
|
|
|
|
function logActividad(int $userId, string $accion, string $modulo, string $desc = ''): void {
|
|
$db = getDB();
|
|
$db->prepare(
|
|
"INSERT INTO log_actividad (usuario_id,accion,modulo,descripcion,ip_address,user_agent) VALUES (?,?,?,?,?,?)"
|
|
)->execute([$userId, $accion, $modulo, $desc, $_SERVER['REMOTE_ADDR']??null, $_SERVER['HTTP_USER_AGENT']??null]);
|
|
}
|