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; } }