From 672b7dd7353423d3ce8caed2652e72b5032f31bd Mon Sep 17 00:00:00 2001 From: Juan Felipe Zapata Moreno Date: Thu, 18 Dec 2025 16:02:03 -0600 Subject: [PATCH] =?UTF-8?q?feat:=20agregar=20soporte=20para=20la=20sustitu?= =?UTF-8?q?ci=C3=B3n=20de=20tags=20en=20la=20b=C3=BAsqueda=20de=20registro?= =?UTF-8?q?s=20de=20veh=C3=ADculos,=20correci=C3=B3n=20excel=20general?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/Repuve/ExcelController.php | 485 ++++++++---------- .../Repuve/InscriptionController.php | 80 ++- 2 files changed, 292 insertions(+), 273 deletions(-) diff --git a/app/Http/Controllers/Repuve/ExcelController.php b/app/Http/Controllers/Repuve/ExcelController.php index 65d2ee1..af4c2b0 100644 --- a/app/Http/Controllers/Repuve/ExcelController.php +++ b/app/Http/Controllers/Repuve/ExcelController.php @@ -170,7 +170,7 @@ public function constanciasSustituidas(Request $request) $sheet->getStyle('B7')->getFont()->setBold(false)->setSize(14); $sheet->mergeCells('C7:I7'); - $sheet->setCellValue('C7', strtoupper($module->name)); + $sheet->setCellValue('C7', mb_strtoupper($module->name, 'UTF-8')); $sheet->getStyle('C7:I7')->applyFromArray($styleBox); $sheet->getStyle('C7')->getFont()->setBold(true)->setSize(20); @@ -436,7 +436,7 @@ public function constanciasCanceladas(Request $request) $sheet->getStyle('A7')->getFont()->setBold(false)->setSize(14); $sheet->mergeCells('D7:M7'); - $sheet->setCellValue('D7', strtoupper($module->name ?? 'TODOS LOS MÓDULOS')); + $sheet->setCellValue('D7', mb_strtoupper($module->name ?? 'TODOS LOS MÓDULOS', 'UTF-8')); $sheet->getStyle('D7:M7')->applyFromArray($styleBox); $sheet->getStyle('D7')->getFont()->setBold(true)->setSize(20); @@ -558,20 +558,21 @@ public function constanciasCanceladas(Request $request) public function excelGeneral(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', + '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; + $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 + // QUERY 1: VehicleTagLogs (Inscripciones, Sustituciones, Cancelaciones de Vehículo) $logs = VehicleTagLog::with([ 'vehicle.records.module', 'tag', @@ -588,7 +589,6 @@ public function excelGeneral(Request $request) ->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); @@ -596,6 +596,7 @@ public function excelGeneral(Request $request) }) ->get(); + //TagCancellationLogs (Cancelaciones de Tags sueltos) $logsT = TagCancellationLog::with([ 'tag.module', 'tag.vehicle', @@ -610,312 +611,262 @@ public function excelGeneral(Request $request) ->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, - ]); + return response()->json(['message' => 'No se encontraron registros en el periodo especificado'], 404); } + // Unificación y Ordenamiento $allLogs = collect(); foreach ($logs as $log) { $log->sort_date = $log->action_type == 'cancelacion' ? $log->cancellation_at : $log->created_at; + $log->source_type = 'vehicle_log'; // Marcador auxiliar $allLogs->push($log); } foreach ($logsT as $log) { $log->sort_date = $log->cancellation_at; + $log->action_type = 'cancelacion'; // Normalizamos el tipo + $log->source_type = 'tag_log'; // Marcador auxiliar $allLogs->push($log); } $allLogs = $allLogs->sortBy('sort_date'); - $data = $this->prepareExcelDataGeneral($allLogs); + // 2. MAPEO DE DATOS (Normalización A-M) + $data = $allLogs->map(function ($log) { + // Determinar Vehículo + $vehicle = $log->vehicle ?? $log->tag->vehicle ?? null; - $fileName = 'Reporte_General_' . $fechaInicio->format('Ymd') . '_' . $fechaFin->format('Ymd') . '.xlsx'; + // Determinar Nombre Módulo + // Intentamos sacar el módulo del registro del vehículo o del tag + $nombreModulo = 'S/N'; + if ($log->source_type == 'vehicle_log') { + // Lógica aproximada: tomar el último módulo registrado o el actual + $nombreModulo = $vehicle->records->last()->module->name ?? 'GENERAL'; + } else { + $nombreModulo = $log->tag->module->name ?? 'GENERAL'; + } + + // Formatear Acción + $accion = ucfirst($log->action_type ?? 'Registro'); + + // Fechas + $fecha = $log->sort_date ? Carbon::parse($log->sort_date)->format('d/m/Y') : ''; + + // Motivo + $motivo = $log->cancellationReason->reason ?? $log->cancellationReason->name ?? ''; + if ($log->action_type == 'sustitucion') $motivo = 'SUSTITUCIÓN'; + if ($log->action_type == 'inscripcion') $motivo = 'INSCRIPCIÓN'; + if ($log->action_type == 'cancelacion' && empty($motivo)) $motivo = 'DAÑADA'; + + return [ + 'modulo' => mb_strtoupper($nombreModulo, 'UTF-8'), + 'accion' => mb_strtoupper($accion, 'UTF-8'), + 'niv' => $vehicle->niv ?? '', + 'nrpv' => $vehicle->nrpv ?? '', + 'marca' => $vehicle->marca ?? '', + 'placa' => $vehicle->placa ?? '', + 'modelo' => $vehicle->modelo ?? '', + 'folio' => $log->tag->folio ?? $log->new_tag_folio ?? '', + 'chip' => $log->tag->rfid ?? '', + 'fecha' => $fecha, + 'motivo' => mb_strtoupper($motivo, 'UTF-8'), + 'observaciones' => $log->observations ?? '' + ]; + }); + + // 3. CONFIGURACIÓN EXCEL Y ESTILOS + $fileName = 'Reporte_General_' . $fechaInicio->format('Ymd') . '.xlsx'; $filePath = storage_path('app/temp/' . $fileName); - - if (!file_exists(storage_path('app/temp'))) { - mkdir(storage_path('app/temp'), 0755, true); - } + if (!file_exists(dirname($filePath))) mkdir(dirname($filePath), 0755, true); $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); + // Fuente Global: Montserrat + $sheet->getParent()->getDefaultStyle()->getFont()->setName('Montserrat'); + $sheet->getParent()->getDefaultStyle()->getFont()->setSize(10); + + // Estilos Comunes + $styleBox = [ + 'borders' => ['allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN, 'color' => ['rgb' => '000000']]], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER] + ]; + $styleLabel = [ + 'font' => ['size' => 14], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_RIGHT, 'vertical' => Alignment::VERTICAL_CENTER] + ]; + $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 --- + $sheet->getRowDimension(2)->setRowHeight(10); + $sheet->getRowDimension(3)->setRowHeight(55); // Logo space + $sheet->getRowDimension(5)->setRowHeight(30); + $sheet->getRowDimension(7)->setRowHeight(38); + $sheet->getRowDimension(9)->setRowHeight(30); + + // LOGO $logoPath = storage_path('app/images/logo-seguridad.png'); if (file_exists($logoPath)) { $drawing = new Drawing(); - $drawing->setName('Logo Seguridad'); + $drawing->setName('Logo'); $drawing->setPath($logoPath); - $drawing->setHeight(100); - $drawing->setCoordinates('B1'); + $drawing->setHeight(55); + $drawing->setCoordinates('B3'); $drawing->setWorksheet($sheet); - $sheet->getRowDimension(1)->setRowHeight(45); } - // Definir estilo de bordes - $borderStyle = [ - 'borders' => [ - 'allBorders' => [ - 'borderStyle' => PhpSpreadsheetBorder::BORDER_THIN, - 'color' => ['rgb' => '000000'], - ], - ], - ]; + // --- BLOQUE DE INFORMACIÓN (Encabezado con Merges A-C) --- - $row = 5; + // Fila 5: ENTIDAD + $sheet->mergeCells('A5:C5'); + $sheet->setCellValue('A5', 'ENTIDAD:'); + $sheet->getStyle('A5')->applyFromArray($styleLabel); - // 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++; + $sheet->mergeCells('D5:M5'); + $sheet->setCellValue('D5', 'TABASCO'); + $sheet->getStyle('D5:M5')->applyFromArray($styleBox); + $sheet->getStyle('D5')->getFont()->setBold(true)->setSize(14); - // 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++; + // Fila 7: MÓDULO + $sheet->mergeCells('A7:C7'); + $sheet->setCellValue('A7', 'MÓDULO:'); + $sheet->getStyle('A7')->applyFromArray($styleLabel); - // 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++; + $sheet->mergeCells('D7:M7'); + $sheet->setCellValue('D7', mb_strtoupper($module->name ?? 'TODOS LOS MÓDULOS', 'UTF-8')); + $sheet->getStyle('D7:M7')->applyFromArray($styleBox); + $sheet->getStyle('D7')->getFont()->setBold(true)->setSize(20); - $row++; + // Fila 9: PERIODO + $sheet->mergeCells('A9:C9'); + $sheet->setCellValue('A9', 'PERIODO A INFORMAR:'); + $sheet->getStyle('A9')->applyFromArray($styleLabel); - // 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++; + Carbon::setLocale('es'); + $periodoTexto = 'del ' . $fechaInicio->format('d') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F'); - $row++; + // Fechas (D-G) + $sheet->mergeCells('D9:G9'); + $sheet->setCellValue('D9', $periodoTexto); + $sheet->getStyle('D9:G9')->applyFromArray($styleBox); + $sheet->getStyle('D9')->getFont()->setSize(14); - // 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', - ]; + // "de" (H) + $sheet->setCellValue('H9', 'de'); + $sheet->getStyle('H9')->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER)->setVertical(Alignment::VERTICAL_CENTER); + $sheet->getStyle('H9')->getFont()->setSize(14); - $col = 'A'; - foreach ($headers as $header) { - $sheet->setCellValue($col . $row, $header); - $col++; - } + // Año (I-J) + $sheet->mergeCells('I9:J9'); + $sheet->setCellValue('I9', $fechaFin->format('Y')); + $sheet->getStyle('I9:J9')->applyFromArray($styleBox); + $sheet->getStyle('I9')->getFont()->setSize(14); - $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'], - ], - ], + + // --- TÍTULO Y BARRA ROJA --- + $rowTitle = 11; + $sheet->mergeCells("A{$rowTitle}:M{$rowTitle}"); + $sheet->setCellValue("A{$rowTitle}", 'REPORTE GENERAL DE CONSTANCIAS'); + $sheet->getStyle("A{$rowTitle}")->applyFromArray([ + 'font' => ['bold' => true, 'color' => ['rgb' => '000000'], 'size' => 14], + 'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER], ]); - $row++; + $sheet->getRowDimension($rowTitle)->setRowHeight(25); - // 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++; + $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 (A-M) --- + $h = 14; + $headers = [ + 'A' => 'No.', + 'B' => 'MÓDULO', + 'C' => "TIPO DE\nACCIÓN", + 'D' => "NIV DEL\nVEHÍCULO", + 'E' => 'NRPV/NCI', + 'F' => "MARCA DEL\nVEHÍCULO", + 'G' => 'PLACA', + 'H' => "AÑO\nMODELO", + 'I' => 'FOLIO', + 'J' => "ID DE LA CONSTANCIA\n(CHIP)", + 'K' => 'FECHA', + 'L' => "MOTIVO/\nRAZÓN", + 'M' => 'OBSERVACIONES' + ]; + + foreach ($headers as $col => $text) { + $sheet->setCellValue("{$col}{$h}", $text); } - // 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 + $sheet->getStyle("A{$h}:M{$h}")->applyFromArray($styleTableHeader); + $sheet->getRowDimension($h)->setRowHeight(35); + + + // --- LLENADO DE DATOS --- + $row = 15; + $i = 1; + + foreach ($data as $item) { + $sheet->setCellValue('A' . $row, $i); + $sheet->setCellValue('B' . $row, $item['modulo']); + $sheet->setCellValue('C' . $row, $item['accion']); + $sheet->setCellValue('D' . $row, $item['niv']); + $sheet->setCellValue('E' . $row, $item['nrpv']); + $sheet->setCellValue('F' . $row, $item['marca']); + $sheet->setCellValue('G' . $row, $item['placa']); + $sheet->setCellValue('H' . $row, $item['modelo']); + $sheet->setCellValue('I' . $row, $item['folio']); + $sheet->setCellValue('J' . $row, $item['chip']); + $sheet->setCellValue('K' . $row, $item['fecha']); + $sheet->setCellValue('L' . $row, $item['motivo']); + $sheet->setCellValue('M' . $row, $item['observaciones']); + + // Estilos de fila + $sheet->getStyle("A{$row}:M{$row}")->applyFromArray([ + 'borders' => ['allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN]], + 'alignment' => ['vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true], + 'font' => ['size' => 9] + ]); + + // Centrados + $sheet->getStyle("A{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); // No + $sheet->getStyle("H{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); // Año + $sheet->getStyle("I{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); // Folio + $sheet->getStyle("K{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER); // Fecha + + $row++; + $i++; + } + + // --- ANCHOS DE COLUMNA --- + $sheet->getColumnDimension('A')->setWidth(5); + $sheet->getColumnDimension('B')->setWidth(25); + $sheet->getColumnDimension('C')->setWidth(18); + $sheet->getColumnDimension('D')->setWidth(22); // NIV + $sheet->getColumnDimension('E')->setWidth(15); + $sheet->getColumnDimension('F')->setWidth(18); + $sheet->getColumnDimension('G')->setWidth(12); + $sheet->getColumnDimension('H')->setWidth(10); + $sheet->getColumnDimension('I')->setWidth(15); + $sheet->getColumnDimension('J')->setWidth(25); // Chip + $sheet->getColumnDimension('K')->setWidth(14); // Fecha + $sheet->getColumnDimension('L')->setWidth(20); // Motivo + $sheet->getColumnDimension('M')->setWidth(30); // Obs - // 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; - } } diff --git a/app/Http/Controllers/Repuve/InscriptionController.php b/app/Http/Controllers/Repuve/InscriptionController.php index dc945fc..6ba29e4 100644 --- a/app/Http/Controllers/Repuve/InscriptionController.php +++ b/app/Http/Controllers/Repuve/InscriptionController.php @@ -397,11 +397,39 @@ public function searchRecord(Request $request) $paginatedRecords->getCollection()->transform(function ($record) { $latestLog = $record->vehicle->vehicleTagLogs->first(); + // Detectar si hubo sustitución y obtener datos del tag anterior + $substitutionData = null; + + // Buscar si existe ALGÚN log de sustitución (no solo el último) + $substitutionLogs = $record->vehicle->vehicleTagLogs + ->where('action_type', 'sustitucion') + ->sortBy('id') + ->take(2); + + if ($substitutionLogs->count() >= 2) { + $oldTagLog = $substitutionLogs->first(); // Tag cancelado + $newTagLog = $substitutionLogs->last(); // Tag nuevo + + $substitutionData = [ + 'old_folio' => $oldTagLog->tag?->folio ?? null, + 'old_tag_number' => $oldTagLog->tag?->tag_number ?? null, + 'new_folio' => $newTagLog->tag?->folio ?? $record->folio, + 'new_tag_number' => $newTagLog->tag?->tag_number ?? null, + ]; + } + return [ 'id' => $record->id, 'folio' => $record->folio, 'created_at' => $record->created_at, + // TIPO DE TRÁMITE + 'action_type' => $latestLog?->action_type ?? 'inscripcion', + 'action_date' => $latestLog?->created_at ?? $record->created_at, + + // SUSTITUCIÓN + 'substitution' => $substitutionData, + // MÓDULO 'module' => $record->module ? [ 'id' => $record->module->id, @@ -415,12 +443,53 @@ public function searchRecord(Request $request) 'email' => $record->user->email, ] : null, - // TIPO DE TRÁMITE - 'action_type' => $latestLog?->action_type ?? 'inscripcion', - 'action_date' => $latestLog?->created_at ?? $record->created_at, + // VEHÍCULO + 'vehicle' => [ + 'id' => $record->vehicle->id, + 'placa' => $record->vehicle->placa, + 'niv' => $record->vehicle->niv, + 'marca' => $record->vehicle->marca, + 'linea' => $record->vehicle->linea, + 'sublinea' => $record->vehicle->sublinea, + 'modelo' => $record->vehicle->modelo, + 'color' => $record->vehicle->color, + 'numero_motor' => $record->vehicle->numero_motor, + 'clase_veh' => $record->vehicle->clase_veh, + 'tipo_servicio' => $record->vehicle->tipo_servicio, + 'rfv' => $record->vehicle->rfv, + 'nrpv' => $record->vehicle->nrpv, + 'reporte_robo' => $record->vehicle->reporte_robo, - // Vehículo - 'vehicle' => $record->vehicle, + // PROPIETARIO + 'owner' => $record->vehicle->owner ? [ + 'id' => $record->vehicle->owner->id, + 'name' => $record->vehicle->owner->name, + 'paternal' => $record->vehicle->owner->paternal, + 'maternal' => $record->vehicle->owner->maternal, + 'full_name' => $record->vehicle->owner->full_name, + 'rfc' => $record->vehicle->owner->rfc, + 'curp' => $record->vehicle->owner->curp, + 'telefono' => $record->vehicle->owner->telefono, + 'address' => $record->vehicle->owner->address, + ] : null, + + // TAG ACTUAL + 'tag' => $record->vehicle->tag ? [ + 'id' => $record->vehicle->tag->id, + 'folio' => $record->vehicle->tag->folio, + 'tag_number' => $record->vehicle->tag->tag_number, + 'status' => $record->vehicle->tag->status ? [ + 'id' => $record->vehicle->tag->status->id, + 'code' => $record->vehicle->tag->status->code, + 'name' => $record->vehicle->tag->status->name, + ] : null, + 'package' => $record->vehicle->tag->package ? [ + 'id' => $record->vehicle->tag->package->id, + 'lot' => $record->vehicle->tag->package->lot, + 'box_number' => $record->vehicle->tag->package->box_number, + ] : null, + ] : null, + ], // Archivos 'files' => $record->files->map(function ($file) { @@ -444,7 +513,6 @@ public function searchRecord(Request $request) 'api_response' => $record->api_response, ]; }); - return ApiResponse::OK->response([ 'records' => $paginatedRecords ]);