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; // Obtener información del módulo $module = Module::findOrFail($moduleId); // Obtener logs de sustitución en el rango de fechas $logs = VehicleTagLog::with([ 'vehicle', 'tag', 'cancellationReason' ]) ->where('action_type', 'sustitucion') ->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 ApiResponse::NOT_FOUND->response([ 'message' => 'No se encontraron constancias sustituidas en el periodo especificado', 'fecha_inicio' => $fechaInicio->format('Y-m-d'), 'fecha_fin' => $fechaFin->format('Y-m-d'), 'module_id' => $moduleId, ]); } // Preparar datos para el Excel $data = $this->prepareExcelData($logs); // Generar archivo Excel $fileName = 'Constancias_Sustituidas_' . $fechaInicio->format('Ymd') . '_' . $fechaFin->format('Ymd') . '.xlsx'; $filePath = storage_path('app/temp/' . $fileName); // Crear directorio temporal si no existe if (!file_exists(storage_path('app/temp'))) { mkdir(storage_path('app/temp'), 0755, true); } // Crear Excel $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); // Agregar logo $logoPath = storage_path('app/images/logo-seguridad.png'); if (file_exists($logoPath)) { $drawing = new Drawing(); $drawing->setName('Logo Seguridad'); $drawing->setDescription('Logo Seguridad Pública'); $drawing->setPath($logoPath); $drawing->setHeight(100); // Altura del logo en pixeles $drawing->setCoordinates('B1'); // Posición del logo $drawing->setWorksheet($sheet); // Ajustar altura de las filas del logo $sheet->getRowDimension(1)->setRowHeight(45); } // Definir estilo de bordes $borderStyle = [ 'borders' => [ 'allBorders' => [ 'borderStyle' => PhpSpreadsheetBorder::BORDER_THIN, 'color' => ['rgb' => '000000'], ], ], ]; // Empezar después del logo $row = 5; // Fila 1: ENTIDAD (empieza en B) $sheet->setCellValue('B' . $row, 'ENTIDAD:'); $sheet->mergeCells('C' . $row . ':K' . $row); $sheet->setCellValue('C' . $row, 'TABASCO'); $sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(11); $sheet->getStyle('B' . $row . ':K' . $row)->applyFromArray($borderStyle); $sheet->getStyle('B' . $row . ':K' . $row)->getAlignment()->setWrapText(true); $row++; // Fila 2: MÓDULO (empieza en B) $sheet->setCellValue('B' . $row, 'MÓDULO:'); $sheet->mergeCells('C' . $row . ':K' . $row); $sheet->setCellValue('C' . $row, $module->name ?? 'MÓDULO 1. BASE 4'); $sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(11); $sheet->getStyle('B' . $row . ':K' . $row)->applyFromArray($borderStyle); $sheet->getStyle('B' . $row . ':K' . $row)->getAlignment()->setWrapText(true); $row++; // Fila 3: PERIODO A INFORMAR (empieza en B, fecha completa con año) $sheet->setCellValue('B' . $row, 'PERIODO A INFORMAR:'); $sheet->mergeCells('C' . $row . ':K' . $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 . ':K' . $row)->applyFromArray($borderStyle); $sheet->getStyle('B' . $row . ':K' . $row)->getAlignment()->setWrapText(true); $row++; // Fila vacía $row++; // Título: CONSTANCIAS SUSTITUIDAS (combinada B:K) $sheet->mergeCells('B' . $row . ':K' . $row); $sheet->setCellValue('B' . $row, 'CONSTANCIAS SUSTITUIDAS'); $sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(12); $sheet->getStyle('B' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER)->setWrapText(true); $sheet->getStyle('B' . $row . ':K' . $row)->applyFromArray($borderStyle); $row++; // Fila vacía $row++; // Encabezados de la tabla $headers = [ 'No.', 'NIV DEL VEHÍCULO', 'NRPV/NCI', 'MARCA DEL VEHÍCULO', 'PLACA', 'AÑO MODELO', 'FOLIO ANTERIOR', 'FOLIO ACTUAL', 'ID DE LA CONSTANCIA (CHIP)', 'FECHA DE REEMPLAZO', 'OBSERVACIONES (MOTIVO DEL REEMPLAZO)', ]; $col = 'A'; foreach ($headers as $header) { $sheet->setCellValue($col . $row, $header); $col++; } $sheet->getStyle('A' . $row . ':K' . $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 . ':K' . $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); $sheet->getColumnDimension('B')->setWidth(25); $sheet->getColumnDimension('C')->setWidth(15); $sheet->getColumnDimension('D')->setWidth(20); $sheet->getColumnDimension('E')->setWidth(12); $sheet->getColumnDimension('F')->setWidth(15); $sheet->getColumnDimension('G')->setWidth(18); $sheet->getColumnDimension('H')->setWidth(18); $sheet->getColumnDimension('I')->setWidth(30); $sheet->getColumnDimension('J')->setWidth(20); $sheet->getColumnDimension('K')->setWidth(50); // Guardar archivo $writer = new Xlsx($spreadsheet); $writer->save($filePath); // Descargar archivo y eliminarlo después return response()->download($filePath, $fileName)->deleteFileAfterSend(true); } public function constanciasCanceladas(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', '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]) ->orderBy('cancellation_at', 'asc') ->get(); $logsT = TagCancellationLog::with([ 'tag', 'cancellationReason' ])->when($moduleId, function ($query) use ($moduleId) { $query->whereHas('tag', function ($q) use ($moduleId) { $q->where('module_id', $moduleId); }); }) ->whereBetween('cancellation_at', [$fechaInicio, $fechaFin]) ->orderBy('cancellation_at', 'asc') ->get(); if ($logs->isEmpty() && $logsT->isEmpty()) { return ApiResponse::NOT_FOUND->response([ 'message' => 'No se encontraron constancias canceladas en el periodo especificado', 'fecha_inicio' => $fechaInicio->format('Y-m-d'), 'fecha_fin' => $fechaFin->format('Y-m-d'), 'module_id' => $moduleId, ]); } // Preparar datos para el Excel $allLogs = $logs->concat($logsT)->sortBy('cancellation_at'); $data = $this->prepareExcelDataCanceladas($allLogs); // Generar archivo Excel $fileName = 'Constancias_Canceladas_' . $fechaInicio->format('Ymd') . '_' . $fechaFin->format('Ymd') . '.xlsx'; $filePath = storage_path('app/temp/' . $fileName); // Crear directorio temporal si no existe if (!file_exists(storage_path('app/temp'))) { mkdir(storage_path('app/temp'), 0755, true); } // Crear Excel $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); // Agregar logo $logoPath = storage_path('app/images/logo-seguridad.png'); if (file_exists($logoPath)) { $drawing = new Drawing(); $drawing->setName('Logo Seguridad'); $drawing->setDescription('Logo Seguridad Pública'); $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 . ':K' . $row); $sheet->setCellValue('C' . $row, 'TABASCO'); $sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(11); $sheet->getStyle('B' . $row . ':K' . $row)->applyFromArray($borderStyle); $sheet->getStyle('B' . $row . ':K' . $row)->getAlignment()->setWrapText(true); $row++; // MÓDULO $sheet->setCellValue('B' . $row, 'MÓDULO:'); $sheet->mergeCells('C' . $row . ':K' . $row); $sheet->setCellValue('C' . $row, $module?->name ?? 'S/N MODULO'); $sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(11); $sheet->getStyle('B' . $row . ':K' . $row)->applyFromArray($borderStyle); $sheet->getStyle('B' . $row . ':K' . $row)->getAlignment()->setWrapText(true); $row++; // PERIODO $sheet->setCellValue('B' . $row, 'PERIODO A INFORMAR:'); $sheet->mergeCells('C' . $row . ':K' . $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 . ':K' . $row)->applyFromArray($borderStyle); $sheet->getStyle('B' . $row . ':K' . $row)->getAlignment()->setWrapText(true); $row++; $row++; // Título $sheet->mergeCells('B' . $row . ':K' . $row); $sheet->setCellValue('B' . $row, 'CONSTANCIAS CANCELADAS'); $sheet->getStyle('B' . $row)->getFont()->setBold(true)->setSize(12); $sheet->getStyle('B' . $row)->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER)->setWrapText(true); $sheet->getStyle('B' . $row . ':K' . $row)->applyFromArray($borderStyle); $row++; $row++; // Encabezados $headers = [ 'No.', 'NIV DEL VEHÍCULO', 'NRPV/NCI', 'MARCA DEL VEHÍCULO', 'PLACA', 'AÑO MODELO', 'FOLIO DE LA CONSTANCIA', 'ID DE LA CONSTANCIA (CHIP)', 'FECHA DE CANCELACIÓN', 'MOTIVO DE CANCELACIÓN', 'OBSERVACIONES', ]; $col = 'A'; foreach ($headers as $header) { $sheet->setCellValue($col . $row, $header); $col++; } $sheet->getStyle('A' . $row . ':K' . $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 . ':K' . $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); $sheet->getColumnDimension('B')->setWidth(25); $sheet->getColumnDimension('C')->setWidth(15); $sheet->getColumnDimension('D')->setWidth(20); $sheet->getColumnDimension('E')->setWidth(12); $sheet->getColumnDimension('F')->setWidth(15); $sheet->getColumnDimension('G')->setWidth(25); $sheet->getColumnDimension('H')->setWidth(30); $sheet->getColumnDimension('I')->setWidth(20); $sheet->getColumnDimension('J')->setWidth(35); $sheet->getColumnDimension('K')->setWidth(50); // Guardar archivo $writer = new Xlsx($spreadsheet); $writer->save($filePath); // Descargar archivo y eliminarlo después 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']) ->when($moduleId, function ($query) use ($moduleId) { $query->whereHas('vehicle.records', function ($q) use ($moduleId) { $q->where('module_id', $moduleId); }); }) ->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]); }); })->get(); $logsT = TagCancellationLog::with([ 'tag.module', '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 ApiResponse::NOT_FOUND->response([ 'message' => 'No se encontraron constancias canceladas 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; } }