922 lines
36 KiB
PHP

<?php
namespace App\Http\Controllers\Repuve;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\VehicleTagLog;
use App\Models\Tag;
use App\Models\Module;
use App\Models\TagCancellationLog;
use Carbon\Carbon;
use Notsoweb\ApiResponse\Enums\ApiResponse;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border as PhpSpreadsheetBorder;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
/**
* Descripción
*/
class ExcelController extends Controller
{
public function constanciasSustituidas(Request $request)
{
// VALIDACIÓN Y OBTENCIÓN DE DATOS
$request->validate([
'fecha_inicio' => 'required|date',
'fecha_fin' => 'required|date|after_or_equal:fecha_inicio',
'module_id' => 'required|exists:modules,id',
]);
$fechaInicio = Carbon::parse($request->fecha_inicio)->startOfDay();
$fechaFin = Carbon::parse($request->fecha_fin)->endOfDay();
$moduleId = $request->module_id;
$module = Module::findOrFail($moduleId);
// Consulta de Logs
$logs = VehicleTagLog::with(['vehicle', 'tag', 'cancellationReason'])
->where('action_type', 'sustitucion')
->whereNotNull('cancellation_at')
->whereHas('vehicle.records', function ($query) use ($moduleId) {
$query->where('module_id', $moduleId);
})
->whereBetween('created_at', [$fechaInicio, $fechaFin])
->orderBy('created_at', 'asc')
->get();
if ($logs->isEmpty()) {
return response()->json(['message' => 'No se encontraron registros.'], 404);
}
// PREPARACIÓN DE DATOS
$data = $logs->map(function ($log) {
$newTagLog = VehicleTagLog::with('tag')
->where('vehicle_id', $log->vehicle_id)
->where('action_type', 'sustitucion')
->whereNull('cancellation_at')
->where('id', '!=', $log->id)
->where('created_at', '>=', $log->created_at)
->orderBy('created_at', 'asc')
->first();
return [
'niv' => $log->vehicle->niv ?? '',
'nrpv' => $log->vehicle->nrpv ?? '',
'marca' => $log->vehicle->marca ?? '',
'placa' => $log->vehicle->placa ?? '',
'modelo' => $log->vehicle->modelo ?? '',
'folio_ant' => $log->tag->folio ?? '',
'folio_act' => $newTagLog?->tag?->folio ?? '',
'chip' => $newTagLog?->tag?->rfid ?? $newTagLog?->tag?->tag_number ?? '',
'fecha' => $log->cancellation_at?->format('d/m/Y') ?? $log->created_at->format('d/m/Y'),
'observaciones' => $log->cancellationReason?->name ?? $log->cancellation_observations ?? '',
];
});
// 3. CONFIGURACIÓN INICIAL DEL EXCEL
$fileName = 'Constancias_Sustituidas_' . $fechaInicio->format('Ymd') . '.xlsx';
$tempPath = storage_path('app/temp/');
$filePath = $tempPath . $fileName;
if (!file_exists($tempPath)) {
mkdir($tempPath, 0755, true);
}
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Fuente Global
$sheet->getParent()->getDefaultStyle()->getFont()->setName('Montserrat');
$sheet->getParent()->getDefaultStyle()->getFont()->setSize(10);
// Estilo para cajas de texto
$styleBox = [
'borders' => [
'allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN, 'color' => ['rgb' => '000000']]
],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER]
];
// Estilo para etiquetas
$styleLabel = [
'font' => ['bold' => true, 'size' => 14],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_RIGHT, 'vertical' => Alignment::VERTICAL_CENTER]
];
// Estilo Encabezado Tabla
$styleTableHeader = [
'font' => ['bold' => true, 'size' => 9, 'color' => ['rgb' => '000000']],
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'F2DCDB']],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true],
'borders' => ['allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN]]
];
// ESTRUCTURA DEL DOCUMENTO
// Ajuste de altura para filas superiores
$sheet->getRowDimension(1)->setRowHeight(10);
$sheet->getRowDimension(2)->setRowHeight(55);
$sheet->getRowDimension(5)->setRowHeight(30);
$sheet->getRowDimension(7)->setRowHeight(38);
$sheet->getRowDimension(9)->setRowHeight(30);
// LOGO
$logoPath = storage_path('app/images/logo_excel.png');
if (file_exists($logoPath)) {
$drawing = new Drawing();
$drawing->setName('Logo');
$drawing->setPath($logoPath);
$drawing->setHeight(55);
$drawing->setCoordinates('B2');
$drawing->setWorksheet($sheet);
}
$logoPath = storage_path('app/images/repuve_excel.png');
if (file_exists($logoPath)) {
$drawing = new Drawing();
$drawing->setName('Logo');
$drawing->setPath($logoPath);
$drawing->setHeight(55);
$drawing->setCoordinates('M2');
$drawing->setWorksheet($sheet);
}
// --- BLOQUE DE INFORMACIÓN ---
// Fila 5: ENTIDAD
$sheet->setCellValue('B5', 'ENTIDAD:');
$sheet->getStyle('B5')->applyFromArray($styleLabel);
$sheet->getStyle('B5')->getFont()->setBold(false)->setSize(14);
$sheet->mergeCells('C5:I5');
$sheet->setCellValue('C5', 'TABASCO');
$sheet->getStyle('C5:I5')->applyFromArray($styleBox);
$sheet->getStyle('C5')->getFont()->setBold(true)->setSize(14);
// Fila 7: MÓDULO
$sheet->setCellValue('B7', 'MÓDULO:');
$sheet->getStyle('B7')->applyFromArray($styleLabel);
$sheet->getStyle('B7')->getFont()->setBold(false)->setSize(14);
$sheet->mergeCells('C7:I7');
$sheet->setCellValue('C7', strtoupper($module->name));
$sheet->getStyle('C7:I7')->applyFromArray($styleBox);
$sheet->getStyle('C7')->getFont()->setBold(true)->setSize(20);
// Fila 9: PERIODO A INFORMAR
$sheet->setCellValue('B9', 'PERIODO A INFORMAR:');
$sheet->getStyle('B9')->applyFromArray($styleLabel);
$sheet->getStyle('B9')->getFont()->setBold(false)->setSize(14);
Carbon::setLocale('es');
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
// Fechas
$sheet->mergeCells('C9:F9');
$sheet->setCellValue('C9', $periodoTexto);
$sheet->getStyle('C9:F9')->applyFromArray($styleBox);
$sheet->getStyle('C9')->getFont()->setSize(14);
// Conector "de"
$sheet->setCellValue('G9', 'de');
$sheet->getStyle('G9')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER)->setVertical(Alignment::VERTICAL_CENTER);
$sheet->getStyle('G9')->getFont()->setSize(14);
// Año
$sheet->mergeCells('H9:I9');
$sheet->setCellValue('H9', $fechaFin->format('Y'));
$sheet->getStyle('H9:I9')->applyFromArray($styleBox);
$sheet->getStyle('H9')->getFont()->setSize(14);
// TÍTULO
$rowTitle = 11;
$sheet->mergeCells("A{$rowTitle}:K{$rowTitle}");
$sheet->setCellValue("A{$rowTitle}", 'CONSTANCIAS SUSTITUIDAS');
$sheet->getStyle("A{$rowTitle}")->applyFromArray([
'font' => ['bold' => true, 'color' => ['rgb' => '000000'], 'size' => 14], // Texto Negro
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER],
]);
$sheet->getRowDimension($rowTitle)->setRowHeight(25);
// BARRA ROJA
$rowRedBar = 13;
$sheet->mergeCells("A{$rowRedBar}:K{$rowRedBar}");
$sheet->getStyle("A{$rowRedBar}")->applyFromArray([
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '900000']], // Rojo Oscuro
]);
$sheet->getRowDimension($rowRedBar)->setRowHeight(20);
// --- ENCABEZADOS DE TABLA ---
$h1 = 14;
$h2 = 15;
$headers = [
'A' => 'No.',
'B' => 'NIV DEL VEHÍCULO',
'C' => 'NRPV/NCI',
'D' => 'MARCA DEL VEHÍCULO',
'E' => 'PLACA',
'F' => "AÑO\nMODELO",
'I' => 'ID DE LA CONSTANCIA (CHIP)',
'J' => "FECHA\nREEMPLAZO",
'K' => "OBSERVACIONES (MOTIVO\nDEL REEMPLAZO)"
];
foreach ($headers as $col => $text) {
$sheet->mergeCells("{$col}{$h1}:{$col}{$h2}");
$sheet->setCellValue("{$col}{$h1}", $text);
}
$sheet->mergeCells("G{$h1}:H{$h1}");
$sheet->setCellValue("G{$h1}", 'FOLIO');
$sheet->setCellValue("G{$h2}", 'ANTERIOR');
$sheet->setCellValue("H{$h2}", 'ACTUAL');
$sheet->getStyle("A{$h1}:K{$h2}")->applyFromArray($styleTableHeader);
$sheet->getRowDimension($h1)->setRowHeight(20);
$sheet->getRowDimension($h2)->setRowHeight(25);
// --- LLENADO DE DATOS ---
$row = 16;
$i = 1;
foreach ($data as $item) {
$sheet->setCellValue('A' . $row, $i);
$sheet->setCellValue('B' . $row, $item['niv']);
$sheet->setCellValue('C' . $row, $item['nrpv']);
$sheet->setCellValue('D' . $row, $item['marca']);
$sheet->setCellValue('E' . $row, $item['placa']);
$sheet->setCellValue('F' . $row, $item['modelo']);
$sheet->setCellValue('G' . $row, $item['folio_ant']);
$sheet->setCellValue('H' . $row, $item['folio_act']);
$sheet->setCellValue('I' . $row, $item['chip']);
$sheet->setCellValue('J' . $row, $item['fecha']);
$sheet->setCellValue('K' . $row, $item['observaciones']);
$sheet->getStyle("A{$row}:K{$row}")->applyFromArray([
'borders' => ['allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN]],
'alignment' => ['vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true],
'font' => ['size' => 9]
]);
// Centrados específicos
$sheet->getStyle("A{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$sheet->getStyle("F{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$sheet->getStyle("G{$row}:H{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$sheet->getStyle("J{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
$row++;
$i++;
}
// Anchos de columna
$sheet->getColumnDimension('A')->setWidth(5);
$sheet->getColumnDimension('B')->setWidth(23);
$sheet->getColumnDimension('C')->setWidth(14);
$sheet->getColumnDimension('D')->setWidth(20);
$sheet->getColumnDimension('E')->setWidth(13);
$sheet->getColumnDimension('F')->setWidth(9);
$sheet->getColumnDimension('G')->setWidth(13);
$sheet->getColumnDimension('H')->setWidth(13);
$sheet->getColumnDimension('I')->setWidth(30);
$sheet->getColumnDimension('J')->setWidth(14);
$sheet->getColumnDimension('K')->setWidth(35);
$writer = new Xlsx($spreadsheet);
$writer->save($filePath);
return response()->download($filePath, $fileName)->deleteFileAfterSend(true);
}
public function constanciasCanceladas(Request $request)
{
// 1. VALIDACIÓN Y OBTENCIÓN DE DATOS
$request->validate([
'fecha_inicio' => 'required|date',
'fecha_fin' => 'required|date|after_or_equal:fecha_inicio',
'module_id' => 'nullable|exists:modules,id',
]);
$fechaInicio = Carbon::parse($request->fecha_inicio)->startOfDay();
$fechaFin = Carbon::parse($request->fecha_fin)->endOfDay();
$moduleId = $request->module_id;
// Información del módulo
$module = $moduleId ? Module::find($moduleId) : null;
$logs = VehicleTagLog::with(['vehicle', 'tag', 'cancellationReason'])
->where('action_type', 'cancelacion')
->when($moduleId, function ($query) use ($moduleId) {
$query->whereHas('vehicle.records', function ($q) use ($moduleId) {
$q->where('module_id', $moduleId);
});
})
->whereBetween('cancellation_at', [$fechaInicio, $fechaFin])
->get();
$logsT = TagCancellationLog::with(['tag.vehicle', 'cancellationReason'])
->when($moduleId, function ($query) use ($moduleId) {
$query->whereHas('tag', function ($q) use ($moduleId) {
$q->where('module_id', $moduleId);
});
})
->whereBetween('cancellation_at', [$fechaInicio, $fechaFin])
->get();
if ($logs->isEmpty() && $logsT->isEmpty()) {
return response()->json(['message' => 'No se encontraron constancias canceladas.'], 404);
}
// Unir y ordenar
$allLogs = $logs->concat($logsT)->sortBy('cancellation_at');
// MAPEO DE DATOS
$data = $allLogs->map(function ($log) {
return [
'folio' => $log->tag->folio ?? 'S/F',
'chip' => $log->tag->rfid ?? '',
'fecha' => $log->cancellation_at ? Carbon::parse($log->cancellation_at)->format('d/m/Y') : '',
'motivo' => $log->cancellationReason->name ?? 'DAÑADA',
];
});
// CONFIGURACIÓN EXCEL Y ESTILOS
$fileName = 'Constancias_Canceladas_' . $fechaInicio->format('Ymd') . '.xlsx';
$filePath = storage_path('app/temp/' . $fileName);
if (!file_exists(dirname($filePath))) mkdir(dirname($filePath), 0755, true);
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// Fuente Global
$sheet->getParent()->getDefaultStyle()->getFont()->setName('Montserrat');
$sheet->getParent()->getDefaultStyle()->getFont()->setSize(10);
// Estilo para cajas de texto
$styleBox = [
'borders' => [
'allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN, 'color' => ['rgb' => '000000']]
],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER]
];
// Estilo para etiquetas
$styleLabel = [
'font' => ['bold' => true, 'size' => 14],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_RIGHT, 'vertical' => Alignment::VERTICAL_CENTER]
];
// Estilo Encabezado Tabla
$styleTableHeader = [
'font' => ['bold' => true, 'size' => 9, 'color' => ['rgb' => '000000']],
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => 'F2DCDB']],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true],
'borders' => ['allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN]]
];
// ESTRUCTURA DEL DOCUMENTO
// Ajuste de altura para filas superiores
$sheet->getRowDimension(2)->setRowHeight(10);
$sheet->getRowDimension(3)->setRowHeight(55);
$sheet->getRowDimension(6)->setRowHeight(30);
$sheet->getRowDimension(8)->setRowHeight(38);
$sheet->getRowDimension(10)->setRowHeight(30);
// LOGO
$logoPath = storage_path('app/images/logo_excel.png');
if (file_exists($logoPath)) {
$drawing = new Drawing();
$drawing->setName('Logo');
$drawing->setPath($logoPath);
$drawing->setHeight(55);
$drawing->setCoordinates('B2');
$drawing->setWorksheet($sheet);
}
$logoPath = storage_path('app/images/repuve_excel.png');
if (file_exists($logoPath)) {
$drawing = new Drawing();
$drawing->setName('Logo');
$drawing->setPath($logoPath);
$drawing->setHeight(55);
$drawing->setCoordinates('M2');
$drawing->setWorksheet($sheet);
}
// --- BLOQUE DE INFORMACIÓN ---
// Fila 5: ENTIDAD
$sheet->mergeCells('A5:C5'); // Combinar A-C para la etiqueta
$sheet->setCellValue('A5', 'ENTIDAD:');
$sheet->getStyle('A5')->applyFromArray($styleLabel);
$sheet->getStyle('A5')->getFont()->setBold(false)->setSize(14);
$sheet->mergeCells('D5:M5'); // El valor ocupa el resto
$sheet->setCellValue('D5', 'TABASCO');
$sheet->getStyle('D5:M5')->applyFromArray($styleBox);
$sheet->getStyle('D5')->getFont()->setBold(true)->setSize(14);
// Fila 7: MÓDULO
$sheet->mergeCells('A7:C7');
$sheet->setCellValue('A7', 'MÓDULO:');
$sheet->getStyle('A7')->applyFromArray($styleLabel);
$sheet->getStyle('A7')->getFont()->setBold(false)->setSize(14);
$sheet->mergeCells('D7:M7');
$sheet->setCellValue('D7', strtoupper($module->name ?? 'TODOS LOS MÓDULOS'));
$sheet->getStyle('D7:M7')->applyFromArray($styleBox);
$sheet->getStyle('D7')->getFont()->setBold(true)->setSize(20);
// Fila 9: PERIODO A INFORMAR
$sheet->mergeCells('A9:C9');
$sheet->setCellValue('A9', 'PERIODO A INFORMAR:');
$sheet->getStyle('A9')->applyFromArray($styleLabel);
$sheet->getStyle('A9')->getFont()->setBold(false)->setSize(14);
Carbon::setLocale('es');
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
// Fechas (Movemos a D-G)
$sheet->mergeCells('D9:G9');
$sheet->setCellValue('D9', $periodoTexto);
$sheet->getStyle('D9:G9')->applyFromArray($styleBox);
$sheet->getStyle('D9')->getFont()->setSize(14);
// "de" (Movemos a H)
$sheet->setCellValue('H9', 'de');
$sheet->getStyle('H9')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER)->setVertical(Alignment::VERTICAL_CENTER);
$sheet->getStyle('H9')->getFont()->setSize(14);
// Año (Movemos a I-J)
$sheet->mergeCells('I9:J9');
$sheet->setCellValue('I9', $fechaFin->format('Y'));
$sheet->getStyle('I9:J9')->applyFromArray($styleBox);
$sheet->getStyle('I9')->getFont()->setSize(14);
// --- TÍTULO Y BARRA DECORATIVA ---
$rowTitle = 11;
$sheet->mergeCells("A{$rowTitle}:M{$rowTitle}");
$sheet->setCellValue("A{$rowTitle}", 'CONSTANCIAS DAÑADAS (NO FUERON SUSTITUIDAS)');
$sheet->getStyle("A{$rowTitle}")->applyFromArray([
'font' => ['bold' => true, 'color' => ['rgb' => '000000'], 'size' => 14],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER],
]);
$sheet->getRowDimension($rowTitle)->setRowHeight(25);
// Barra Roja
$rowRedBar = 13;
$sheet->mergeCells("A{$rowRedBar}:M{$rowRedBar}");
$sheet->getStyle("A{$rowRedBar}")->applyFromArray([
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '900000']],
]);
$sheet->getRowDimension($rowRedBar)->setRowHeight(6);
// --- ENCABEZADOS DE TABLA ---
$h1 = 14;
$h2 = 15;
// Definir columnas expandidas
$sheet->mergeCells("A{$h1}:B{$h2}");
$sheet->setCellValue("A{$h1}", 'No.');
$sheet->mergeCells("C{$h1}:F{$h2}");
$sheet->setCellValue("C{$h1}", "FOLIO DE LA\nCONSTANCIA");
$sheet->mergeCells("G{$h1}:I{$h2}");
$sheet->setCellValue("G{$h1}", "FECHA DE\nCANCELACIÓN");
$sheet->mergeCells("J{$h1}:M{$h2}");
$sheet->setCellValue("J{$h1}", "MOTIVO DE\nCANCELACIÓN");
// Aplicar estilo beige
$sheet->getStyle("A{$h1}:M{$h2}")->applyFromArray($styleTableHeader);
$sheet->getRowDimension($h1)->setRowHeight(20);
$sheet->getRowDimension($h2)->setRowHeight(25);
// --- LLENADO DE DATOS ---
$row = 16;
$i = 1;
foreach ($data as $item) {
$sheet->mergeCells("A{$row}:B{$row}");
$sheet->setCellValue("A{$row}", $i);
$sheet->mergeCells("C{$row}:F{$row}");
$sheet->setCellValue("C{$row}", $item['folio']);
$sheet->mergeCells("G{$row}:I{$row}");
$sheet->setCellValue("G{$row}", $item['fecha']);
$sheet->mergeCells("J{$row}:M{$row}");
$sheet->setCellValue("J{$row}", $item['motivo']);
// Estilos de celda
$sheet->getStyle("A{$row}:M{$row}")->applyFromArray([
'borders' => ['allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN]],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true],
'font' => ['size' => 9]
]);
$row++;
$i++;
}
// --- ANCHOS DE COLUMNA ---
$sheet->getColumnDimension('A')->setWidth(5);
$sheet->getColumnDimension('B')->setWidth(5);
$sheet->getColumnDimension('C')->setWidth(22);
$sheet->getColumnDimension('D')->setWidth(10);
$sheet->getColumnDimension('E')->setWidth(10);
$sheet->getColumnDimension('F')->setWidth(10);
$sheet->getColumnDimension('G')->setWidth(10);
$sheet->getColumnDimension('H')->setWidth(10);
$sheet->getColumnDimension('I')->setWidth(10);
$sheet->getColumnDimension('J')->setWidth(15);
$sheet->getColumnDimension('K')->setWidth(15);
$sheet->getColumnDimension('L')->setWidth(15);
$sheet->getColumnDimension('M')->setWidth(15);
// Guardar
$writer = new Xlsx($spreadsheet);
$writer->save($filePath);
return response()->download($filePath, $fileName)->deleteFileAfterSend(true);
}
public function excelGeneral(Request $request)
{
$request->validate([
'fecha_inicio' => 'required|date',
'fecha_fin' => 'required|date|after_or_equal:fecha_inicio',
'module_id' => 'nullable|exists:modules,id',
]);
$fechaInicio = Carbon::parse($request->fecha_inicio)->startOfDay();
$fechaFin = Carbon::parse($request->fecha_fin)->endOfDay();
$moduleId = $request->module_id;
// Obtener información del módulo
$module = $moduleId ? Module::find($moduleId) : null;
// Obtener logs de cancelación en el rango de fechas
$logs = VehicleTagLog::with([
'vehicle.records.module',
'tag',
'cancellationReason'
])
->whereIn('action_type', ['inscripcion', 'sustitucion', 'cancelacion'])
->where(function ($query) use ($fechaInicio, $fechaFin) {
$query->where(function ($q) use ($fechaInicio, $fechaFin) {
$q->whereIn('action_type', ['inscripcion', 'sustitucion'])
->whereBetween('created_at', [$fechaInicio, $fechaFin]);
})
->orWhere(function ($q) use ($fechaInicio, $fechaFin) {
$q->where('action_type', 'cancelacion')
->whereBetween('cancellation_at', [$fechaInicio, $fechaFin]);
});
})
// DESPUÉS: Aplicar filtro de módulo
->when($moduleId, function ($query) use ($moduleId) {
$query->whereHas('vehicle.records', function ($q) use ($moduleId) {
$q->where('module_id', $moduleId);
});
})
->get();
$logsT = TagCancellationLog::with([
'tag.module',
'tag.vehicle',
'cancellationReason'
])
->whereBetween('cancellation_at', [$fechaInicio, $fechaFin])
->when($moduleId, function ($query) use ($moduleId) {
$query->whereHas('tag', function ($q) use ($moduleId) {
$q->where('module_id', $moduleId);
});
})
->get();
if ($logs->isEmpty() && $logsT->isEmpty()) {
return ApiResponse::NOT_FOUND->response([
'message' => 'No se encontraron registros en el periodo especificado',
'fecha_inicio' => $fechaInicio->format('Y-m-d'),
'fecha_fin' => $fechaFin->format('Y-m-d'),
'module_id' => $moduleId,
]);
}
$allLogs = collect();
foreach ($logs as $log) {
$log->sort_date = $log->action_type == 'cancelacion' ? $log->cancellation_at : $log->created_at;
$allLogs->push($log);
}
foreach ($logsT as $log) {
$log->sort_date = $log->cancellation_at;
$allLogs->push($log);
}
$allLogs = $allLogs->sortBy('sort_date');
$data = $this->prepareExcelDataGeneral($allLogs);
$fileName = 'Reporte_General_' . $fechaInicio->format('Ymd') . '_' . $fechaFin->format('Ymd') . '.xlsx';
$filePath = storage_path('app/temp/' . $fileName);
if (!file_exists(storage_path('app/temp'))) {
mkdir(storage_path('app/temp'), 0755, true);
}
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$logoPath = storage_path('app/images/logo-seguridad.png');
if (file_exists($logoPath)) {
$drawing = new Drawing();
$drawing->setName('Logo Seguridad');
$drawing->setPath($logoPath);
$drawing->setHeight(100);
$drawing->setCoordinates('B1');
$drawing->setWorksheet($sheet);
$sheet->getRowDimension(1)->setRowHeight(45);
}
// Definir estilo de bordes
$borderStyle = [
'borders' => [
'allBorders' => [
'borderStyle' => PhpSpreadsheetBorder::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
];
$row = 5;
// ENTIDAD
$sheet->setCellValue('B' . $row, 'ENTIDAD:');
$sheet->mergeCells('C' . $row . ':M' . $row);
$sheet->setCellValue('C' . $row, 'TABASCO');
$sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(11);
$sheet->getStyle('B' . $row . ':M' . $row)->applyFromArray($borderStyle);
$sheet->getStyle('B' . $row . ':M' . $row)->getAlignment()->setWrapText(true);
$row++;
// MÓDULO
$sheet->setCellValue('B' . $row, 'MÓDULO:');
$sheet->mergeCells('C' . $row . ':M' . $row);
$sheet->setCellValue('C' . $row, $module?->name ?? 'TODOS LOS MÓDULOS');
$sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(11);
$sheet->getStyle('B' . $row . ':M' . $row)->applyFromArray($borderStyle);
$sheet->getStyle('B' . $row . ':M' . $row)->getAlignment()->setWrapText(true);
$row++;
// PERIODO
$sheet->setCellValue('B' . $row, 'PERIODO A INFORMAR:');
$sheet->mergeCells('C' . $row . ':M' . $row);
$sheet->setCellValue('C' . $row, $fechaInicio->format('d/m/Y') . ' al ' . $fechaFin->format('d/m/Y'));
$sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(11);
$sheet->getStyle('B' . $row . ':M' . $row)->applyFromArray($borderStyle);
$sheet->getStyle('B' . $row . ':M' . $row)->getAlignment()->setWrapText(true);
$row++;
$row++;
// Título
$sheet->mergeCells('B' . $row . ':M' . $row);
$sheet->setCellValue('B' . $row, 'REPORTE GENERAL DE CONSTANCIAS');
$sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(12);
$sheet->getStyle('B' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER)->setWrapText(true);
$sheet->getStyle('B' . $row . ':M' . $row)->applyFromArray($borderStyle);
$row++;
$row++;
// Encabezados (12 columnas: A-L)
$headers = [
'No.',
'MÓDULO',
'TIPO DE ACCIÓN',
'NIV DEL VEHÍCULO',
'NRPV/NCI',
'MARCA DEL VEHÍCULO',
'PLACA',
'AÑO MODELO',
'FOLIO',
'ID DE LA CONSTANCIA (CHIP)',
'FECHA',
'MOTIVO/RAZÓN',
'OBSERVACIONES',
];
$col = 'A';
foreach ($headers as $header) {
$sheet->setCellValue($col . $row, $header);
$col++;
}
$sheet->getStyle('A' . $row . ':M' . $row)->applyFromArray([
'font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF'], 'size' => 10],
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '8B0000']],
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'wrapText' => true],
'borders' => [
'allBorders' => [
'borderStyle' => PhpSpreadsheetBorder::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
]);
$row++;
// Agregar datos
foreach ($data as $rowData) {
$col = 'A';
foreach ($rowData as $value) {
$sheet->setCellValue($col . $row, $value);
$col++;
}
$sheet->getStyle('A' . $row . ':M' . $row)->applyFromArray([
'font' => ['size' => 10],
'alignment' => ['wrapText' => true],
'borders' => [
'allBorders' => [
'borderStyle' => PhpSpreadsheetBorder::BORDER_THIN,
'color' => ['rgb' => '000000'],
],
],
]);
$row++;
}
// Ajustar anchos de columnas
$sheet->getColumnDimension('A')->setWidth(6); // No.
$sheet->getColumnDimension('B')->setWidth(25); // MÓDULO
$sheet->getColumnDimension('C')->setWidth(18); // TIPO DE ACCIÓN
$sheet->getColumnDimension('D')->setWidth(25); // NIV
$sheet->getColumnDimension('E')->setWidth(15); // NRPV/NCI
$sheet->getColumnDimension('F')->setWidth(20); // MARCA
$sheet->getColumnDimension('G')->setWidth(12); // PLACA
$sheet->getColumnDimension('H')->setWidth(12); // AÑO MODELO
$sheet->getColumnDimension('I')->setWidth(25); // FOLIO
$sheet->getColumnDimension('J')->setWidth(30); // CHIP
$sheet->getColumnDimension('K')->setWidth(20); // FECHA
$sheet->getColumnDimension('L')->setWidth(35); // MOTIVO/RAZÓN
$sheet->getColumnDimension('M')->setWidth(50); // OBSERVACIONES
// Guardar archivo
$writer = new Xlsx($spreadsheet);
$writer->save($filePath);
// Descargar archivo y eliminarlo después
return response()->download($filePath, $fileName)->deleteFileAfterSend(true);
}
private function prepareExcelDataGeneral($logs)
{
$data = [];
$no = 1;
foreach ($logs as $log) {
$isVehicleTagLog = isset($log->action_type);
if ($isVehicleTagLog) {
$vehicle = $log->vehicle;
$tag = $log->tag;
$actionType = strtoupper($log->action_type);
$moduleName = $vehicle->records->first()?->module?->name ?? 'SIN MODULO ASIGNADO';
$fecha = $log->action_type == 'cancelacion'
? $log->cancellation_at?->format('d/m/Y')
: $log->created_at->format('d/m/Y');
$motivo = match ($log->action_type) {
'inscripcion' => 'INSCRIPCIÓN',
'sustitucion' => 'SUSTITUCIÓN DE CONSTANCIA',
'cancelacion' => $log->cancellationReason?->name ?? 'N/A',
default => 'N/A',
};
$data[] = [
$no,
$moduleName,
$actionType,
$vehicle->niv ?? 'N/A',
$vehicle->nrpv ?? 'N/A',
strtoupper($vehicle->marca ?? 'N/A'),
strtoupper($vehicle->placa ?? 'N/A'),
$vehicle->modelo ?? 'N/A',
$tag->folio ?? 'N/A',
$tag->tag_number ?? 'N/A',
$fecha,
$motivo,
$log->cancellation_observations ?? 'N/A',
];
} else {
$vehicle = $log->tag->vehicle;
$tag = $log->tag;
$moduleName = $tag->module?->name ?? 'SIN MODULO ASIGNADO';
$data[] = [
$no,
$moduleName,
'CANCELACION',
$vehicle->niv ?? 'N/A',
$vehicle->nrpv ?? 'N/A',
strtoupper($vehicle->marca ?? 'N/A'),
strtoupper($vehicle->placa ?? 'N/A'),
$vehicle->modelo ?? 'N/A',
$tag->folio ?? 'N/A',
$tag->tag_number ?? 'N/A',
$log->cancellation_at ? $log->cancellation_at->format('d/m/Y') : 'N/A',
$log->cancellationReason?->name ?? 'CANCELACIÓN',
$log->cancellation_observations ?? 'N/A',
];
}
$no++;
}
return $data;
}
private function prepareExcelData($logs)
{
$data = [];
$no = 1;
foreach ($logs as $log) {
$vehicle = $log->vehicle;
$newTag = $log->tag;
// Extraer el folio anterior de las observaciones
$folioAnterior = 'N/A';
if ($log->cancellation_observations) {
if (preg_match('/Folio:\s*([^)]+)/', $log->cancellation_observations, $matches)) {
$folioAnterior = trim($matches[1]);
}
}
$data[] = [
$no,
$vehicle->niv ?? 'N/A',
$vehicle->nrpv ?? 'N/A',
strtoupper($vehicle->marca ?? 'N/A'),
strtoupper($vehicle->placa ?? 'N/A'),
$vehicle->modelo ?? 'N/A',
$folioAnterior,
$newTag->folio ?? 'N/A',
$newTag->tag_number ?? 'N/A',
$log->created_at->format('d/m/Y'),
$log->cancellation_observations ?? 'CONSTANCIA SUSTITUIDA',
];
$no++;
}
return $data;
}
private function prepareExcelDataCanceladas($logs)
{
$data = [];
$no = 1;
foreach ($logs as $log) {
$vehicle = $log->vehicle ?? $log->tag->vehicle;
$tag = $log->tag;
$data[] = [
$no,
$vehicle->niv ?? 'N/A',
$vehicle->nrpv ?? 'N/A',
strtoupper($vehicle->marca ?? 'N/A'),
strtoupper($vehicle->placa ?? 'N/A'),
$vehicle->modelo ?? 'N/A',
$tag->folio ?? 'N/A',
$tag->tag_number ?? 'N/A',
$log->cancellation_at ? $log->cancellation_at->format('d/m/Y') : 'N/A',
$log->cancellationReason?->name ?? 'N/A',
$log->cancellation_observations ?? 'CONSTANCIA CANCELADA',
];
$no++;
}
return $data;
}
}