feat: Add module_id to records and telefono to owners, implement PDF form generation, and update PDF views for better layout and data presentation.

This commit is contained in:
Juan Felipe Zapata Moreno 2025-11-25 22:09:37 -06:00
parent 90f943291e
commit dfb60806cb
11 changed files with 575 additions and 230 deletions

View File

@ -133,6 +133,7 @@ public function vehicleInscription(VehicleStoreRequest $request)
'folio' => $folio,
'vehicle_id' => $vehicle->id,
'user_id' => Auth::id(),
'module_id' => Auth::user()->module_id,
]);
// Procesar archivos

View File

@ -14,7 +14,7 @@ class RecordController extends Controller
{
public function generatePdf($id)
{
$record = Record::with('vehicle')->findOrFail($id);
$record = Record::with('vehicle.owner', 'user', 'module')->findOrFail($id);
$pdf = Pdf::loadView('pdfs.record', compact('record'))
->setPaper('a4', 'portrait')
@ -179,6 +179,47 @@ public function generatePdfImages($id)
}
}
public function generatePdfForm(Request $request)
{
$request->validate([
'fecha' => 'nullable|string',
'mes' => 'nullable|string',
'anio' => 'nullable|string',
'marca' => 'required|string',
'linea' => 'required|string',
'modelo' => 'required|string',
'niv' => 'required|string',
'numero_motor' => 'required|string',
'placa' => 'required|string',
'folio' => 'required|string',
'telefono' => 'nullable|string',
]);
$data = [
'fecha' => $request->input('fecha', ''),
'mes' => $request->input('mes', ''),
'anio' => $request->input('anio', ''),
'marca' => $request->input('marca'),
'linea' => $request->input('linea'),
'modelo' => $request->input('modelo'),
'niv' => $request->input('niv'),
'numero_motor' => $request->input('numero_motor'),
'placa' => $request->input('placa'),
'folio' => $request->input('folio'),
'telefono' => $request->input('telefono', ''),
];
$pdf = Pdf::loadView('pdfs.form', $data)
->setPaper('a4', 'portrait')
->setOptions([
'defaultFont' => 'sans-serif',
'isHtml5ParserEnabled' => true,
'isRemoteEnabled' => true,
]);
return $pdf->stream('solicitud-sustitucion-' . time() . '.pdf');
}
public function errors(Request $request)
{
$request->validate([

View File

@ -27,6 +27,7 @@ class Owner extends Model
'num_int',
'colonia',
'cp',
'telefono',
];
protected $appends = [

View File

@ -13,6 +13,7 @@ class Record extends Model
'folio',
'vehicle_id',
'user_id',
'module_id',
'error_id',
'api_response',
'error_occurred_at',
@ -42,4 +43,9 @@ public function error()
{
return $this->belongsTo(Error::class);
}
public function module()
{
return $this->belongsTo(Module::class);
}
}

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('records', function (Blueprint $table) {
$table->foreignId('module_id')
->nullable()
->after('user_id')
->constrained('modules')
->nullOnDelete();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('records', function (Blueprint $table) {
$table->dropForeign(['module_id']);
$table->dropColumn('module_id');
});
}
};

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('owners', function (Blueprint $table) {
$table->string('telefono')->nullable()->after('cp');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('owners', function (Blueprint $table) {
$table->dropColumn('telefono');
});
}
};

View File

@ -6,174 +6,192 @@
<title>Etiquetas Vehiculares</title>
<style>
@page {
margin: 0.5cm;
size: landscape;
margin: 0;
size: portrait;
}
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 9pt;
color: #000;
margin: 0;
padding: 0;
}
.container {
width: 78%;
position: relative;
width: 210mm;
height: 297mm;
}
.label-row {
width: 100%;
margin-bottom: 20px;
/* COLUMNA IZQUIERDA */
.left-col-vin {
position: absolute;
left: 20mm;
top: 40mm;
}
.label {
width: 100%;
padding: 15px;
margin-bottom: 20px;
.left-col-placa {
position: absolute;
left: 20mm;
top: 65mm;
}
.label-content {
border: 1px solid #000;
padding: 10px;
text-align: center;
.left-col-marca {
position: absolute;
left: 20mm;
top: 72mm;
}
.barcode-container {
margin: 10px 0;
.left-col-linea-modelo {
position: absolute;
left: 20mm;
top: 79mm;
width: 70mm;
}
.vehicle-info {
margin-top: 10px;
text-align: left;
.left-col-linea {
display: inline-block;
width: 40mm;
}
.info-row {
display: table;
width: 100%;
margin-bottom: 3px;
.left-col-modelo {
display: inline-block;
text-align: right;
width: 25mm;
}
.info-label {
display: table-cell;
font-weight: bold;
width: 40%;
padding-right: 5px;
.left-col-propietario {
position: absolute;
left: 20mm;
top: 86mm;
}
.info-value {
display: table-cell;
width: 60%;
}
.owner-name {
margin-top: 8px;
.left-col-direccion {
position: absolute;
left: 20mm;
top: 93mm;
font-size: 8pt;
text-align: center;
font-weight: bold;
}
.address {
margin-top: 5px;
font-size: 7pt;
text-align: center;
color: #333;
.left-col-municipio {
position: absolute;
left: 20mm;
top: 100mm;
}
.barcode-text {
font-size: 10pt;
font-weight: bold;
margin-top: 3px;
/* COLUMNA DERECHA */
.right-col-vin {
position: absolute;
left: 115mm;
top: 40mm;
}
.right-col-placa {
position: absolute;
left: 115mm;
top: 65mm;
}
.right-col-marca {
position: absolute;
left: 115mm;
top: 72mm;
}
.right-col-linea-modelo {
position: absolute;
left: 115mm;
top: 79mm;
width: 70mm;
}
.right-col-linea {
display: inline-block;
width: 40mm;
}
.right-col-modelo {
display: inline-block;
text-align: right;
width: 25mm;
}
.right-col-particular {
position: absolute;
left: 115mm;
top: 86mm;
}
</style>
</head>
<body>
<div class="container">
<!-- Etiqueta 1 - Código de Barras -->
<div class="label">
<div class="label-content">
<!-- Código de barras del NIV -->
<div class="barcode-container">
{!! DNS1D::getBarcodeHTML($record->vehicle->niv, 'C128', 2, 60) !!}
<div class="barcode-text">
{{ $record->vehicle->niv }}
</div>
</div>
<!-- ============= COLUMNA IZQUIERDA ============= -->
<!-- Información del vehículo -->
<div class="vehicle-info">
<div class="info-row">
<div class="info-label"></div>
<div class="info-value">{{ $record->vehicle->placa }}</div>
</div>
<div class="info-row">
<div class="info-label">{{ strtoupper($record->vehicle->marca) }}</div>
<div class="info-value"></div>
</div>
<div class="info-row">
<div class="info-label">{{ strtoupper($record->vehicle->linea) }}</div>
<div class="info-value">{{ $record->vehicle->modelo }}</div>
</div>
<div class="info-row">
<div class="info-label">{{ strtoupper($record->vehicle->tipo) }}</div>
<div class="info-value"></div>
</div>
</div>
<!-- Propietario -->
<div class="owner-name">
{{ strtoupper($record->vehicle->owner->full_name) }}
</div>
<!-- Dirección -->
<div class="address">
{{ strtoupper($record->vehicle->owner->address) }}
</div>
</div>
<!-- VIN -->
<div class="left-col-vin ">
{{ $record->vehicle->niv }}
</div>
<!-- Etiqueta 2 - Código de Barras (duplicado) -->
<div class="label">
<div class="label-content">
<!-- Código de barras del NIV -->
<div class="barcode-container">
{!! DNS1D::getBarcodeHTML($record->vehicle->niv, 'C128', 2, 60) !!}
<div class="barcode-text">
{{ $record->vehicle->niv }}
</div>
</div>
<!-- Placa -->
<div class="left-col-placa ">
{{ strtoupper($record->vehicle->placa) }}
</div>
<!-- Información del vehículo -->
<div class="vehicle-info">
<div class="info-row">
<div class="info-label"></div>
<div class="info-value">{{ $record->vehicle->placa }}</div>
</div>
<div class="info-row">
<div class="info-label">{{ strtoupper($record->vehicle->marca) }}</div>
<div class="info-value"></div>
</div>
<div class="info-row">
<div class="info-label">{{ strtoupper($record->vehicle->linea) }}</div>
<div class="info-value">{{ $record->vehicle->modelo }}</div>
</div>
<div class="info-row">
<div class="info-label">{{ strtoupper($record->vehicle->tipo_veh) }}</div>
<div class="info-value"></div>
</div>
</div>
<!-- Marca -->
<div class="left-col-marca">
{{ strtoupper($record->vehicle->marca) }}
</div>
<!-- Propietario -->
<div class="owner-name">
{{ strtoupper($record->vehicle->owner->full_name) }}
</div>
<!-- Línea y Modelo -->
<div class="left-col-linea-modelo">
<span class="left-col-linea">{{ strtoupper($record->vehicle->linea) }}</span>
<span class="left-col-modelo">{{ $record->vehicle->modelo }}</span>
</div>
<!-- Dirección -->
<div class="address">
{{ strtoupper($record->vehicle->owner->address) }}
</div>
</div>
<!-- Propietario -->
<div class="left-col-propietario ">
{{ strtoupper($record->vehicle->owner->full_name) }}
</div>
<!-- Dirección -->
<div class="left-col-direccion">
{{ strtoupper($record->vehicle->owner->callep ?? '') }}
{{ $record->vehicle->owner->num_ext }}
</div>
<!-- Municipio -->
<div class="left-col-municipio ">
{{ strtoupper($record->vehicle->owner->colonia ?? '') }}
</div>
<!-- ============= COLUMNA DERECHA ============= -->
<!-- VIN -->
<div class="right-col-vin ">
{{ $record->vehicle->niv }}
</div>
<!-- Placa -->
<div class="right-col-placa ">
{{ strtoupper($record->vehicle->placa) }}
</div>
<!-- Marca -->
<div class="right-col-marca">
{{ strtoupper($record->vehicle->marca) }}
</div>
<!-- Línea y Modelo -->
<div class="right-col-linea-modelo">
<span class="right-col-linea">{{ strtoupper($record->vehicle->linea) }}</span>
<span class="right-col-modelo">{{ $record->vehicle->modelo }}</span>
</div>
<!-- PARTICULAR -->
<div class="right-col-particular ">
{{ strtoupper($record->vehicle->tipo_veh) }}
</div>
</div>
</body>

View File

@ -0,0 +1,287 @@
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Solicitud de Sustitución de Constancia de Inscripción</title>
<style>
@page {
margin: 1.5cm;
}
body {
font-family: Arial, Helvetica, sans-serif;
font-size: 11pt;
line-height: 1.4;
color: #000;
margin: 0;
padding: 0;
}
.container {
width: 100%;
margin: 0 auto;
}
.header {
width: 100%;
border-bottom: 1px solid #000;
padding-bottom: 10px;
margin-bottom: 15px;
}
.header-table {
width: 100%;
border-collapse: collapse;
}
.header-left {
width: 40%;
vertical-align: middle;
}
.header-center {
width: 20%;
vertical-align: middle;
}
.header-right {
width: 40%;
text-align: right;
vertical-align: middle;
font-size: 9pt;
}
.header-right strong {
font-size: 11pt;
display: block;
}
.logo {
max-height: 50px;
width: auto;
}
.date-line {
text-align: right;
font-size: 10pt;
margin-bottom: 15px;
}
.date-blank {
display: inline-block;
border-bottom: 1px solid #000;
min-width: 50px;
text-align: center;
}
.title {
text-align: center;
font-weight: bold;
font-size: 12pt;
margin: 15px 0;
}
.department {
font-weight: bold;
font-size: 10pt;
margin-bottom: 2px;
}
.presente {
font-weight: bold;
font-size: 10pt;
letter-spacing: 3px;
margin-bottom: 15px;
}
.intro-text {
text-align: justify;
font-size: 10pt;
line-height: 1.5;
margin-bottom: 12px;
}
.data-row {
margin-bottom: 7px;
font-size: 10pt;
}
.data-label {
font-weight: bold;
display: inline;
}
.data-underline {
display: inline-block;
border-bottom: 1px solid #000;
min-width: 150px;
}
.data-underline-long {
display: inline-block;
border-bottom: 1px solid #000;
min-width: 300px;
}
.section-title {
text-align: center;
font-weight: bold;
font-size: 10pt;
margin: 20px 0 10px 0;
}
.motivo-lines {
margin: 10px 0;
}
.motivo-line {
border-bottom: 1px solid #000;
height: 15px;
margin: 5px 0;
}
.signature-section {
margin-top: 30px;
text-align: center;
}
.signature-line {
display: inline-block;
width: 250px;
border-top: 1px solid #000;
padding-top: 5px;
margin-top: 25px;
}
.signature-text {
font-size: 10pt;
}
.telefono-row {
margin-top: 10px;
font-size: 10pt;
}
.telefono-label {
font-weight: bold;
}
.telefono-underline {
display: inline-block;
border-bottom: 1px solid #000;
min-width: 100px;
}
</style>
</head>
<body>
<div class="container">
<!-- Header -->
<div class="header">
<table class="header-table">
<tr>
<td class="header-left">
<?php
$imagePath = resource_path('images/logo-seguridad.png');
$imageData = base64_encode(file_get_contents($imagePath));
$imageSrc = 'data:image/png;base64,' . $imageData;
?>
<img src="{{ $imageSrc }}" alt="Logo" class="logo">
</td>
<td class="header-center"></td>
<td class="header-right">
<strong>SEGURIDAD</strong>
SECRETARÍA DE SEGURIDAD<br>Y PROTECCIÓN CIUDADANA
</td>
</tr>
</table>
</div>
<!-- Fecha -->
<div class="date-line">
<span class="date-blank">{{ $fecha ?? '' }}</span> de
<span class="date-blank" style="min-width: 90px;">{{ $mes ?? '' }}</span> del 20<span class="date-blank" style="min-width: 30px;">{{ $anio ?? '' }}</span>
</div>
<!-- Título -->
<div class="title">
Solicitud de Sustitución de Constancia de Inscripción
</div>
<!-- Destinatario -->
<div class="department">
Departamento del REPUVE del Estado de Tabasco
</div>
<div class="presente">
P R E S E N T E
</div>
<!-- Texto introductorio -->
<div class="intro-text">
Por este medio me permito solicitar el cambio de constancia de inscripción a mi vehículo
con los siguientes datos:
</div>
<!-- Datos del vehículo -->
<div class="data-row">
<span class="data-label">Marca: </span><span class="data-underline">{{ strtoupper($marca ?? '') }}</span>
</div>
<div class="data-row">
<span class="data-label">Modelo: </span><span class="data-underline">{{ strtoupper($linea ?? '') }}</span>
</div>
<div class="data-row">
<span class="data-label">Año Modelo: </span><span class="data-underline">{{ $modelo ?? '' }}</span>
</div>
<div class="data-row">
<span class="data-label">Número de Identificación (NIV): </span><span class="data-underline-long">{{ strtoupper($niv ?? '') }}</span>
</div>
<div class="data-row">
<span class="data-label">Número de Motor: </span><span class="data-underline-long">{{ strtoupper($numero_motor ?? '') }}</span>
</div>
<div class="data-row">
<span class="data-label">Placas: </span><span class="data-underline">{{ strtoupper($placa ?? '') }}</span>
</div>
<div class="data-row">
<span class="data-label">Folio de Constancia de Inscripción: </span><span class="data-underline">{{ strtoupper($folio ?? '') }}</span>
</div>
<!-- Sección MOTIVO -->
<div class="section-title">
MOTIVO
</div>
<div class="motivo-lines">
<div class="motivo-line"></div>
<div class="motivo-line"></div>
<div class="motivo-line"></div>
<div class="motivo-line"></div>
<div class="motivo-line"></div>
</div>
<!-- Sección PROPIETARIO -->
<div class="section-title">
PROPIETARIO
</div>
<!-- Firma -->
<div class="signature-section">
<div class="signature-line">
<div class="signature-text">Nombre y firma</div>
</div>
</div>
<!-- Teléfono -->
<div class="telefono-row">
<span class="telefono-label">Teléfono:</span><span class="telefono-underline">{{ $telefono ?? '' }}</span>
</div>
</div>
</body>
</html>

View File

@ -94,28 +94,18 @@
}
.owner-section {
margin: 25px 0;
border: 1px solid #000;
padding: 15px;
}
.owner-section h3 {
font-size: 10pt;
font-weight: bold;
margin-bottom: 12px;
border-bottom: 1px solid #000;
padding-bottom: 5px;
margin: 15px 0;
}
.data-row {
margin-bottom: 10px;
margin-bottom: 9px;
font-size: 10pt;
}
.data-label {
font-weight: bold;
display: inline-block;
width: 100px;
min-width: 100px;
}
.address-row {
@ -123,19 +113,16 @@
font-size: 10pt;
}
.date-section {
text-align: center;
margin: 40px 0 80px 0;
font-size: 10pt;
}
.date-section .underline {
text-decoration: underline;
padding: 0 5px;
}
.signatures {
margin-top: 100px;
margin-top: 60px;
}
.footer-registro {
text-align: center;
margin-top: 15px;
color: red;
font-weight: bold;
font-size: 9pt;
}
.signature-table {
@ -210,42 +197,37 @@
<!-- Datos del propietario -->
<div class="owner-section">
<h3>DATOS DE PROPIETARIO</h3>
<div class="data-row">
<span class="data-label">RFC:</span>
<span>{{ $record->vehicle->owner->rfc }}</span>
<span>{{ $record->vehicle->owner->rfc ?? '' }}</span>
</div>
<div class="data-row">
<span class="data-label">NOMBRE:</span>
<span>{{$record->vehicle->owner->full_name }} </span>
<span>{{ $record->vehicle->owner->full_name ?? '' }}</span>
</div>
<div class="address-row">
<div class="data-row">
<span class="data-label">CALLE:</span>
<span>{{ $record->vehicle->owner->address }}</span>
<span style="display: inline-block; min-width: 200px;">{{ $record->vehicle->owner->callep ?? '' }}</span>
<span style="display: inline-block; margin-left: 20px;"><strong>No Ext:</strong> {{ $record->vehicle->owner->num_ext ?? '' }}</span>
<span style="display: inline-block; margin-left: 20px;"><strong>No Int:</strong> {{ $record->vehicle->owner->num_int ?? '' }}</span>
</div>
<div class="data-row">
<span class="data-label">COLONIA:</span>
<span>{{ $record->vehicle->owner->address }}</span>
<span>{{ $record->vehicle->owner->colonia ?? '' }}</span>
</div>
<div class="data-row">
<span class="data-label">MUNICIPIO:</span>
<span>{{ $record->vehicle->municipio }}</span>
<span>{{ $record->vehicle->owner->munic ?? '' }}</span>
</div>
</div>
<!-- Fecha y lugar -->
<div class="date-section">
<p>
VILLAHERMOSA, TAB
<span class="underline">{{ now()->format('d') }}</span> de
<span class="underline">{{ ucfirst(now()->translatedFormat('F')) }}</span> de
<span class="underline">{{ now()->format('Y') }}</span>
</p>
<div class="data-row">
<span class="data-label">NUMERO TELEFONICO:</span>
<span>{{ $record->vehicle->owner->telefono ?? '' }}</span>
</div>
</div>
<!-- Firmas -->
@ -254,17 +236,20 @@
<tr>
<td class="signature-cell">
<div class="signature-line"></div>
<p class="signature-name">{{ $record->vehicle->owner->full_name }}</p>
<p class="signature-role">Propietario</p>
<p class="signature-role">PROPIETARIO</p>
</td>
<td class="signature-cell">
<div class="signature-line"></div>
<p class="signature-name">{{ $record->user->full_name }}</p>
<p class="signature-role">Operador</p>
<p class="signature-role">OPERADOR</p>
</td>
</tr>
</table>
</div>
<!-- Footer - Registro realizado en -->
<div class="footer-registro">
<p>REGISTRO REALIZADO EN {{ strtoupper($record->module->name ?? 'N/A') }}</p>
</div>
</div>
</body>
</html>

View File

@ -35,25 +35,6 @@
padding: 2px 0;
}
.vehicle-info {
margin-bottom: 10px;
}
.info-row {
display: table;
width: 100%;
margin-bottom: 5px;
}
.info-cell {
display: table-cell;
width: 50%;
padding: 2px 5px;
}
.info-label {
font-weight: bold;
}
.signature-section {
position: absolute;
@ -62,6 +43,7 @@
width: 180px;
text-align: center;
padding: 1px;
margin-bottom: 50px;
}
.signature-line {
@ -78,7 +60,7 @@
table {
width: 100%;
border-collapse: collapse;
margin-top: 1px;
margin-top: 86px;
}
table th {
@ -132,44 +114,6 @@
<div class="header">
<h1>HOJA DE VERIFICACION VEHICULAR</h1>
</div>
<!-- Información del vehículo -->
<div class="vehicle-info">
<div class="info-row">
<div class="info-cell">
<span class="info-label">Placa:</span> {{ $record->vehicle->placa }}
</div>
<div class="info-cell">
<span class="info-label">NIV:</span> {{ $record->vehicle->niv }}
</div>
</div>
<div class="info-row">
<div class="info-cell">
<span class="info-label">Marca:</span> {{ strtoupper($record->vehicle->marca) }}
</div>
<div class="info-cell">
<span class="info-label">Sub Marca:</span> {{ strtoupper($record->vehicle->linea) }}
</div>
</div>
<div class="info-row">
<div class="info-cell">
<span class="info-label">Modelo:</span> {{ $record->vehicle->modelo }}
</div>
<div class="info-cell">
<span class="info-label">Tipo:</span> {{ strtoupper($record->vehicle->tipo_servicio) }}
</div>
</div>
<div class="info-row">
<div class="info-cell"></div>
<div class="info-cell">
<span class="info-label">Tipo Vehi:</span> {{ strtoupper($record->vehicle->tipo_veh) }}
</div>
</div>
</div>
<!-- Firma del operador -->
<div class="signature-section">
<div class="signature-line"></div>

View File

@ -38,6 +38,7 @@
Route::get('expediente/{id}/pdfVerificacion', [RecordController::class, 'generatePdfVerification']);
Route::get('expediente/{id}/pdfConstancia', [RecordController::class, 'generatePdfConstancia']);
Route::get('expediente/{id}/pdfImagenes', [RecordController::class, 'generatePdfImages']);
Route::post('expediente/pdfFormulario', [RecordController::class, 'generatePdfForm']);
Route::get('RecordErrors', [RecordController::class, 'errors']);
//Rutas de Actualización