Files
MVE/resumen_diario.php
2025-05-30 10:59:54 -06:00

494 lines
20 KiB
PHP
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once __DIR__ . '/../../config/database.php';
require_once __DIR__ . '/../helpers/crypto.php';
require_once __DIR__ . '/../helpers/env.php';
require_once __DIR__ . '/../../vendor/autoload.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
loadEnv();
/** Función principal para enviar resúmenes diarios a todos los usuarios que lo tengan activado **/
function procesarResumenesDiarios()
{
$conn = getConnection();
if (!$conn) {
error_log("❌ No se pudo conectar a la base de datos para procesar resúmenes diarios");
return false;
}
try {
// Obtener usuarios que tienen activado el resumen diario
$fechaAyer = date('Y-m-d', strtotime('-1 day'));
$horaActual = date('H:i');
$diaActual = obtenerDiaSemana();
$sqlUsuarios = "
SELECT DISTINCT
u.id_usuario,
u.nombre,
u.email,
u.notificaciones,
u.notificaciones_extra,
ce.correo as correo_extra,
pnu.resumen_diario,
pnu.resumen_diario_hora,
pnu.resumen_diario_dias
FROM usuarios_sistema u
INNER JOIN preferencias_notificaciones_usuario pnu ON u.id_usuario = pnu.id_usuario
LEFT JOIN correo_extra ce ON u.id_usuario = ce.id_usuario
LEFT JOIN informacion_general ig ON u.id_usuario = ig.id_usuario
WHERE u.notificaciones = 1
AND pnu.resumen_diario = 1
AND u.activo = 1
";
$stmtUsuarios = sqlsrv_query($conn, $sqlUsuarios);
if ($stmtUsuarios === false) {
throw new Exception("Error al consultar usuarios para resumen diario");
}
$usuariosProcesados = 0;
$resumenesEnviados = 0;
while ($usuario = sqlsrv_fetch_array($stmtUsuarios, SQLSRV_FETCH_ASSOC)) {
// Verificar si es hora de enviar el resumen para este usuario
if (!esHoraDeEnvio($usuario, $horaActual, $diaActual)) {
continue;
}
$usuariosProcesados++;
// Desencriptar datos del usuario
$usuario['email'] = decrypt($usuario['email']);
$usuario['nombre'] = decrypt($usuario['nombre']);
// Generar y enviar el resumen
if (enviarResumenDiarioUsuario($usuario, $fechaAyer)) {
$resumenesEnviados++;
}
}
sqlsrv_free_stmt($stmtUsuarios);
sqlsrv_close($conn);
error_log("✅ Proceso de resúmenes diarios completado. Usuarios procesados: $usuariosProcesados, Resúmenes enviados: $resumenesEnviados");
return true;
} catch (Exception $e) {
error_log("❌ Error en proceso de resúmenes diarios: " . $e->getMessage());
if (isset($conn)) {
sqlsrv_close($conn);
}
return false;
}
}
/** Función para verificar si es hora de enviar el resumen para un usuario específico **/
function esHoraDeEnvio($usuario, $horaActual, $diaActual)
{
$horaConfigurada = $usuario['resumen_diario_hora'] ?: '08:00';
$diasConfigurados = $usuario['resumen_diario_dias'] ?: 'L-V';
// Verificar hora (con margen de 5 minutos)
$horaActualMinutos = horaAMinutos($horaActual);
$horaConfiguradaMinutos = horaAMinutos($horaConfigurada);
if (abs($horaActualMinutos - $horaConfiguradaMinutos) > 5) {
return false;
}
// Verificar día
if ($diasConfigurados === 'TODOS') {
return true;
} elseif ($diasConfigurados === 'L-V') {
return in_array($diaActual, ['L', 'M', 'X', 'J', 'V']);
} else {
// Días personalizados
$diasArray = array_map('trim', explode(',', $diasConfigurados));
return in_array($diaActual, $diasArray);
}
}
/** Función auxiliar para convertir hora a minutos **/
function horaAMinutos($hora)
{
list($h, $m) = explode(':', $hora);
return ($h * 60) + $m;
}
/** Función auxiliar para obtener el día de la semana **/
function obtenerDiaSemana()
{
$dias = ['D', 'L', 'M', 'X', 'J', 'V', 'S'];
return $dias[date('w')];
}
/** Función para enviar el resumen diario a un usuario específico **/
function enviarResumenDiarioUsuario($usuario, $fechaFiltro)
{
try {
// Obtener datos del importador
$datosImportador = obtenerDatosImportador($usuario['id_usuario']);
if (!$datosImportador) {
error_log("⚠️ No se encontraron datos del importador para usuario ID: {$usuario['id_usuario']}");
return false;
}
// Obtener operaciones del día anterior
$operaciones = obtenerOperacionesDiarias($usuario['id_usuario'], $fechaFiltro);
// Si no hay operaciones, no enviar resumen (opcional: enviar notificación de "sin actividad")
if (empty($operaciones)) {
error_log(" Sin operaciones para enviar resumen a: {$usuario['email']} en fecha: $fechaFiltro");
return false;
}
// Generar PDF
$rutaPdf = generarPdfResumenDiario($datosImportador, $operaciones, $fechaFiltro);
if (!$rutaPdf) {
error_log("❌ Error al generar PDF para usuario: {$usuario['email']}");
return false;
}
$resultadoEnvio = false;
// Enviar al correo principal
$resultadoPrincipal = enviarEmailResumenDiario(
$usuario['email'],
$usuario['nombre'],
$datosImportador,
$fechaFiltro,
count($operaciones),
$rutaPdf
);
if ($resultadoPrincipal) {
$resultadoEnvio = true;
error_log("✅ Resumen diario enviado a: {$usuario['email']}");
}
// Enviar al correo adicional si está configurado
if ($usuario['notificaciones_extra'] == 1 && !empty($usuario['correo_extra'])) {
$resultadoExtra = enviarEmailResumenDiario(
$usuario['correo_extra'],
$usuario['nombre'],
$datosImportador,
$fechaFiltro,
count($operaciones),
$rutaPdf,
true
);
if ($resultadoExtra) {
$resultadoEnvio = true;
error_log("✅ Resumen diario enviado a correo adicional: {$usuario['correo_extra']}");
}
}
// Limpiar archivo temporal
if (file_exists($rutaPdf)) {
unlink($rutaPdf);
}
return $resultadoEnvio;
} catch (Exception $e) {
error_log("❌ Error al procesar resumen diario para usuario {$usuario['email']}: " . $e->getMessage());
return false;
}
}
/** Función para obtener datos del importador **/
function obtenerDatosImportador($idUsuario)
{
$conn = getConnection();
if (!$conn) {
return null;
}
$sql = "
SELECT
ig.nombre,
ig.rfc,
CONCAT(ig.calle, ' ',
CASE WHEN ig.num_exterior IS NOT NULL THEN CONCAT('#', ig.num_exterior) ELSE '' END,
CASE WHEN ig.num_interior IS NOT NULL AND ig.num_interior != '' THEN CONCAT(' Int. ', ig.num_interior) ELSE '' END,
CASE WHEN ig.colonia IS NOT NULL AND ig.colonia != '' THEN CONCAT(', ', ig.colonia) ELSE '' END,
CASE WHEN ig.ciudad IS NOT NULL AND ig.ciudad != '' THEN CONCAT(', ', ig.ciudad) ELSE '' END,
CASE WHEN ig.estado IS NOT NULL AND ig.estado != '' THEN CONCAT(', ', ig.estado) ELSE '' END,
CASE WHEN ig.codigo_postal IS NOT NULL THEN CONCAT(' C.P. ', ig.codigo_postal) ELSE '' END
) as direccion,
ig.correo as correo_contacto,
ig.telefono,
u.email as email_usuario
FROM informacion_general ig
INNER JOIN usuarios_sistema u ON ig.id_usuario = u.id_usuario
WHERE u.id_usuario = ?
";
$stmt = sqlsrv_prepare($conn, $sql, [$idUsuario]);
if (!$stmt || !sqlsrv_execute($stmt)) {
sqlsrv_close($conn);
return null;
}
$datos = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC);
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
if ($datos) {
// Desencriptar datos sensibles
$datos['nombre_importador'] = decrypt($datos['nombre']); // Mapear nombre a nombre_importador para compatibilidad
$datos['direccion'] = $datos['direccion']; // Ya concatenada en la consulta
$datos['correo_contacto'] = decrypt($datos['correo_contacto']);
$datos['email_usuario'] = decrypt($datos['email_usuario']);
$datos['rfc'] = $datos['rfc']; // RFC generalmente no se encripta
}
return $datos;
}
/** Función para obtener operaciones diarias **/
function obtenerOperacionesDiarias($idUsuario, $fecha)
{
$conn = getConnection();
if (!$conn) {
return [];
}
$sql = "
SELECT
sif.numero_factura,
sif.valor_factura,
sif.tipo_moneda,
sif.fecha_factura,
sif.numero_pedimento,
sif.pais_proveedor,
sif.aduana,
CASE
WHEN sif.status = 0 THEN 'Pendiente'
WHEN sif.status = 1 THEN 'En Proceso'
WHEN sif.status = 2 THEN 'Completado'
WHEN sif.status = 3 THEN 'Cancelado'
ELSE 'Sin Estado'
END as status
FROM solicitud_importacion_factura sif
INNER JOIN usuarios_sistema u ON sif.id_importador = u.id_importador
WHERE u.id_usuario = ?
AND CAST(sif.fecha_factura AS DATE) = ?
ORDER BY sif.fecha_factura DESC
";
$stmt = sqlsrv_prepare($conn, $sql, [$idUsuario, $fecha]);
if (!$stmt || !sqlsrv_execute($stmt)) {
sqlsrv_close($conn);
return [];
}
$operaciones = [];
while ($row = sqlsrv_fetch_array($stmt, SQLSRV_FETCH_ASSOC)) {
// Desencriptar datos si es necesario
$row['numero_factura'] = decrypt($row['numero_factura']);
$row['numero_pedimento'] = $row['numero_pedimento'] ? decrypt($row['numero_pedimento']) : 'N/A';
$operaciones[] = $row;
}
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
return $operaciones;
}
/** Función para generar el PDF del resumen **/
function generarPdfResumenDiario($datosImportador, $operaciones, $fecha)
{
require_once 'vendor/autoload.php'; // Asumiendo que usas TCPDF o similar
try {
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
// Configuración del documento
$pdf->SetCreator('SIIH | AduanaSoft');
$pdf->SetAuthor('Sistema de Importaciones');
$pdf->SetTitle('Resumen Diario de Importaciones - ' . $fecha);
$pdf->SetSubject('Resumen de Operaciones');
// Configurar margenes
$pdf->SetMargins(15, 20, 15);
$pdf->SetHeaderMargin(10);
$pdf->SetFooterMargin(10);
// Agregar página
$pdf->AddPage();
// Encabezado
$html = '<h1 style="text-align: center; color: #2c3e50;">📊 Resumen Diario de Importaciones</h1>';
$html .= '<h2 style="text-align: center; color: #7f8c8d;">Fecha: ' . date('d/m/Y', strtotime($fecha)) . '</h2>';
// Datos del importador
$html .= '<div style="margin: 20px 0; padding: 15px; background-color: #f8f9fa; border: 1px solid #dee2e6;">';
$html .= '<h3 style="color: #495057; margin-top: 0;">🏢 Datos del Importador</h3>';
$html .= '<p><strong>Nombre:</strong> ' . htmlspecialchars($datosImportador['nombre_importador']) . '</p>';
$html .= '<p><strong>RFC:</strong> ' . htmlspecialchars($datosImportador['rfc'] ?? 'N/A') . '</p>';
$html .= '<p><strong>Dirección:</strong> ' . htmlspecialchars($datosImportador['direccion']) . '</p>';
$html .= '<p><strong>Correo:</strong> ' . htmlspecialchars($datosImportador['correo_contacto']) . '</p>';
$html .= '</div>';
// Resumen estadístico
$totalOperaciones = count($operaciones);
$valorTotal = array_sum(array_column($operaciones, 'valor_factura'));
$html .= '<div style="margin: 20px 0; padding: 15px; background-color: #e8f4fd; border: 1px solid #b8daff;">';
$html .= '<h3 style="color: #004085; margin-top: 0;">📈 Resumen Estadístico</h3>';
$html .= '<p><strong>Total de Operaciones:</strong> ' . $totalOperaciones . '</p>';
$html .= '<p><strong>Valor Total:</strong> $' . number_format($valorTotal, 2) . '</p>';
$html .= '</div>';
// Tabla de operaciones
$html .= '<h3 style="color: #495057;">📋 Detalle de Operaciones</h3>';
$html .= '<table border="1" cellpadding="8" cellspacing="0" style="width: 100%; border-collapse: collapse;">';
$html .= '<thead style="background-color: #6c757d; color: white;">';
$html .= '<tr>';
$html .= '<th style="width: 20%;">No. Factura</th>';
$html .= '<th style="width: 20%;">Valor</th>';
$html .= '<th style="width: 15%;">Moneda</th>';
$html .= '<th style="width: 25%;">Estado</th>';
$html .= '<th style="width: 20%;">Fecha</th>';
$html .= '</tr>';
$html .= '</thead>';
$html .= '<tbody>';
foreach ($operaciones as $op) {
$html .= '<tr>';
$html .= '<td>' . htmlspecialchars($op['numero_factura']) . '</td>';
$html .= '<td>$' . number_format($op['valor_factura'], 2) . '</td>';
$html .= '<td>' . htmlspecialchars($op['tipo_moneda']) . '</td>';
$html .= '<td>' . htmlspecialchars($op['status']) . '</td>';
$html .= '<td>' . date('d/m/Y H:i', strtotime($op['fecha_factura'])) . '</td>';
$html .= '</tr>';
}
$html .= '</tbody>';
$html .= '</table>';
// Pie del documento
$html .= '<div style="margin-top: 30px; text-align: center; color: #6c757d; font-size: 10px;">';
$html .= '<p>Este documento fue generado automáticamente el ' . date('d/m/Y H:i:s') . '</p>';
$html .= '<p>SIIH | AduanaSoft - Sistema Integral de Importaciones</p>';
$html .= '</div>';
$pdf->writeHTML($html, true, false, true, false, '');
// Generar nombre único para el archivo
$nombreArchivo = 'resumen_diario_' . $fecha . '_' . uniqid() . '.pdf';
$rutaCompleta = sys_get_temp_dir() . '/' . $nombreArchivo;
$pdf->Output($rutaCompleta, 'F');
return $rutaCompleta;
} catch (Exception $e) {
error_log("❌ Error al generar PDF: " . $e->getMessage());
return false;
}
}
/** Función para enviar el email con el resumen **/
function enviarEmailResumenDiario($emailDestino, $nombreUsuario, $datosImportador, $fecha, $totalOperaciones, $rutaPdf, $esCorreoAdicional = false)
{
try {
$mail = new PHPMailer(true);
// Configuración de servidor SMTP
$mail->isSMTP();
$mail->Host = 'secure.emailsrvr.com';
$mail->SMTPAuth = true;
$mail->Username = 'noreply@aduanasoft.com.mx';
$mail->Password = $_ENV['SMTP_PASS'];
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = 587;
// Configuración del correo
$mail->setFrom('noreply@aduanasoft.com.mx', 'SIIH | AduanaSoft');
$mail->addAddress($emailDestino);
$mail->CharSet = 'UTF-8';
$mail->isHTML(true);
// Adjuntar PDF
$mail->addAttachment($rutaPdf, 'resumen_diario_' . $fecha . '.pdf');
$fechaFormateada = date('d/m/Y', strtotime($fecha));
$asunto = "📊 Resumen Diario de Importaciones - $fechaFormateada$tipoCorreo";
$mail->Subject = $asunto;
$mail->Body = "
<html>
<head>
<meta charset='UTF-8'>
<title>Resumen Diario de Importaciones</title>
</head>
<body style='font-family: Arial, sans-serif; line-height: 1.6; color: #333;'>
<div style='max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd; border-radius: 10px;'>
<div style='text-align: center; margin-bottom: 30px;'>
<h1 style='color: #2c3e50; margin: 0;'>📊 Resumen Diario de Importaciones</h1>
<h2 style='color: #7f8c8d; margin: 10px 0 0 0;'>$fechaFormateada</h2>
</div>
<div style='background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 20px;'>
<h2 style='color: #2c3e50; margin-top: 0;'>Hola " . htmlspecialchars($nombreUsuario) . ",</h2>
<p>Te enviamos el resumen de tus operaciones de importación correspondientes al día <strong>$fechaFormateada</strong>.</p>
</div>
<div style='background-color: #e8f4fd; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #0066cc;'>
<h3 style='color: #004085; margin-top: 0;'>🏢 Importador: " . htmlspecialchars($datosImportador['nombre_importador']) . "</h3>
<p style='margin: 5px 0;'><strong>📧 Correo:</strong> " . htmlspecialchars($datosImportador['correo_contacto']) . "</p>
<p style='margin: 5px 0;'><strong>📍 Dirección:</strong> " . htmlspecialchars($datosImportador['direccion']) . "</p>
</div>
<div style='background-color: #d4edda; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #28a745;'>
<h3 style='color: #155724; margin-top: 0;'>📈 Resumen del Día</h3>
<p style='margin: 5px 0; font-size: 16px;'><strong>Total de Operaciones:</strong> <span style='color: #28a745; font-size: 18px;'>$totalOperaciones</span></p>
<p style='margin: 10px 0 0 0;'>📎 <strong>Adjunto:</strong> Reporte detallado en formato PDF con todas tus operaciones</p>
</div>
<div style='background-color: #fff3cd; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #ffc107;'>
<h3 style='color: #856404; margin-top: 0;'>📋 ¿Qué incluye el reporte?</h3>
<ul style='margin: 10px 0; padding-left: 20px;'>
<li>Número de factura de cada operación</li>
<li>Valor y moneda de las facturas</li>
<li>Estado actual de cada solicitud</li>
<li>Fecha y hora de registro</li>
<li>Resumen estadístico del día</li>
</ul>
</div>
<div style='background-color: #d1ecf1; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #17a2b8;'>
<h3 style='color: #0c5460; margin-top: 0;'>⚙️ Configurar Notificaciones</h3>
<p>Puedes modificar la frecuencia y horario de estos resúmenes desde tu panel de <strong>Preferencias de Notificaciones</strong> en el sistema.</p>
</div>
<div style='text-align: center; margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee;'>
<p style='color: #666; font-size: 12px; margin: 0;'>
Este resumen se genera automáticamente según tus preferencias de notificación.<br>
SIIH | AduanaSoft - Sistema Integral de Importaciones<br>
<em>Generado el " . date('d/m/Y H:i:s') . "</em>
</p>
</div>
</div>
</body>
</html>";
// Enviar el correo
$mail->send();
return true;
} catch (Exception $e) {
error_log("❌ Error al enviar resumen diario a $emailDestino: " . $e->getMessage());
return false;
}
}
?>