497 lines
17 KiB
PHP
497 lines
17 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Repuve;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Barryvdh\DomPDF\Facade\Pdf;
|
|
use App\Models\Record;
|
|
use App\Models\Tag;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
|
use Codedge\Fpdf\Fpdf\Fpdf;
|
|
use Illuminate\Http\Request;
|
|
|
|
class RecordController extends Controller
|
|
{
|
|
public function generatePdf($id)
|
|
{
|
|
$record = Record::with('vehicle.owner', 'user', 'module')->findOrFail($id);
|
|
|
|
$pdf = Pdf::loadView('pdfs.record', compact('record'))
|
|
->setPaper('a4', 'portrait')
|
|
->setOptions([
|
|
'defaultFont' => 'sans-serif',
|
|
'isHtml5ParserEnabled' => true,
|
|
'isRemoteEnabled' => true,
|
|
]);
|
|
|
|
return $pdf->stream('constancia-inscripcion-' . $id . '.pdf');
|
|
}
|
|
|
|
public function generatePdfVerification($id)
|
|
{
|
|
$record = Record::with('vehicle.owner', 'user')->findOrFail($id);
|
|
|
|
$pdf = Pdf::loadView('pdfs.verification', compact('record'))
|
|
->setPaper('a4', 'landscape')
|
|
->setOptions([
|
|
'defaultFont' => 'sans-serif',
|
|
'isHtml5ParserEnabled' => true,
|
|
'isRemoteEnabled' => true,
|
|
]);
|
|
|
|
return $pdf->stream('hoja-verificacion-' . $id . '.pdf');
|
|
}
|
|
|
|
public function generatePdfConstancia($id)
|
|
{
|
|
$record = Record::with('vehicle.owner.municipality', 'user')->findOrFail($id);
|
|
|
|
$pdf = Pdf::loadView('pdfs.constancia', compact('record'))
|
|
->setPaper('a4', 'landscape')
|
|
->setOptions([
|
|
'defaultFont' => 'sans-serif',
|
|
'isHtml5ParserEnabled' => true,
|
|
'isRemoteEnabled' => true,
|
|
]);
|
|
|
|
return $pdf->stream('constancia-inscripcion' . $id . '.pdf');
|
|
}
|
|
|
|
/**
|
|
* Generar PDF con las imágenes
|
|
*/
|
|
public function generatePdfImages($id)
|
|
{
|
|
try {
|
|
// Obtener el record con sus archivos
|
|
$record = Record::with(['vehicle.owner', 'files'])->findOrFail($id);
|
|
|
|
// Validar que tenga archivos
|
|
if ($record->files->isEmpty()) {
|
|
return ApiResponse::NOT_FOUND->response([
|
|
'message' => 'El expediente no tiene imágenes adjuntas.',
|
|
'record_id' => $id,
|
|
]);
|
|
}
|
|
|
|
// Crear instancia de FPDF
|
|
$pdf = new Fpdf('P', 'mm', 'A4');
|
|
$pdf->SetAutoPageBreak(false);
|
|
$pdf->SetMargins(10, 10, 10);
|
|
|
|
$currentImage = 0;
|
|
|
|
foreach ($record->files as $file) {
|
|
$currentImage++;
|
|
|
|
// Buscar archivo en disk 'records'
|
|
$diskRecords = Storage::disk('records');
|
|
|
|
$fileContent = null;
|
|
if ($diskRecords->exists($file->path)) {
|
|
$fileContent = $diskRecords->get($file->path);
|
|
}
|
|
|
|
// Si no se encontró el archivo, continuar
|
|
if ($fileContent === null) {
|
|
continue;
|
|
}
|
|
|
|
// Agregar nueva página
|
|
$pdf->AddPage();
|
|
|
|
// Header con folio
|
|
$pdf->SetFillColor(44, 62, 80);
|
|
$pdf->Rect(0, 0, 210, 20, 'F');
|
|
$pdf->SetTextColor(255, 255, 255);
|
|
$pdf->SetFont('Arial', 'B', 14);
|
|
$pdf->SetXY(10, 7);
|
|
$pdf->Cell(0, 6, 'FOLIO: ' . $record->folio, 0, 1, 'L');
|
|
|
|
// Obtener ruta temporal del archivo
|
|
$tempPath = tempnam(sys_get_temp_dir(), 'pdf_img_');
|
|
file_put_contents($tempPath, $fileContent);
|
|
|
|
// Obtener dimensiones de la imagen
|
|
$imageInfo = getimagesize($tempPath);
|
|
|
|
if ($imageInfo !== false) {
|
|
list($originalWidth, $originalHeight) = $imageInfo;
|
|
$imageType = $imageInfo[2];
|
|
|
|
$availableWidth = 190; // 210mm - 20mm márgenes
|
|
$availableHeight = 247; // 297mm - 20mm header - 20mm footer - 10mm márgenes
|
|
|
|
// Calcular dimensiones manteniendo proporción
|
|
$ratio = min($availableWidth / $originalWidth, $availableHeight / $originalHeight);
|
|
$newWidth = $originalWidth * $ratio;
|
|
$newHeight = $originalHeight * $ratio;
|
|
|
|
// Centrar imagen
|
|
$x = (210 - $newWidth) / 2;
|
|
$y = 25 + (($availableHeight - $newHeight) / 2);
|
|
|
|
// Determinar tipo de imagen
|
|
$imageExtension = '';
|
|
switch ($imageType) {
|
|
case IMAGETYPE_JPEG:
|
|
$imageExtension = 'JPEG';
|
|
break;
|
|
case IMAGETYPE_JPEG:
|
|
$imageExtension = 'JPG';
|
|
break;
|
|
case IMAGETYPE_PNG:
|
|
$imageExtension = 'PNG';
|
|
break;
|
|
default:
|
|
// Si no es un formato soportado, continuar
|
|
unlink($tempPath);
|
|
continue 2;
|
|
}
|
|
|
|
// Insertar imagen
|
|
$pdf->Image($tempPath, $x, $y, $newWidth, $newHeight, $imageExtension);
|
|
}
|
|
|
|
// Limpiar archivo temporal
|
|
unlink($tempPath);
|
|
}
|
|
|
|
// Verificar que se agregaron páginas
|
|
if ($pdf->PageNo() == 0) {
|
|
return ApiResponse::NOT_FOUND->response([
|
|
'message' => 'No se pudieron procesar las imágenes del expediente.',
|
|
'record_id' => $id,
|
|
]);
|
|
}
|
|
|
|
// Generar PDF
|
|
$pdfContent = $pdf->Output('S');
|
|
|
|
return response($pdfContent, 200)
|
|
->header('Content-Type', 'application/pdf')
|
|
->header('Content-Disposition', 'inline; filename="expediente-imagenes-' . $record->folio . '.pdf"');
|
|
} catch (\Exception $e) {
|
|
return ApiResponse::INTERNAL_ERROR->response([
|
|
'message' => 'Error al generar el PDF de imágenes',
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
public function generatePdfForm($id)
|
|
{
|
|
try {
|
|
$record = Record::with([
|
|
'vehicle',
|
|
'vehicle.owner',
|
|
'vehicle.tag',
|
|
])->findOrFail($id);
|
|
|
|
if (!$record->vehicle) {
|
|
return ApiResponse::NOT_FOUND->response([
|
|
'message' => 'El registro no tiene un vehículo asociado.',
|
|
'record_id' => $id,
|
|
]);
|
|
}
|
|
|
|
$vehicle = $record->vehicle;
|
|
$owner = $vehicle->owner;
|
|
$tag = $vehicle->tag;
|
|
|
|
$now = Carbon::now()->locale('es_MX');
|
|
|
|
$data = [
|
|
// Datos del vehículo
|
|
'marca' => strtoupper($vehicle->marca ?? ''),
|
|
'linea' => strtoupper($vehicle->linea ?? ''),
|
|
'modelo' => $vehicle->modelo ?? '',
|
|
'niv' => strtoupper($vehicle->niv ?? ''),
|
|
'numero_motor' => strtoupper($vehicle->numero_motor ?? ''),
|
|
'placa' => strtoupper($vehicle->placa ?? ''),
|
|
'folio' => $tag?->folio ?? $record->folio ?? '',
|
|
|
|
// Datos del propietario
|
|
'telefono' => $owner?->telefono ?? '',
|
|
|
|
// Fecha actual
|
|
'fecha' => $now->format('d'),
|
|
'mes' => ucfirst($now->translatedFormat('F')),
|
|
'anio' => $now->format('Y'),
|
|
|
|
'record_id' => $record->id,
|
|
'owner_name' => $owner?->full_name ?? '',
|
|
];
|
|
|
|
$pdf = Pdf::loadView('pdfs.form', $data)
|
|
->setPaper('a4', 'portrait')
|
|
->setOptions([
|
|
'defaultFont' => 'sans-serif',
|
|
'isHtml5ParserEnabled' => true,
|
|
'isRemoteEnabled' => true,
|
|
]);
|
|
|
|
return $pdf->stream('solicitud-sustitucion-' . time() . '.pdf');
|
|
} catch (\Exception $e) {
|
|
return ApiResponse::NOT_FOUND->response([
|
|
'message' => 'No se encontró el registro del expediente proporcionado.',
|
|
'record_id' => $id,
|
|
]);
|
|
} catch (\Exception $e) {
|
|
return ApiResponse::INTERNAL_ERROR->response([
|
|
'message' => 'Error al generar el PDF del formulario',
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
public function pdfCancelledTag(Tag $tag)
|
|
{
|
|
try {
|
|
// Validar que el tag esté cancelado
|
|
if (!$tag->isCancelled()) {
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'Solo se puede generar PDF para tags cancelados.',
|
|
'current_status' => $tag->status->name,
|
|
]);
|
|
}
|
|
|
|
// Obtener datos de cancelación
|
|
$cancellationData = $this->cancellationData($tag);
|
|
|
|
$pdf = Pdf::loadView('pdfs.tag', [
|
|
'cancellation' => $cancellationData,
|
|
])
|
|
->setPaper('a4', 'portrait')
|
|
->setOptions([
|
|
'defaultFont' => 'sans-serif',
|
|
'isHtml5ParserEnabled' => true,
|
|
'isRemoteEnabled' => true,
|
|
]);
|
|
|
|
return $pdf->stream('constancia_cancelada_' . $tag->tag_number . '.pdf');
|
|
} catch (\Exception $e) {
|
|
return ApiResponse::INTERNAL_ERROR->response([
|
|
'message' => 'Error al generar el PDF.',
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
public function pdfSubstitutedTag(Tag $tag)
|
|
{
|
|
try {
|
|
// Validar que el tag tenga una sustitución registrada
|
|
$hasSubstitution = $tag->vehicleTagLogs()
|
|
->where('action_type', 'sustitucion')
|
|
->whereNotNull('cancellation_at')
|
|
->exists();
|
|
|
|
if (!$hasSubstitution) {
|
|
return ApiResponse::BAD_REQUEST->response([
|
|
'message' => 'Solo se puede generar PDF para tags sustituidos.',
|
|
'tag_folio' => $tag->folio,
|
|
]);
|
|
}
|
|
|
|
// Obtener datos de sustitución
|
|
$substitutionData = $this->substitutionData($tag);
|
|
|
|
$pdf = Pdf::loadView('pdfs.tag_substituted', [
|
|
'substitution' => $substitutionData,
|
|
])
|
|
->setPaper('a4', 'portrait')
|
|
->setOptions([
|
|
'defaultFont' => 'sans-serif',
|
|
'isHtml5ParserEnabled' => true,
|
|
'isRemoteEnabled' => true,
|
|
]);
|
|
|
|
return $pdf->stream('constancia_sustituida_' . $tag->folio . '.pdf');
|
|
} catch (\Exception $e) {
|
|
return ApiResponse::INTERNAL_ERROR->response([
|
|
'message' => 'Error al generar el PDF.',
|
|
'error' => $e->getMessage(),
|
|
]);
|
|
}
|
|
}
|
|
|
|
|
|
private function cancellationData(Tag $tag)
|
|
{
|
|
$data = [
|
|
'fecha' => now()->format('d/m/Y'),
|
|
'folio' => $tag->folio ?? '',
|
|
'id_chip' => '',
|
|
'placa' => '',
|
|
'niv' => '',
|
|
'motivo' => 'N/A',
|
|
'operador' => 'N/A',
|
|
'modulo' => '',
|
|
'ubicacion' => '',
|
|
];
|
|
|
|
// Intentar obtener datos del vehículo si existe
|
|
if ($tag->vehicle_id && $tag->vehicle) {
|
|
$data['id_chip'] = $tag->vehicle->id_chip ?? '';
|
|
$data['placa'] = $tag->vehicle->placa ?? '';
|
|
$data['niv'] = $tag->vehicle->niv ?? '';
|
|
}
|
|
|
|
// Buscar log de cancelación directa
|
|
$tagCancellationLog = $tag->cancellationLogs()
|
|
->with(['cancellationReason', 'cancelledBy'])
|
|
->latest()
|
|
->first();
|
|
|
|
if ($tagCancellationLog) {
|
|
$data['motivo'] = $tagCancellationLog->cancellationReason->name ?? 'No especificado';
|
|
$data['operador'] = $tagCancellationLog->cancelledBy->name ?? 'Sistema';
|
|
|
|
// Cargar módulo del cual el usuario es responsable
|
|
if ($tagCancellationLog->cancelledBy) {
|
|
$user = $tagCancellationLog->cancelledBy;
|
|
$this->loadUserModule($user, $data);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
// Buscar log de vehículo (tag asignado y luego cancelado)
|
|
$vehicleTagLog = $tag->vehicleTagLogs()
|
|
->where('action_type', 'cancelacion')
|
|
->with(['cancellationReason', 'cancelledBy', 'vehicle'])
|
|
->latest()
|
|
->first();
|
|
|
|
if ($vehicleTagLog) {
|
|
$data['motivo'] = $vehicleTagLog->cancellationReason->name ?? 'No especificado';
|
|
$data['operador'] = $vehicleTagLog->cancelledBy->name ?? 'Sistema';
|
|
|
|
// Cargar módulo del cual el usuario es responsable
|
|
if ($vehicleTagLog->cancelledBy) {
|
|
$user = $vehicleTagLog->cancelledBy;
|
|
$this->loadUserModule($user, $data);
|
|
}
|
|
|
|
if ($vehicleTagLog->vehicle) {
|
|
$data['id_chip'] = $vehicleTagLog->vehicle->id_chip ?? '';
|
|
$data['placa'] = $vehicleTagLog->vehicle->placa ?? '';
|
|
$data['niv'] = $vehicleTagLog->vehicle->niv ?? '';
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
private function substitutionData(Tag $tag)
|
|
{
|
|
$data = [
|
|
'fecha' => now()->format('d/m/Y'),
|
|
'folio' => $tag->folio ?? '',
|
|
'folio_sustituto' => '',
|
|
'id_chip' => '',
|
|
'placa' => '',
|
|
'niv' => '',
|
|
'motivo' => 'N/A',
|
|
'operador' => 'N/A',
|
|
'modulo' => '',
|
|
'ubicacion' => '',
|
|
];
|
|
|
|
// log de CANCELACIÓN del tag original
|
|
$oldTagLog = $tag->vehicleTagLogs()
|
|
->where('action_type', 'sustitucion')
|
|
->whereNotNull('cancellation_at')
|
|
->with(['cancellationReason', 'cancelledBy', 'vehicle'])
|
|
->latest()
|
|
->first();
|
|
|
|
if (!$oldTagLog) {
|
|
return $data; // No se encontró sustitución
|
|
}
|
|
|
|
// datos del motivo y operador
|
|
$data['fecha'] = $oldTagLog->cancellation_at->format('d/m/Y');
|
|
$data['motivo'] = $oldTagLog->cancellationReason->name ?? 'No especificado';
|
|
$data['operador'] = $oldTagLog->cancelledBy->name ?? 'Sistema';
|
|
|
|
// módulo del usuario
|
|
if ($oldTagLog->cancelledBy) {
|
|
$this->loadUserModule($oldTagLog->cancelledBy, $data);
|
|
}
|
|
|
|
// datos del vehículo
|
|
if ($oldTagLog->vehicle) {
|
|
$data['id_chip'] = $oldTagLog->vehicle->id_chip ?? '';
|
|
$data['placa'] = $oldTagLog->vehicle->placa ?? '';
|
|
$data['niv'] = $oldTagLog->vehicle->niv ?? '';
|
|
|
|
// tag NUEVO
|
|
$newTag = $oldTagLog->vehicle->tag;
|
|
$data['folio_sustituto'] = $newTag?->folio ?? '';
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Cargar módulo del usuario
|
|
*/
|
|
private function loadUserModule($user, &$data)
|
|
{
|
|
// Intentar cargar module
|
|
$user->load('module');
|
|
|
|
// Si no tiene module, usar responsibleModule
|
|
if (!$user->module) {
|
|
$user->load('responsibleModule');
|
|
|
|
if ($user->responsibleModule) {
|
|
$data['modulo'] = $user->responsibleModule->name;
|
|
$data['ubicacion'] = $user->responsibleModule->address;
|
|
}
|
|
} else {
|
|
$data['modulo'] = $user->module->name;
|
|
$data['ubicacion'] = $user->module->address;
|
|
}
|
|
}
|
|
|
|
public function errors(Request $request)
|
|
{
|
|
$request->validate([
|
|
'folio' => 'nullable|string',
|
|
'placa' => 'nullable|string',
|
|
'vin' => 'nullable|string',
|
|
]);
|
|
|
|
$records = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'user', 'error'])
|
|
->whereNotNull('api_response')
|
|
->whereRaw("JSON_EXTRACT(api_response, '$.has_error') = true")
|
|
->orderBy('id', 'ASC');
|
|
|
|
if ($request->filled('folio')) {
|
|
$records->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') . '%');
|
|
});
|
|
}
|
|
|
|
return ApiResponse::OK->response([
|
|
'message' => 'Expedientes con errores encontrados exitosamente',
|
|
'records' => $records->paginate(config('app.pagination')),
|
|
]);
|
|
}
|
|
}
|