1500 lines
64 KiB
PHP
1500 lines
64 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\Module;
|
|
use App\Models\Record;
|
|
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;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Routing\Controllers\HasMiddleware;
|
|
|
|
|
|
/**
|
|
* Descripción
|
|
*/
|
|
class ExcelController extends Controller implements HasMiddleware
|
|
{
|
|
/**
|
|
* Middleware
|
|
*/
|
|
public static function middleware(): array
|
|
{
|
|
return [
|
|
self::can('reports.vehicle_updates.index', ['vehicleActualizaciones']),
|
|
self::can('reports.substitutions.index', ['constanciasSustituidas']),
|
|
self::can('reports.cancellations.index', ['constanciasCanceladas']),
|
|
self::can('reports.general.index', ['excelGeneral']),
|
|
self::can('reports.search_records.index', ['exportSearchRecords']),
|
|
];
|
|
}
|
|
|
|
|
|
public function vehicleActualizaciones(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' => 'nullable|exists:modules,id',
|
|
]);
|
|
|
|
$fechaInicio = Carbon::parse($request->fecha_inicio)->startOfDay();
|
|
$fechaFin = Carbon::parse($request->fecha_fin)->endOfDay();
|
|
$moduleId = $request->module_id;
|
|
|
|
$module = $moduleId ? Module::find($moduleId) : null;
|
|
|
|
// Consulta de Logs de actualizaciones (último por vehículo)
|
|
$logs = VehicleTagLog::with(['vehicle', 'tag', 'performedBy.module'])
|
|
->where('action_type', 'actualizacion')
|
|
->when($moduleId, function ($query) use ($moduleId) {
|
|
$query->whereHas('performedBy', function ($q) use ($moduleId) {
|
|
$q->where('module_id', $moduleId);
|
|
});
|
|
})
|
|
->whereBetween('created_at', [$fechaInicio, $fechaFin])
|
|
->orderBy('created_at', 'desc')
|
|
->get()
|
|
->unique('vehicle_id')
|
|
->sortBy('created_at')
|
|
->values();
|
|
|
|
if ($logs->isEmpty()) {
|
|
return response()->json(['message' => 'No se encontraron registros de actualizaciones.'], 404);
|
|
}
|
|
|
|
// PREPARACIÓN DE DATOS
|
|
$data = $logs->map(function ($log) {
|
|
return [
|
|
'niv' => $log->vehicle->niv ?? '',
|
|
'marca' => $log->vehicle->marca ?? '',
|
|
'placa' => $log->vehicle->placa ?? '',
|
|
'modelo' => $log->vehicle->modelo ?? '',
|
|
'folio' => $log->tag->folio ?? '',
|
|
'chip' => substr($log->tag->rfid ?? $log->tag->tag_number ?? '', 0, 24),
|
|
'fecha' => $log->created_at->format('d/m/Y'),
|
|
];
|
|
});
|
|
|
|
// CONFIGURACIÓN INICIAL DEL EXCEL
|
|
$fileName = 'Constancias_Actualizadas_' . $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
|
|
$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 izquierdo
|
|
$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);
|
|
}
|
|
|
|
// LOGO derecho
|
|
$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('J2');
|
|
$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', mb_strtoupper($module->name ?? 'TODOS LOS MÓDULOS', 'UTF-8'));
|
|
$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');
|
|
if ($fechaInicio->format('m/Y') === $fechaFin->format('m/Y')) {
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
} elseif ($fechaInicio->format('Y') === $fechaFin->format('Y')) {
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' de ' . $fechaInicio->translatedFormat('F') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
} else {
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' de ' . $fechaInicio->translatedFormat('F') . ' ' . $fechaInicio->format('Y') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
}
|
|
|
|
// 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', $anioTexto);
|
|
$sheet->getStyle('H9:I9')->applyFromArray($styleBox);
|
|
$sheet->getStyle('H9')->getFont()->setSize(14);
|
|
|
|
// TÍTULO
|
|
$rowTitle = 11;
|
|
$sheet->mergeCells("A{$rowTitle}:I{$rowTitle}");
|
|
$sheet->setCellValue("A{$rowTitle}", 'CONSTANCIAS ACTUALIZADAS');
|
|
$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 = 12;
|
|
$sheet->mergeCells("A{$rowRedBar}:H{$rowRedBar}");
|
|
$sheet->getStyle("A{$rowRedBar}")->applyFromArray([
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '900000']],
|
|
]);
|
|
$sheet->getRowDimension($rowRedBar)->setRowHeight(6);
|
|
|
|
// --- ENCABEZADOS DE TABLA ---
|
|
$h1 = 13;
|
|
$h2 = 14;
|
|
|
|
$headers = [
|
|
'A' => 'No.',
|
|
'B' => 'NIV DEL VEHÍCULO',
|
|
'C' => 'MARCA DEL VEHÍCULO',
|
|
'D' => 'PLACA',
|
|
'E' => "AÑO\nMODELO",
|
|
'F' => "FOLIO\nCONSTANCIA",
|
|
'G' => 'ID DE LA CONSTANCIA (CHIP)',
|
|
'H' => "FECHA\nACTUALIZACIÓN",
|
|
];
|
|
|
|
foreach ($headers as $col => $text) {
|
|
$sheet->mergeCells("{$col}{$h1}:{$col}{$h2}");
|
|
$sheet->setCellValue("{$col}{$h1}", $text);
|
|
}
|
|
|
|
$sheet->getStyle("A{$h1}:H{$h2}")->applyFromArray($styleTableHeader);
|
|
$sheet->getRowDimension($h1)->setRowHeight(20);
|
|
$sheet->getRowDimension($h2)->setRowHeight(25);
|
|
|
|
// --- LLENADO DE DATOS ---
|
|
$row = 15;
|
|
$i = 1;
|
|
|
|
foreach ($data as $item) {
|
|
$sheet->setCellValue('A' . $row, $i);
|
|
$sheet->setCellValue('B' . $row, $item['niv']);
|
|
$sheet->setCellValue('C' . $row, $item['marca']);
|
|
$sheet->setCellValue('D' . $row, $item['placa']);
|
|
$sheet->setCellValue('E' . $row, $item['modelo']);
|
|
$sheet->setCellValue('F' . $row, $item['folio']);
|
|
$sheet->setCellValue('G' . $row, $item['chip']);
|
|
$sheet->setCellValue('H' . $row, $item['fecha']);
|
|
|
|
$sheet->getStyle("A{$row}:H{$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("E{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
|
$sheet->getStyle("F{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
|
$sheet->getStyle("H{$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(20);
|
|
$sheet->getColumnDimension('D')->setWidth(13);
|
|
$sheet->getColumnDimension('E')->setWidth(10);
|
|
$sheet->getColumnDimension('F')->setWidth(15);
|
|
$sheet->getColumnDimension('G')->setWidth(30);
|
|
$sheet->getColumnDimension('H')->setWidth(16);
|
|
|
|
$writer = new Xlsx($spreadsheet);
|
|
$writer->save($filePath);
|
|
|
|
return response()->download($filePath, $fileName)->deleteFileAfterSend(true);
|
|
}
|
|
|
|
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' => 'nullable|exists:modules,id',
|
|
]);
|
|
|
|
$fechaInicio = Carbon::parse($request->fecha_inicio)->startOfDay();
|
|
$fechaFin = Carbon::parse($request->fecha_fin)->endOfDay();
|
|
$moduleId = $request->module_id;
|
|
|
|
$module = $moduleId ? Module::find($moduleId) : null;
|
|
|
|
// Consulta de Logs
|
|
$logs = VehicleTagLog::with(['vehicle', 'tag', 'cancellationReason'])
|
|
->where(function ($query) use ($fechaInicio, $fechaFin) {
|
|
// sustitucion normal: el log del TAG viejo tiene cancellation_at
|
|
$query->where(function ($q) use ($fechaInicio, $fechaFin) {
|
|
$q->where('action_type', 'sustitucion')
|
|
->whereNotNull('cancellation_at')
|
|
->whereBetween('cancellation_at', [$fechaInicio, $fechaFin]);
|
|
})
|
|
// sustitucion_primera_vez: un solo log sin cancellation_at, filtrar por created_at
|
|
->orWhere(function ($q) use ($fechaInicio, $fechaFin) {
|
|
$q->where('action_type', 'sustitucion_primera_vez')
|
|
->whereNull('cancellation_at')
|
|
->whereBetween('created_at', [$fechaInicio, $fechaFin]);
|
|
});
|
|
})
|
|
->when($moduleId, function ($query) use ($moduleId) {
|
|
$query->whereHas('performedBy', function ($q) use ($moduleId) {
|
|
$q->where('module_id', $moduleId);
|
|
});
|
|
})
|
|
->orderByRaw('COALESCE(cancellation_at, 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) {
|
|
// sustitucion_primera_vez: un solo log, el log mismo ES el nuevo TAG asignado
|
|
if ($log->action_type === 'sustitucion_primera_vez') {
|
|
return [
|
|
'niv' => $log->vehicle->niv ?? '',
|
|
'nrpv' => $log->vehicle->nrpv ?? '',
|
|
'marca' => $log->vehicle->marca ?? '',
|
|
'placa' => $log->vehicle->placa ?? '',
|
|
'modelo' => $log->vehicle->modelo ?? '',
|
|
'folio_ant' => '',
|
|
'folio_act' => $log->tag->folio ?? '',
|
|
'chip' => substr($log->tag->rfid ?? $log->tag->tag_number ?? '', 0, 24),
|
|
'fecha' => $log->created_at->format('d/m/Y'),
|
|
'observaciones' => $log->cancellationReason?->name ?? $log->cancellation_observations ?? '',
|
|
];
|
|
}
|
|
|
|
// sustitucion normal: este log es el TAG viejo cancelado, buscar el nuevo TAG
|
|
$newTagLog = VehicleTagLog::with('tag')
|
|
->where('vehicle_id', $log->vehicle_id)
|
|
->whereIn('action_type', ['sustitucion_primera_vez', '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' => substr($newTagLog?->tag?->rfid ?? $newTagLog?->tag?->tag_number ?? '', 0, 24),
|
|
'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', mb_strtoupper($module->name ?? 'TODOS LOS MÓDULOS', 'UTF-8'));
|
|
$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');
|
|
// Formato completo para manejar diferentes meses/años
|
|
if ($fechaInicio->format('m/Y') === $fechaFin->format('m/Y')) {
|
|
// Mismo mes y año
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
} elseif ($fechaInicio->format('Y') === $fechaFin->format('Y')) {
|
|
// Diferente mes, mismo año
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' de ' . $fechaInicio->translatedFormat('F') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
} else {
|
|
// Diferente año
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' de ' . $fechaInicio->translatedFormat('F') . ' ' . $fechaInicio->format('Y') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
}
|
|
|
|
// 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', $anioTexto);
|
|
$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',
|
|
'tag_number' => substr( $log->tag->tag_number ?? '', 0, 24) ?? 'N/A',
|
|
'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_dañadas_' . $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', mb_strtoupper($module->name ?? 'TODOS LOS MÓDULOS', 'UTF-8'));
|
|
$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');
|
|
// Formato completo para manejar diferentes meses/años
|
|
if ($fechaInicio->format('m/Y') === $fechaFin->format('m/Y')) {
|
|
// Mismo mes y año
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
} elseif ($fechaInicio->format('Y') === $fechaFin->format('Y')) {
|
|
// Diferente mes, mismo año
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' de ' . $fechaInicio->translatedFormat('F') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
} else {
|
|
// Diferente año
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' de ' . $fechaInicio->translatedFormat('F') . ' ' . $fechaInicio->format('Y') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
}
|
|
|
|
// 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', $anioTexto);
|
|
$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}", "ID DE LA\nCONSTANCIA (CHIP)");
|
|
|
|
$sheet->mergeCells("J{$h1}:K{$h2}");
|
|
$sheet->setCellValue("J{$h1}", "FECHA DE\nVINCULACIÓN");
|
|
|
|
$sheet->mergeCells("L{$h1}:N{$h2}");
|
|
$sheet->setCellValue("L{$h1}", "OBSERVACIONES \n(MOTIVO DEL DAÑO, ERRORES, ETC)");
|
|
// Aplicar estilo beige
|
|
$sheet->getStyle("A{$h1}:N{$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['tag_number']);
|
|
|
|
$sheet->mergeCells("J{$row}:K{$row}");
|
|
$sheet->setCellValue("J{$row}", $item['fecha']);
|
|
|
|
$sheet->mergeCells("L{$row}:N{$row}");
|
|
$sheet->setCellValue("L{$row}", $item['motivo']);
|
|
|
|
// Estilos de celda
|
|
$sheet->getStyle("A{$row}:N{$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)
|
|
{
|
|
// 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;
|
|
|
|
// Obtener información del módulo
|
|
$module = $moduleId ? Module::find($moduleId) : null;
|
|
|
|
// QUERY 1: VehicleTagLogs (Inscripciones, Sustituciones, Actualizaciones, Cancelaciones de Vehículo)
|
|
$logs = VehicleTagLog::with([
|
|
'vehicle.records.module',
|
|
'tag',
|
|
'cancellationReason'
|
|
])
|
|
->whereIn('action_type', ['sustitucion_primera_vez', 'sustitucion', 'actualizacion', 'cancelacion'])
|
|
->where(function ($query) use ($fechaInicio, $fechaFin) {
|
|
$query->where(function ($q) use ($fechaInicio, $fechaFin) {
|
|
$q->whereIn('action_type', ['sustitucion_primera_vez', 'sustitucion', 'actualizacion'])
|
|
->whereBetween('created_at', [$fechaInicio, $fechaFin]);
|
|
})
|
|
->orWhere(function ($q) use ($fechaInicio, $fechaFin) {
|
|
$q->where('action_type', 'cancelacion')
|
|
->whereBetween('cancellation_at', [$fechaInicio, $fechaFin]);
|
|
});
|
|
})
|
|
->when($moduleId, function ($query) use ($moduleId) {
|
|
$query->whereHas('vehicle.records', function ($q) use ($moduleId) {
|
|
$q->where('module_id', $moduleId);
|
|
});
|
|
})
|
|
->get();
|
|
|
|
//TagCancellationLogs (Cancelaciones de Tags sueltos)
|
|
$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 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');
|
|
|
|
// 2. MAPEO DE DATOS (Normalización A-M)
|
|
$data = $allLogs->map(function ($log) {
|
|
// Determinar Vehículo
|
|
$vehicle = $log->vehicle ?? $log->tag->vehicle ?? null;
|
|
|
|
// 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 == 'sustitucion_primera_vez') $motivo = 'SUSTITUCIÓN POR PRIMERA VEZ';
|
|
if ($log->action_type == 'actualizacion') $motivo = 'ACTUALIZACIÓ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->tag_number ?? '',
|
|
'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(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');
|
|
$drawing->setPath($logoPath);
|
|
$drawing->setHeight(55);
|
|
$drawing->setCoordinates('B3');
|
|
$drawing->setWorksheet($sheet);
|
|
}
|
|
|
|
// --- BLOQUE DE INFORMACIÓN (Encabezado con Merges A-C) ---
|
|
|
|
// Fila 5: ENTIDAD
|
|
$sheet->mergeCells('A5:C5');
|
|
$sheet->setCellValue('A5', 'ENTIDAD:');
|
|
$sheet->getStyle('A5')->applyFromArray($styleLabel);
|
|
|
|
$sheet->mergeCells('D5:M5');
|
|
$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->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);
|
|
|
|
// Fila 9: PERIODO
|
|
$sheet->mergeCells('A9:C9');
|
|
$sheet->setCellValue('A9', 'PERIODO A INFORMAR:');
|
|
$sheet->getStyle('A9')->applyFromArray($styleLabel);
|
|
|
|
Carbon::setLocale('es');
|
|
// Formato completo para manejar diferentes meses/años
|
|
if ($fechaInicio->format('m/Y') === $fechaFin->format('m/Y')) {
|
|
// Mismo mes y año
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
} elseif ($fechaInicio->format('Y') === $fechaFin->format('Y')) {
|
|
// Diferente mes, mismo año
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' de ' . $fechaInicio->translatedFormat('F') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
} else {
|
|
// Diferente año
|
|
$periodoTexto = 'del ' . $fechaInicio->format('d') . ' de ' . $fechaInicio->translatedFormat('F') . ' ' . $fechaInicio->format('Y') . ' al ' . $fechaFin->format('d') . ' de ' . $fechaFin->translatedFormat('F');
|
|
$anioTexto = $fechaFin->format('Y');
|
|
}
|
|
|
|
// Fechas (D-G)
|
|
$sheet->mergeCells('D9:G9');
|
|
$sheet->setCellValue('D9', $periodoTexto);
|
|
$sheet->getStyle('D9:G9')->applyFromArray($styleBox);
|
|
$sheet->getStyle('D9')->getFont()->setSize(14);
|
|
|
|
// "de" (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 (I-J)
|
|
$sheet->mergeCells('I9:J9');
|
|
$sheet->setCellValue('I9', $anioTexto);
|
|
$sheet->getStyle('I9:J9')->applyFromArray($styleBox);
|
|
$sheet->getStyle('I9')->getFont()->setSize(14);
|
|
|
|
|
|
// --- 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],
|
|
]);
|
|
$sheet->getRowDimension($rowTitle)->setRowHeight(25);
|
|
|
|
$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);
|
|
}
|
|
|
|
$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
|
|
|
|
$writer = new Xlsx($spreadsheet);
|
|
$writer->save($filePath);
|
|
|
|
return response()->download($filePath, $fileName)->deleteFileAfterSend(true);
|
|
}
|
|
|
|
/**
|
|
* Exportar resultados de búsqueda a Excel
|
|
*/
|
|
public function exportSearchRecords(Request $request)
|
|
{
|
|
// Reutilizar la misma validación y lógica de búsqueda
|
|
$request->validate([
|
|
'folio' => 'nullable|string',
|
|
'placa' => 'nullable|string',
|
|
'vin' => 'nullable|string',
|
|
'tag_number' => 'nullable|string',
|
|
'module_id' => 'nullable|integer|exists:modules,id',
|
|
'action_type' => 'nullable|string|in:sustitucion_primera_vez,sustitucion,cancelacion',
|
|
'status' => 'nullable|string',
|
|
'start_date' => 'nullable|date',
|
|
'end_date' => 'nullable|date|after_or_equal:start_date',
|
|
]);
|
|
|
|
// Reutilizar la misma query de búsqueda (sin paginación)
|
|
$records = Record::forUserModule(Auth::user())
|
|
->with([
|
|
'vehicle',
|
|
'vehicle.owner',
|
|
'vehicle.tag:id,vehicle_id,folio,tag_number,status_id,package_id,module_id',
|
|
'vehicle.tag.status:id,code,name',
|
|
'vehicle.tag.package:id,lot,box_number',
|
|
'vehicle.tag.module:id,name',
|
|
'files:id,record_id,name_id,path,md5',
|
|
'files.catalogName:id,name',
|
|
'user:id,name,username,module_id',
|
|
'module:id,name',
|
|
'error:id,code,description',
|
|
'vehicle.vehicleTagLogs' => function ($q) {
|
|
$q->with([
|
|
'tag:id,folio,tag_number,status_id,module_id,package_id',
|
|
'tag.status:id,code,name',
|
|
'tag.module:id,name',
|
|
'tag.package:id,lot,box_number'
|
|
])->orderBy('created_at', 'DESC');
|
|
},
|
|
])->orderBy('id', 'ASC');
|
|
|
|
// Aplicar los mismos filtros
|
|
if ($request->filled('folio')) {
|
|
$records->whereHas('vehicle.tag', function ($q) use ($request) {
|
|
$q->where('folio', 'LIKE', '%' . $request->input('folio') . '%');
|
|
});
|
|
}
|
|
if ($request->filled('placa')) {
|
|
$records->whereHas('vehicle', function ($q) use ($request) {
|
|
$q->where('placa', 'LIKE', '%' . $request->input('placa') . '%');
|
|
});
|
|
}
|
|
if ($request->filled('vin')) {
|
|
$records->whereHas('vehicle', function ($q) use ($request) {
|
|
$q->where('niv', 'LIKE', '%' . $request->input('vin') . '%');
|
|
});
|
|
}
|
|
if ($request->filled('tag_number')) {
|
|
$records->whereHas('vehicle.tag', function ($q) use ($request) {
|
|
$q->where('tag_number', 'LIKE', '%' . $request->input('tag_number') . '%');
|
|
});
|
|
}
|
|
if ($request->filled('module_id')) {
|
|
$records->where('module_id', $request->input('module_id'));
|
|
}
|
|
if ($request->filled('action_type')) {
|
|
$records->whereHas('vehicle.vehicleTagLogs', function ($q) use ($request) {
|
|
$q->where('action_type', $request->input('action_type'))
|
|
->whereRaw('id = (
|
|
SELECT MAX(id)
|
|
FROM vehicle_tags_logs
|
|
WHERE vehicle_id = vehicle.id
|
|
)');
|
|
});
|
|
}
|
|
if ($request->filled('status')) {
|
|
$records->whereHas('vehicle.tag.status', function ($q) use ($request) {
|
|
$q->where('code', $request->input('status'));
|
|
});
|
|
}
|
|
if ($request->filled('start_date')) {
|
|
$records->whereDate('created_at', '>=', $request->input('start_date'));
|
|
}
|
|
if ($request->filled('end_date')) {
|
|
$records->whereDate('created_at', '<=', $request->input('end_date'));
|
|
}
|
|
|
|
$allRecords = $records->get();
|
|
|
|
if ($allRecords->isEmpty()) {
|
|
return ApiResponse::NOT_FOUND->response([
|
|
'message' => 'No se encontraron registros con los criterios de búsqueda proporcionados.',
|
|
]);
|
|
}
|
|
|
|
// Preparar datos para Excel: una fila por cada entrada del historial de tags
|
|
$excelRows = [];
|
|
|
|
foreach ($allRecords as $record) {
|
|
$vehicle = $record->vehicle;
|
|
$niv = $vehicle->niv ?? '';
|
|
$nrpv = $vehicle->nrpv ?? '';
|
|
$marca = $vehicle->marca ?? '';
|
|
$placa = $vehicle->placa ?? '';
|
|
$modelo = $vehicle->modelo ?? '';
|
|
$moduleName = $record->module->name ?? '';
|
|
$operador = $record->user->name ?? $record->user->username ?? '';
|
|
|
|
// Obtener logs ordenados por fecha, excluyendo actualizaciones
|
|
$vehicleLogs = $vehicle->vehicleTagLogs
|
|
->whereIn('action_type', ['sustitucion_primera_vez', 'sustitucion', 'cancelacion'])
|
|
->sortBy('created_at');
|
|
|
|
// Generar una fila por cada log (cada acción en el historial)
|
|
foreach ($vehicleLogs as $log) {
|
|
$tag = $log->tag;
|
|
|
|
// Determinar status basado en el tipo de acción y si tiene cancelación
|
|
$status = 'Asignado';
|
|
$tipoTramite = ucfirst($log->action_type);
|
|
$fecha = Carbon::parse($log->created_at);
|
|
|
|
// Si el log tiene cancellation_at, es una cancelación/sustitución del tag
|
|
if ($log->cancellation_at) {
|
|
$status = 'Cancelado';
|
|
$fecha = Carbon::parse($log->cancellation_at);
|
|
} elseif ($tag && $tag->status) {
|
|
$status = $tag->status->name;
|
|
}
|
|
|
|
$excelRows[] = [
|
|
'status' => $status,
|
|
'modulo' => $tag?->module?->name ?? $moduleName,
|
|
'operador' => $operador,
|
|
'tipo_tramite' => $tipoTramite,
|
|
'niv' => $niv,
|
|
'nrpv' => $nrpv,
|
|
'marca' => $marca,
|
|
'placa' => $placa,
|
|
'modelo' => $modelo,
|
|
'folio' => $tag?->folio ?? '',
|
|
'caja' => $tag?->package?->box_number ?? '',
|
|
'tag' => $tag?->tag_number ?? '',
|
|
'fecha' => $fecha->format('d/m/Y H:i'),
|
|
'sort_date' => $fecha->timestamp,
|
|
];
|
|
}
|
|
|
|
// Si no hay logs pero hay tag asignado, agregar al menos una fila
|
|
if ($vehicleLogs->isEmpty() && $vehicle->tag) {
|
|
$currentTag = $vehicle->tag;
|
|
$fechaActual = $record->created_at ? Carbon::parse($record->created_at) : now();
|
|
|
|
$excelRows[] = [
|
|
'status' => $currentTag->status?->name ?? 'Asignado',
|
|
'modulo' => $currentTag->module?->name ?? $moduleName,
|
|
'operador' => $operador,
|
|
'tipo_tramite' => 'Inscripcion',
|
|
'niv' => $niv,
|
|
'nrpv' => $nrpv,
|
|
'marca' => $marca,
|
|
'placa' => $placa,
|
|
'modelo' => $modelo,
|
|
'folio' => $currentTag->folio ?? '',
|
|
'caja' => $currentTag->package?->box_number ?? '',
|
|
'tag' => $currentTag->tag_number ?? '',
|
|
'fecha' => $fechaActual->format('d/m/Y H:i'),
|
|
'sort_date' => $fechaActual->timestamp,
|
|
];
|
|
}
|
|
}
|
|
|
|
// Ordenar todas las filas por fecha ascendente
|
|
usort($excelRows, function ($a, $b) {
|
|
return $a['sort_date'] <=> $b['sort_date'];
|
|
});
|
|
|
|
// Crear Excel
|
|
$fileName = 'Busqueda_Registros_' . now()->format('Ymd_His') . '.xlsx';
|
|
$filePath = storage_path('app/temp/' . $fileName);
|
|
if (!file_exists(dirname($filePath))) {
|
|
mkdir(dirname($filePath), 0755, true);
|
|
}
|
|
|
|
$spreadsheet = new Spreadsheet();
|
|
$sheet = $spreadsheet->getActiveSheet();
|
|
$sheet->setTitle('Registros');
|
|
|
|
// Estilos
|
|
$headerStyle = [
|
|
'font' => ['bold' => true, 'size' => 11, 'color' => ['rgb' => 'FFFFFF']],
|
|
'fill' => ['fillType' => Fill::FILL_SOLID, 'startColor' => ['rgb' => '4472C4']],
|
|
'alignment' => ['horizontal' => Alignment::HORIZONTAL_CENTER, 'vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true],
|
|
'borders' => ['allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN]]
|
|
];
|
|
|
|
$cellStyle = [
|
|
'borders' => ['allBorders' => ['borderStyle' => PhpSpreadsheetBorder::BORDER_THIN]],
|
|
'alignment' => ['vertical' => Alignment::VERTICAL_CENTER, 'wrapText' => true],
|
|
];
|
|
|
|
// Encabezados con todos los campos solicitados
|
|
$headers = [
|
|
'A' => 'Estatus',
|
|
'B' => 'Módulo',
|
|
'C' => 'Operador',
|
|
'D' => 'Tipo de Trámite',
|
|
'E' => 'NIV',
|
|
'F' => 'NRPV/NCI',
|
|
'G' => 'Marca',
|
|
'H' => 'Placa',
|
|
'I' => 'Año',
|
|
'J' => 'Folio',
|
|
'K' => 'Caja',
|
|
'L' => 'Tag ID',
|
|
];
|
|
|
|
$row = 1;
|
|
foreach ($headers as $col => $text) {
|
|
$sheet->setCellValue("{$col}{$row}", $text);
|
|
$sheet->getStyle("{$col}{$row}")->applyFromArray($headerStyle);
|
|
}
|
|
$sheet->getRowDimension($row)->setRowHeight(30);
|
|
|
|
// Datos
|
|
$row = 2;
|
|
foreach ($excelRows as $item) {
|
|
$sheet->setCellValue('A' . $row, $item['status']);
|
|
$sheet->setCellValue('B' . $row, $item['modulo']);
|
|
$sheet->setCellValue('C' . $row, $item['operador']);
|
|
$sheet->setCellValue('D' . $row, $item['tipo_tramite']);
|
|
$sheet->setCellValue('E' . $row, $item['niv']);
|
|
$sheet->setCellValue('F' . $row, $item['nrpv']);
|
|
$sheet->setCellValue('G' . $row, $item['marca']);
|
|
$sheet->setCellValue('H' . $row, $item['placa']);
|
|
$sheet->setCellValue('I' . $row, $item['modelo']);
|
|
$sheet->setCellValue('J' . $row, $item['folio']);
|
|
$sheet->setCellValue('K' . $row, $item['caja']);
|
|
$sheet->setCellValue('L' . $row, $item['tag']);
|
|
|
|
// Aplicar estilos a todas las columnas
|
|
foreach (range('A', 'L') as $col) {
|
|
$sheet->getStyle("{$col}{$row}")->applyFromArray($cellStyle);
|
|
}
|
|
|
|
// Centrar columnas específicas
|
|
$sheet->getStyle("A{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
|
$sheet->getStyle("D{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
|
$sheet->getStyle("I{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
|
$sheet->getStyle("J{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
|
$sheet->getStyle("K{$row}")->getAlignment()->setHorizontal(Alignment::HORIZONTAL_CENTER);
|
|
|
|
$row++;
|
|
}
|
|
|
|
// Ajustar anchos de columna
|
|
$sheet->getColumnDimension('A')->setWidth(15); // Estatus
|
|
$sheet->getColumnDimension('B')->setWidth(25); // Módulo
|
|
$sheet->getColumnDimension('C')->setWidth(20); // Operador
|
|
$sheet->getColumnDimension('D')->setWidth(18); // Tipo de Trámite
|
|
$sheet->getColumnDimension('E')->setWidth(22); // NIV
|
|
$sheet->getColumnDimension('F')->setWidth(14); // NRPV/NCI
|
|
$sheet->getColumnDimension('G')->setWidth(15); // Marca
|
|
$sheet->getColumnDimension('H')->setWidth(12); // Placa
|
|
$sheet->getColumnDimension('I')->setWidth(8); // Año
|
|
$sheet->getColumnDimension('J')->setWidth(12); // Folio
|
|
$sheet->getColumnDimension('K')->setWidth(8); // Caja
|
|
$sheet->getColumnDimension('L')->setWidth(30); // Tag ID
|
|
|
|
// Guardar archivo
|
|
$writer = new Xlsx($spreadsheet);
|
|
$writer->save($filePath);
|
|
|
|
return response()->download($filePath, $fileName)->deleteFileAfterSend(true);
|
|
}
|
|
}
|