ProyectoGestionDocumentos/controllers/ReporteController.php

200 lines
7.9 KiB
PHP

<?php
/**
* ReporteController.php — Exportación de PDF e informes
*/
declare(strict_types=1);
require_once __DIR__ . '/../config/config.php';
require_once __DIR__ . '/../controllers/AuthController.php';
require_once __DIR__ . '/../models/Oficio.php';
class ReporteController {
/**
* Genera PDF de un oficio individual usando DomPDF si está disponible,
* o HTML simple de fallback
*/
public static function generarPdfOficio(array $oficio): void {
$dompdfPath = BASE_PATH . '/lib/dompdf/autoload.inc.php';
$html = self::htmlOficio($oficio);
if (file_exists($dompdfPath)) {
require_once $dompdfPath;
$options = new \Dompdf\Options();
$options->set('defaultFont', 'DejaVu Sans');
$options->setIsHtml5ParserEnabled(true);
$options->setIsFontSubsettingEnabled(true);
$dompdf = new \Dompdf\Dompdf($options);
$dompdf->loadHtml($html);
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();
$filename = 'Oficio_' . preg_replace('/[^A-Za-z0-9\-_]/', '_', $oficio['numero_oficio']) . '_' . date('Ymd') . '.pdf';
$dompdf->stream($filename, ['Attachment' => true]);
} else {
// Fallback: HTML descargable
header('Content-Type: text/html; charset=utf-8');
header('Content-Disposition: attachment; filename="oficio_' . $oficio['id'] . '.html"');
echo $html;
}
exit();
}
/**
* Genera HTML del oficio para PDF
*/
private static function htmlOficio(array $o): string {
$logo = file_exists(LOGO_PATH) ? '<img src="' . LOGO_PATH . '" style="max-height:60px">' : '';
$fecha = date('d/m/Y H:i');
$vence = $o['fecha_vencimiento'] ? date('d/m/Y', strtotime($o['fecha_vencimiento'])) : 'Sin fecha';
$recep = date('d/m/Y', strtotime($o['fecha_recepcion']));
$etiquetas = $o['etiquetas'] ?? '—';
$colorPrioridad = ['alta' => '#d32f2f', 'media' => '#fbc02d', 'baja' => '#388e3c'];
$colorEstado = ['recibido' => '#2e7d32', 'en_proceso' => '#fbc02d', 'respondido' => '#0288d1', 'vencido' => '#d32f2f', 'archivado' => '#64748b'];
$pColor = $colorPrioridad[$o['prioridad']] ?? '#64748b';
$eColor = $colorEstado[$o['estado']] ?? '#64748b';
return <<<HTML
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: DejaVu Sans, Arial, sans-serif; font-size: 12px; color: #1e293b; }
.header { background: #1e1b4b; color: white; padding: 20px 30px; display: flex; justify-content: space-between; align-items: center; }
.header-title { font-size: 11px; opacity: .7; margin-top: 4px; }
.content { padding: 30px; }
.oficio-title { font-size: 20px; font-weight: bold; color: #1e1b4b; margin-bottom: 4px; }
.oficio-asunto { color: #475569; font-size: 13px; margin-bottom: 24px; }
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 20px; }
.field-group { border: 1px solid #e2e8f0; border-radius: 8px; padding: 12px; }
.field-label { font-size: 9px; font-weight: bold; color: #94a3b8; text-transform: uppercase; letter-spacing: .05em; margin-bottom: 4px; }
.field-value { font-size: 12px; font-weight: 600; }
.badge { display: inline-block; padding: 3px 10px; border-radius: 99px; font-size: 10px; font-weight: bold; }
.section-title { font-size: 11px; font-weight: bold; color: #64748b; text-transform: uppercase; letter-spacing: .05em; border-bottom: 2px solid #e2e8f0; padding-bottom: 6px; margin: 20px 0 12px; }
.description-box { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 14px; font-size: 12px; line-height: 1.7; min-height: 60px; }
.footer { margin-top: 40px; border-top: 1px solid #e2e8f0; padding-top: 20px; display: flex; justify-content: space-between; }
.firma-box { border-top: 1px solid #1e1b4b; width: 200px; text-align: center; padding-top: 6px; font-size: 11px; }
.watermark { color: #94a3b8; font-size: 10px; }
</style>
</head>
<body>
<div class="header">
<div>
<div style="font-size:15px;font-weight:bold">OFICIO DTIC</div>
<div class="header-title">{$o['numero_oficio']} &nbsp;·&nbsp; {$o['tipo']}</div>
</div>
<div style="text-align:right">
$logo
<div style="font-size:10px;opacity:.7;margin-top:4px">{INSTITUCION_NOMBRE}</div>
</div>
</div>
<div class="content">
<div class="oficio-title">{$o['numero_oficio']}</div>
<div class="oficio-asunto">{$o['asunto']}</div>
<div class="grid">
<div class="field-group">
<div class="field-label">Remitente</div>
<div class="field-value">{$o['remitente']}</div>
</div>
<div class="field-group">
<div class="field-label">Destinatario</div>
<div class="field-value">{$o['destinatario']}</div>
</div>
<div class="field-group">
<div class="field-label">Fecha de Recepción</div>
<div class="field-value">$recep</div>
</div>
<div class="field-group">
<div class="field-label">Fecha de Vencimiento</div>
<div class="field-value">$vence</div>
</div>
<div class="field-group">
<div class="field-label">Prioridad</div>
<div class="field-value">
<span class="badge" style="background:{$pColor}20;color:$pColor">{$o['prioridad']}</span>
</div>
</div>
<div class="field-group">
<div class="field-label">Estado</div>
<div class="field-value">
<span class="badge" style="background:{$eColor}20;color:$eColor">{$o['estado']}</span>
</div>
</div>
<div class="field-group">
<div class="field-label">Responsable</div>
<div class="field-value">{$o['responsable_nombre']}</div>
</div>
<div class="field-group">
<div class="field-label">Etiquetas</div>
<div class="field-value">$etiquetas</div>
</div>
</div>
<div class="section-title">Descripción</div>
<div class="description-box">{$o['descripcion']}</div>
<div class="footer">
<div class="watermark">
Generado el $fecha &nbsp;·&nbsp; {APP_NAME}<br>
Documento generado automáticamente. No requiere firma.
</div>
<div>
<div class="firma-box">
<div>{INSTITUCION_CARGO_FIRMA}</div>
</div>
</div>
</div>
</div>
</body>
</html>
HTML;
}
/**
* Descarga backup de la BD como SQL
*/
public static function descargarBackupSQL(): void {
$dbName = env('DB_NAME', 'gestion_documentos');
$dbHost = env('DB_HOST', 'localhost');
$dbUser = env('DB_USER', 'root');
$dbPass = env('DB_PASS', '');
$fecha = date('Ymd_His');
$archivo = "$dbName-backup-$fecha.sql";
$backupDir = BASE_PATH . '/exports/backups/';
if (!is_dir($backupDir)) mkdir($backupDir, 0755, true);
$cmd = sprintf(
'mysqldump --host=%s --user=%s --password=%s %s > %s',
escapeshellarg($dbHost),
escapeshellarg($dbUser),
escapeshellarg($dbPass),
escapeshellarg($dbName),
escapeshellarg($backupDir . $archivo)
);
system($cmd, $ret);
if ($ret === 0 && file_exists($backupDir . $archivo)) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $archivo . '"');
header('Content-Length: ' . filesize($backupDir . $archivo));
readfile($backupDir . $archivo);
} else {
http_response_code(500);
echo 'Error al generar el respaldo. Verifique mysqldump en el servidor.';
}
exit();
}
}
// Dispatcher si se invoca directamente
if (basename($_SERVER['PHP_SELF']) === 'ReporteController.php') {
AuthController::requerirAdmin();
$action = $_GET['action'] ?? '';
if ($action === 'backup_sql') ReporteController::descargarBackupSQL();
redirect(APP_URL . '/views/reportes/index.php');
}