ADD: Implementar generación de PDF para tags sustituidos, correción de Reporte Robo
This commit is contained in:
parent
cb16c0b91c
commit
8c6afe40bc
@ -283,63 +283,6 @@ private function checkIfStolen(string $niv)
|
||||
return $this->repuveService->verificarRobo($niv);
|
||||
}
|
||||
|
||||
public function stolen(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'folio' => ['sometimes', 'string', 'max:50'],
|
||||
'tag_number' => ['sometimes', 'string', 'exists:tags,tag_number'],
|
||||
'vin' => ['sometimes', 'string', 'max:30'],
|
||||
]);
|
||||
|
||||
$folio = $request->input('folio');
|
||||
$tagNumber = $request->input('tag_number');
|
||||
$vin = $request->input('vin');
|
||||
|
||||
// Validar que se proporcione al menos un criterio de búsqueda
|
||||
if (!$folio && !$tagNumber && !$vin) {
|
||||
return ApiResponse::BAD_REQUEST->response([
|
||||
'message' => 'Debe proporcionar al menos un criterio de búsqueda: folio y tag_number, o vin',
|
||||
]);
|
||||
}
|
||||
$query = Record::with(['vehicle.owner', 'vehicle.tag']);
|
||||
|
||||
// Búsqueda por folio ó tag_number
|
||||
if ($folio && !$tagNumber) {
|
||||
$query->where('folio', $folio);
|
||||
} elseif (!$folio && $tagNumber) {
|
||||
$query->whereHas('vehicle.tag', function ($q) use ($tagNumber) {
|
||||
$q->where('tag_number', $tagNumber);
|
||||
});
|
||||
} // Búsqueda solo por VIN
|
||||
elseif ($vin) {
|
||||
$query->whereHas('vehicle', function ($q) use ($vin) {
|
||||
$q->where('niv', $vin);
|
||||
});
|
||||
}
|
||||
|
||||
$record = $query->first();
|
||||
|
||||
if (!$record) {
|
||||
return ApiResponse::NOT_FOUND->response([
|
||||
'stolen' => false,
|
||||
'message' => 'No se encontró ningún registro con los criterios proporcionados',
|
||||
]);
|
||||
}
|
||||
|
||||
return ApiResponse::OK->response([
|
||||
'stolen' => true,
|
||||
'vehicle' => [
|
||||
'folio' => $record->folio,
|
||||
'placa' => $record->vehicle->placa,
|
||||
'tag_number' => $record->vehicle->tag?->tag_number,
|
||||
'vin' => $record->vehicle->niv,
|
||||
'marca' => $record->vehicle->marca,
|
||||
'modelo' => $record->vehicle->linea,
|
||||
'año' => $record->vehicle->modelo,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function searchRecord(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
|
||||
@ -280,6 +280,45 @@ public function pdfCancelledTag(Tag $tag)
|
||||
}
|
||||
}
|
||||
|
||||
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 = [
|
||||
@ -347,6 +386,57 @@ private function cancellationData(Tag $tag)
|
||||
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
|
||||
*/
|
||||
|
||||
@ -167,25 +167,83 @@ private function parseVehicleResponse(string $soapResponse, string $niv)
|
||||
/**
|
||||
* Verifica si un vehículo está reportado como robado
|
||||
*/
|
||||
public function verificarRobo(string $niv)
|
||||
public function verificarRobo(string $niv): bool
|
||||
{
|
||||
try {
|
||||
$resultado = $this->consultarPadron($niv);
|
||||
$url = $this->baseUrl . $this->roboEndpoint;
|
||||
|
||||
if ($resultado['has_error']) {
|
||||
$arg2 = $niv . str_repeat('|', 8);
|
||||
|
||||
$soapBody = <<<XML
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://consultaRpv.org/wsdl">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<wsdl:doConsRepRobo>
|
||||
<arg0>{$this->username}</arg0>
|
||||
<arg1>{$this->password}</arg1>
|
||||
<arg2>{$arg2}</arg2>
|
||||
</wsdl:doConsRepRobo>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
XML;
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $soapBody);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: text/xml; charset=utf-8',
|
||||
'SOAPAction: "doConsRepRobo"',
|
||||
'Content-Length: ' . strlen($soapBody),
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
// Si hay error de conexión, asumir que NO está robado
|
||||
if ($error) {
|
||||
logger()->error('REPUVE verificarRobo: Error de conexión', [
|
||||
'error' => $error,
|
||||
'niv' => $niv,
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
$estatus = $resultado['repuve_response']['estatus_registro'] ?? null;
|
||||
// Si hay error HTTP, asumir que NO está robado
|
||||
if ($httpCode !== 200) {
|
||||
logger()->error('REPUVE verificarRobo: HTTP error', [
|
||||
'http_code' => $httpCode,
|
||||
'niv' => $niv,
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parsear respuesta
|
||||
$resultado = $this->parseRoboResponse($response, $niv);
|
||||
|
||||
// Si hubo error al parsear, asumir que NO está robado
|
||||
if ($resultado['has_error'] ?? true) {
|
||||
logger()->warning('REPUVE verificarRobo: Error al parsear respuesta', [
|
||||
'niv' => $niv,
|
||||
'error' => $resultado['error_message'] ?? 'Desconocido',
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retornar si está robado o no
|
||||
return $resultado['is_robado'] ?? false;
|
||||
|
||||
return in_array(strtoupper($estatus), ['ROBADO', 'ROBO']);
|
||||
} catch (Exception $e) {
|
||||
logger()->error("Error al verificar robo en REPUVE: " . $e->getMessage());
|
||||
logger()->error('REPUVE verificarRobo: Excepción capturada', [
|
||||
'niv' => $niv,
|
||||
'exception' => $e->getMessage(),
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function inscribirVehiculo(array $datos)
|
||||
{
|
||||
$url = $this->baseUrl . $this->inscripcionEndpoint;
|
||||
@ -389,4 +447,99 @@ private function parsearRespuestaInscripcion(string $soapResponse)
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function parseRoboResponse(string $soapResponse, string $valor): array
|
||||
{
|
||||
// Extraer contenido del tag <return>
|
||||
preg_match('/<return>(.*?)<\/return>/s', $soapResponse, $matches);
|
||||
|
||||
if (!isset($matches[1])) {
|
||||
logger()->error('REPUVE parseRoboResponse: No se encontró tag <return>', [
|
||||
'soap_response' => substr($soapResponse, 0, 500),
|
||||
'valor' => $valor,
|
||||
]);
|
||||
|
||||
return [
|
||||
'has_error' => true,
|
||||
'is_robado' => false,
|
||||
'error_message' => 'Respuesta SOAP inválida',
|
||||
];
|
||||
}
|
||||
|
||||
$contenido = trim($matches[1]);
|
||||
|
||||
// Verificar si hay error de REPUVE Nacional (ERR: o ERROR:)
|
||||
if (preg_match('/(ERR|ERROR|err|error):(-?\d+)/i', $contenido, $errorMatch)) {
|
||||
$errorCode = $errorMatch[2];
|
||||
|
||||
logger()->warning('REPUVE parseRoboResponse: Servicio retornó error', [
|
||||
'error_code' => $errorCode,
|
||||
'contenido' => $contenido,
|
||||
'valor' => $valor,
|
||||
]);
|
||||
|
||||
return [
|
||||
'has_error' => true,
|
||||
'is_robado' => false,
|
||||
'error_code' => $errorCode,
|
||||
'error_message' => "Error REPUVE código {$errorCode}",
|
||||
'raw_response' => $contenido,
|
||||
];
|
||||
}
|
||||
|
||||
// Si empieza con OK:, parsear los datos de robo
|
||||
if (str_starts_with($contenido, 'OK:')) {
|
||||
$datos = str_replace('OK:', '', $contenido);
|
||||
$valores = explode('|', $datos);
|
||||
|
||||
// 1 = robado, 0 = no robado
|
||||
$indicador = $valores[0] ?? '0';
|
||||
$isRobado = ($indicador === '1');
|
||||
|
||||
$roboData = [
|
||||
'has_error' => false,
|
||||
'is_robado' => $isRobado,
|
||||
'indicador' => $indicador,
|
||||
'fecha_robo' => $valores[1] ?? null,
|
||||
'placa' => $valores[2] ?? null,
|
||||
'niv' => $valores[3] ?? null,
|
||||
'autoridad' => $valores[4] ?? null,
|
||||
'acta' => $valores[5] ?? null,
|
||||
'denunciante' => $valores[6] ?? null,
|
||||
'fecha_acta' => $valores[7] ?? null,
|
||||
'raw_response' => $contenido,
|
||||
];
|
||||
|
||||
// Log importante si está robado
|
||||
if ($isRobado) {
|
||||
logger()->warning('REPUVE: Vehículo reportado como ROBADO', [
|
||||
'valor_buscado' => $valor,
|
||||
'niv' => $roboData['niv'],
|
||||
'placa' => $roboData['placa'],
|
||||
'autoridad' => $roboData['autoridad'],
|
||||
'acta' => $roboData['acta'],
|
||||
'denunciante' => $roboData['denunciante'],
|
||||
]);
|
||||
} else {
|
||||
logger()->info('REPUVE: Vehículo NO reportado como robado', [
|
||||
'valor_buscado' => $valor,
|
||||
]);
|
||||
}
|
||||
|
||||
return $roboData;
|
||||
}
|
||||
|
||||
// Si no tiene formato reconocido
|
||||
logger()->error('REPUVE parseRoboResponse: Formato desconocido', [
|
||||
'contenido' => $contenido,
|
||||
'valor' => $valor,
|
||||
]);
|
||||
|
||||
return [
|
||||
'has_error' => true,
|
||||
'is_robado' => false,
|
||||
'error_message' => 'Formato de respuesta no reconocido',
|
||||
'raw_response' => $contenido,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
155
resources/views/pdfs/tag_sustitution.blade.php
Normal file
155
resources/views/pdfs/tag_sustitution.blade.php
Normal file
@ -0,0 +1,155 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Constancia Sustituida - REPUVE Tabasco</title> <!-- CAMBIO LÍNEA 5 -->
|
||||
<style>
|
||||
@page {
|
||||
margin: 2cm 1.5cm;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 10pt;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
border: 2px solid #000;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
background-color: #f0f0f0;
|
||||
color: #000;
|
||||
padding: 8px;
|
||||
font-weight: bold;
|
||||
font-size: 14pt;
|
||||
margin: 0;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
.subheader {
|
||||
text-align: center;
|
||||
background-color: #f0f0f0;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
font-size: 11pt;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
.space-box {
|
||||
height: 200px;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
font-weight: bold;
|
||||
font-size: 16pt;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
|
||||
.space-box-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
background-color: #f0f0f0;
|
||||
font-weight: bold;
|
||||
padding: 8px 10px;
|
||||
width: 25%;
|
||||
border-right: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
font-size: 9pt;
|
||||
}
|
||||
|
||||
.data-value {
|
||||
padding: 8px 10px;
|
||||
width: 75%;
|
||||
border-bottom: 1px solid #000;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.data-row:last-child .data-label,
|
||||
.data-row:last-child .data-value {
|
||||
border-bottom: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Header -->
|
||||
<div class="header">
|
||||
REPUVE TABASCO
|
||||
</div>
|
||||
|
||||
<!-- Subheader -->
|
||||
<div class="subheader">
|
||||
CONSTANCIA SUSTITUIDA
|
||||
</div>
|
||||
|
||||
<div class="space-box">
|
||||
<div class="space-box-text">
|
||||
PEGAR CONSTANCIA
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabla de datos -->
|
||||
<table class="data-table">
|
||||
<tr class="data-row">
|
||||
<td class="data-label">FECHA:</td>
|
||||
<td class="data-value">{{ $substitution['fecha'] }}</td>
|
||||
</tr>
|
||||
<tr class="data-row">
|
||||
<td class="data-label">FOLIO:</td>
|
||||
<td class="data-value">{{ $substitution['folio'] ?? '' }}</td>
|
||||
</tr>
|
||||
<tr class="data-row">
|
||||
<td class="data-label">ID CHIP:</td>
|
||||
<td class="data-value">{{ $substitution['id_chip'] ?? '' }}</td>
|
||||
</tr>
|
||||
<tr class="data-row">
|
||||
<td class="data-label">FOLIO QUE SUSTITUYE:</td>
|
||||
<td class="data-value">{{ $substitution['folio_sustituto'] ?? '' }}</td>
|
||||
</tr>
|
||||
<tr class="data-row">
|
||||
<td class="data-label">PLACAS:</td>
|
||||
<td class="data-value">{{ $substitution['placa'] ?? '' }}</td>
|
||||
</tr>
|
||||
<tr class="data-row">
|
||||
<td class="data-label">VIN:</td>
|
||||
<td class="data-value">{{ $substitution['niv'] ?? '' }}</td>
|
||||
</tr>
|
||||
<tr class="data-row">
|
||||
<td class="data-label">MOTIVO:</td>
|
||||
<td class="data-value">{{ strtoupper($substitution['motivo'] ?? '') }}</td>
|
||||
</tr>
|
||||
<tr class="data-row">
|
||||
<td class="data-label">OPERADOR:</td>
|
||||
<td class="data-value">{{ strtoupper($substitution['operador'] ?? '') }}</td>
|
||||
</tr>
|
||||
<tr class="data-row">
|
||||
<td class="data-label">MÓDULO:</td>
|
||||
<td class="data-value">{{ $substitution['modulo']}}</td>
|
||||
</tr>
|
||||
<tr class="data-row">
|
||||
<td class="data-label">UBICACIÓN:</td>
|
||||
<td class="data-value">{{ $substitution['ubicacion'] }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -42,6 +42,7 @@
|
||||
Route::get('expediente/{id}/pdfConstancia', [RecordController::class, 'generatePdfConstancia']);
|
||||
Route::get('expediente/{id}/pdfImagenes', [RecordController::class, 'generatePdfImages']);
|
||||
Route::get('tags/{tag}/pdfTag-cancelado', [RecordController::class, 'pdfCancelledTag']);
|
||||
Route::get('tags/{tag}/pdfTag-sustituido', [RecordController::class, 'pdfSubstitutedTag']);
|
||||
Route::get('expediente/{id}/pdfFormulario', [RecordController::class, 'generatePdfForm']);
|
||||
Route::get('RecordErrors', [RecordController::class, 'errors']);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user