FEAT: Creacion de RecordController, FileStoreRequest, actualiza modelos y controladores, renombra VehicleRequest, y ajusta configuración y vistas para gestión de expedientes
This commit is contained in:
parent
71788ddd2e
commit
de0ac7a3aa
16
.env.example
16
.env.example
@ -36,22 +36,6 @@ DB_USERNAME=notsoweb
|
|||||||
DB_PASSWORD=
|
DB_PASSWORD=
|
||||||
PMA_PORT=8081 # Puerto para phpMyAdmin
|
PMA_PORT=8081 # Puerto para phpMyAdmin
|
||||||
|
|
||||||
# MongoDB Atlas Configuración nube
|
|
||||||
MONGO_DSN="mongodb+srv://"
|
|
||||||
MONGO_DATABASE=repuve
|
|
||||||
MONGO_SSL=true
|
|
||||||
MONGO_AUTH_SOURCE=admin
|
|
||||||
MONGO_RETRY_WRITES=true
|
|
||||||
MONGO_W=majority
|
|
||||||
|
|
||||||
# MongoDB Local
|
|
||||||
MONGO_HOST=mongodb
|
|
||||||
MONGO_PORT=27017
|
|
||||||
MONGO_DATABASE=holos
|
|
||||||
MONGO_USERNAME=root
|
|
||||||
MONGO_PASSWORD=secret
|
|
||||||
|
|
||||||
REDIS_PORT=6379 # Puerto para Redis
|
|
||||||
NGINX_PORT=8080 # Puerto para Nginx
|
NGINX_PORT=8080 # Puerto para Nginx
|
||||||
|
|
||||||
SESSION_DRIVER=database
|
SESSION_DRIVER=database
|
||||||
|
|||||||
117
app/Http/Controllers/Repuve/RecordController.php
Normal file
117
app/Http/Controllers/Repuve/RecordController.php
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Repuve;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Barryvdh\DomPDF\Facade\Pdf;
|
||||||
|
use App\Http\Requests\Repuve\FileStoreRequest;
|
||||||
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use App\Models\Record;
|
||||||
|
use App\Models\File;
|
||||||
|
|
||||||
|
class RecordController extends Controller
|
||||||
|
{
|
||||||
|
public function generatePdf($id)
|
||||||
|
{
|
||||||
|
$record = Record::with('vehicle')->findOrFail($id);
|
||||||
|
|
||||||
|
$pdf = Pdf::loadView('pdfs.record', compact('record'))
|
||||||
|
->setPaper('a4', 'portrait')
|
||||||
|
->setOptions([
|
||||||
|
'defaultFont' => 'sans-serif',
|
||||||
|
'isHtml5ParserEnabled' => true,
|
||||||
|
'isRemoteEnabled' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $pdf->stream('record-' . $id . '.pdf');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uploadFile(FileStoreRequest $request){
|
||||||
|
try {
|
||||||
|
// Verificar que existe el record
|
||||||
|
$record = Record::findOrFail($request->record_id);
|
||||||
|
|
||||||
|
// Obtener el archivo
|
||||||
|
$uploadedFile = $request->file('file');
|
||||||
|
|
||||||
|
// Generar nombre único
|
||||||
|
$fileName = time() . '_' . $uploadedFile->getClientOriginalName();
|
||||||
|
|
||||||
|
// Guardar en storage
|
||||||
|
$path = $uploadedFile->storeAs('records', $fileName, 'public');
|
||||||
|
|
||||||
|
// Calcular MD5 del archivo
|
||||||
|
$md5 = md5_file($uploadedFile->getRealPath());
|
||||||
|
|
||||||
|
// Crear registro en la tabla files
|
||||||
|
$file = File::create([
|
||||||
|
'name' => $request->name,
|
||||||
|
'path' => $path,
|
||||||
|
'md5' => $md5,
|
||||||
|
'record_id' => $record->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Archivo subido exitosamente',
|
||||||
|
'file' => [
|
||||||
|
'id' => $file->id,
|
||||||
|
'name' => $file->name,
|
||||||
|
'path' => $file->path,
|
||||||
|
'md5' => $file->md5,
|
||||||
|
'url' => $file->url,
|
||||||
|
'record_id' => $file->record_id,
|
||||||
|
'created_at' => $file->created_at->toDateTimeString(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return ApiResponse::UNPROCESSABLE_CONTENT->response([
|
||||||
|
'message' => 'Error al subir el archivo',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFile($recordId) {
|
||||||
|
try {
|
||||||
|
$record = Record::with('files')->findOrFail($recordId);
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'record_id' => $record->id,
|
||||||
|
'files' => $record->files,
|
||||||
|
'folio' => $record->folio,
|
||||||
|
]);
|
||||||
|
}catch(\Exception $e) {
|
||||||
|
return ApiResponse::NOT_FOUND->response([
|
||||||
|
'message' => 'No se encontró el expediente o no tiene archivos asociados.',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteFile($fileId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$file = File::findOrFail($fileId);
|
||||||
|
|
||||||
|
// Eliminar archivo físico del storage
|
||||||
|
if (Storage::disk('public')->exists($file->path)) {
|
||||||
|
Storage::disk('public')->delete($file->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eliminar registro de la BD
|
||||||
|
$file->delete();
|
||||||
|
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Archivo eliminado exitosamente',
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'message' => 'Error al eliminar el archivo',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,64 +1,217 @@
|
|||||||
<?php namespace App\Http\Controllers\Repuve;
|
<?php namespace App\Http\Controllers\Repuve;
|
||||||
/**
|
|
||||||
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
|
|
||||||
*/
|
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
use Notsoweb\ApiResponse\Enums\ApiResponse;
|
||||||
use App\Http\Requests\Repuve\VehicleRequest;
|
use App\Http\Requests\Repuve\VehicleStoreRequest;
|
||||||
use App\Models\Vehicle;
|
use App\Models\Vehicle;
|
||||||
use App\Models\Record;
|
use App\Models\Record;
|
||||||
|
use App\Models\Owner;
|
||||||
|
use App\Models\File;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class RepuveController extends Controller
|
class RepuveController extends Controller
|
||||||
{
|
{
|
||||||
public function consultarVehiculo(VehicleRequest $request)
|
public function inscripcionVehiculo(VehicleStoreRequest $request)
|
||||||
{
|
{
|
||||||
// La validación por el Request
|
try {
|
||||||
$folio = $request->input('folio');
|
// Validar folio
|
||||||
|
$folio = $request->input('folio');
|
||||||
|
|
||||||
// Simular consulta de robo
|
// Simular consulta de robo
|
||||||
$isStolen = (bool) rand(0, 1);
|
$isStolen = $this->checkIfStolen($folio);
|
||||||
$stolenMessage = $isStolen
|
|
||||||
? 'El vehículo reporta robo'
|
|
||||||
: 'El vehículo no reporta robo';
|
|
||||||
|
|
||||||
// Obtener datos del vehículo
|
// Si está robado, detener el flujo
|
||||||
$vehicleData = $this->getVehicle();
|
if ($isStolen) {
|
||||||
|
return ApiResponse::FORBIDDEN->response([
|
||||||
|
'folio' => $folio,
|
||||||
|
'stolen' => true,
|
||||||
|
'message' => 'El vehículo reporta robo. No se puede continuar con la inscripción.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$vehicle = Vehicle::updateOrCreate(
|
// Iniciar transacción
|
||||||
['placa' => $vehicleData['PLACA']],
|
DB::beginTransaction();
|
||||||
[
|
|
||||||
'modelo' => $vehicleData['MODELO'],
|
|
||||||
'marca' => $vehicleData['MARCA'],
|
|
||||||
'nrpv' => $vehicleData['NUMERO_SERIE'],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Crear registro con folio + vehicle_id
|
// Obtener datos del vehículo y propietario
|
||||||
$record = Record::create([
|
$vehicleData = $this->getVehicle();
|
||||||
'folio' => $folio,
|
$ownerData = $this->getOwner();
|
||||||
'vehicle_id' => $vehicle->id,
|
|
||||||
'user_id' => auth()->id() ?? null,
|
//Crear propietario
|
||||||
|
$owner = Owner::updateOrCreate(
|
||||||
|
['rfc' => $ownerData['rfc']],
|
||||||
|
[
|
||||||
|
'name' => $ownerData['name'],
|
||||||
|
'paternal' => $ownerData['paternal'],
|
||||||
|
'maternal' => $ownerData['maternal'],
|
||||||
|
'curp' => $ownerData['curp'],
|
||||||
|
'address' => $ownerData['address'],
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Crear o actualizar vehículo
|
||||||
|
$vehicle = Vehicle::updateOrCreate(
|
||||||
|
['placa' => $vehicleData['PLACA']],
|
||||||
|
[
|
||||||
|
'anio_placa' => $vehicleData['ANIO_PLACA'],
|
||||||
|
'placa' => $vehicleData['PLACA'],
|
||||||
|
'numero_serie' => $vehicleData['NO_SERIE'],
|
||||||
|
'rfc' => $vehicleData['RFC'],
|
||||||
|
'folio' => $vehicleData['FOLIO'],
|
||||||
|
'vigencia' => $vehicleData['VIGENCIA'],
|
||||||
|
'fecha_impresion' => $vehicleData['FECHA_IMPRESION'],
|
||||||
|
'qr_hash' => $vehicleData['QR_HASH'],
|
||||||
|
'valido' => $vehicleData['VALIDO'],
|
||||||
|
'foliotemp' => $vehicleData['FOLIOTEMP'],
|
||||||
|
'nombre' => $vehicleData['NOMBRE'],
|
||||||
|
'nombre2' => $vehicleData['NOMBRE2'],
|
||||||
|
'municipio' => $vehicleData['MUNICIPIO'],
|
||||||
|
'localidad' => $vehicleData['LOCALIDAD'],
|
||||||
|
'calle' => $vehicleData['CALLE'],
|
||||||
|
'calle2' => $vehicleData['CALLE2'],
|
||||||
|
'tipo' => $vehicleData['TIPO'],
|
||||||
|
'tipo_servicio' => $vehicleData['TIPO_SERVICIO'],
|
||||||
|
'marca' => $vehicleData['MARCA'],
|
||||||
|
'linea' => $vehicleData['LINEA'],
|
||||||
|
'sublinea' => $vehicleData['SUBLINEA'],
|
||||||
|
'modelo' => $vehicleData['MODELO'],
|
||||||
|
'numero_motor' => $vehicleData['NUMERO_MOTOR'],
|
||||||
|
'descripcion_origen' => $vehicleData['DESCRIPCION_ORIGEN'],
|
||||||
|
'color' => $vehicleData['COLOR'],
|
||||||
|
'codigo_postal' => $vehicleData['CODIGO_POSTAL'],
|
||||||
|
'serie_folio' => $vehicleData['SERIE_FOLIO'],
|
||||||
|
'sfolio' => $vehicleData['SFOLIO'],
|
||||||
|
'nrpv' => $vehicleData['NUMERO_SERIE'],
|
||||||
|
'owner_id' => $owner->id,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
//Crear registro (expediente)
|
||||||
|
$record = Record::create([
|
||||||
|
'folio' => $folio,
|
||||||
|
'vehicle_id' => $vehicle->id,
|
||||||
|
'user_id' => auth()->id() ?? null,
|
||||||
|
]);
|
||||||
|
|
||||||
|
//Procesar y guardar archivos si existen
|
||||||
|
$uploadedFiles = [];
|
||||||
|
if ($request->hasFile('files')) {
|
||||||
|
$files = $request->file('files');
|
||||||
|
$fileNames = $request->input('file_names', []);
|
||||||
|
|
||||||
|
foreach ($files as $index => $file) {
|
||||||
|
// Generar nombre único
|
||||||
|
$fileName = time() . '_' . $index . '_' . $file->getClientOriginalName();
|
||||||
|
|
||||||
|
// Guardar archivos
|
||||||
|
$path = $file->storeAs('records', $fileName, 'public');
|
||||||
|
|
||||||
|
// Calcular MD5
|
||||||
|
$md5 = md5_file($file->getRealPath());
|
||||||
|
|
||||||
|
// Crear registro en BD
|
||||||
|
$fileRecord = File::create([
|
||||||
|
'name' => $fileNames[$index] ?? "Archivo " . ($index + 1),
|
||||||
|
'path' => $path,
|
||||||
|
'md5' => $md5,
|
||||||
|
'record_id' => $record->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$uploadedFiles[] = [
|
||||||
|
'id' => $fileRecord->id,
|
||||||
|
'name' => $fileRecord->name,
|
||||||
|
'path' => $fileRecord->path,
|
||||||
|
'url' => $fileRecord->url,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Confirmar transacción
|
||||||
|
DB::commit();
|
||||||
|
|
||||||
|
// Responder con éxito
|
||||||
|
return ApiResponse::OK->response([
|
||||||
|
'folio' => $folio,
|
||||||
|
'stolen' => false,
|
||||||
|
'message' => 'Vehículo inscrito exitosamente',
|
||||||
|
'record' => [
|
||||||
|
'id' => $record->id,
|
||||||
|
'folio' => $record->folio,
|
||||||
|
'vehicle_id' => $vehicle->id,
|
||||||
|
'user_id' => $record->user_id,
|
||||||
|
'created_at' => $record->created_at->toDateTimeString(),
|
||||||
|
],
|
||||||
|
'vehicle' => [
|
||||||
|
'id' => $vehicle->id,
|
||||||
|
'placa' => $vehicle->placa,
|
||||||
|
'numero_serie' => $vehicle->numero_serie,
|
||||||
|
'marca' => $vehicle->marca,
|
||||||
|
'modelo' => $vehicle->modelo,
|
||||||
|
'color' => $vehicle->color,
|
||||||
|
],
|
||||||
|
'owner' => [
|
||||||
|
'id' => $owner->id,
|
||||||
|
'full_name' => $owner->full_name,
|
||||||
|
'rfc' => $owner->rfc,
|
||||||
|
],
|
||||||
|
'files' => $uploadedFiles,
|
||||||
|
'total_files' => count($uploadedFiles),
|
||||||
|
]);
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
// Revertir transacción en caso de error
|
||||||
|
DB::rollBack();
|
||||||
|
|
||||||
|
// Log del error
|
||||||
|
Log::error('Error en inscripcionVehiculo: ' . $e->getMessage(), [
|
||||||
|
'folio' => $folio ?? null,
|
||||||
|
'trace' => $e->getTraceAsString()
|
||||||
|
]);
|
||||||
|
|
||||||
|
return ApiResponse::BAD_REQUEST->response([
|
||||||
|
'message' => 'Error al procesar la inscripción del vehículo',
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkIfStolen(string $folio): bool
|
||||||
|
{
|
||||||
|
// Aquí api servicio de REPUVE
|
||||||
|
// simulamos con random
|
||||||
|
return (bool) rand(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function consultaExpediente(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'folio' => 'required|string',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
//Responder al cliente
|
$folio = $request->input('folio');
|
||||||
|
|
||||||
|
// Cargar record con todas sus relaciones
|
||||||
|
$record = Record::where('folio', $folio)
|
||||||
|
->with(['vehicle.owner', 'files', 'user'])
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (!$record) {
|
||||||
|
return ApiResponse::NOT_FOUND->response([
|
||||||
|
'message' => 'No se encontró ningún expediente con el folio proporcionado.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
return ApiResponse::OK->response([
|
return ApiResponse::OK->response([
|
||||||
'folio' => $folio,
|
'record' => [
|
||||||
'stolen' => $isStolen,
|
'id' => $record->id,
|
||||||
'message' => $stolenMessage,
|
'folio' => $record->folio,
|
||||||
'vehicle' => [
|
'vehicle' => $record->vehicle,
|
||||||
'id' => $vehicle->id,
|
'owner' => $record->vehicle->owner,
|
||||||
'placa' => $vehicleData['PLACA'],
|
'files' => $record->files,
|
||||||
'modelo' => $vehicleData['MODELO'],
|
'user' => $record->user,
|
||||||
'marca' => $vehicleData['MARCA'],
|
'created_at' => $record->created_at->toDateTimeString(),
|
||||||
'numero_serie' => $vehicleData['NUMERO_SERIE'],
|
]
|
||||||
'color' => $vehicleData['COLOR'],
|
|
||||||
'tipo' => $vehicleData['TIPO'],
|
|
||||||
'linea' => $vehicleData['LINEA'],
|
|
||||||
],
|
|
||||||
'record_id' => $record->id,
|
|
||||||
'created_at' => $record->created_at->toDateTimeString(),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,5 +250,17 @@ private function getVehicle(): array
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getOwner(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'Nicolas',
|
||||||
|
'paternal' => 'Hernandez',
|
||||||
|
'maternal' => 'Castillo',
|
||||||
|
'rfc' => 'HECN660509HTCRSC01',
|
||||||
|
'curp' => 'HECN660509HTCRSC01',
|
||||||
|
'address' => 'Fracc Pomoca, Calle Armadillo MZ9 LT28',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
30
app/Http/Requests/Repuve/FileStoreRequest.php
Normal file
30
app/Http/Requests/Repuve/FileStoreRequest.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
namespace App\Http\Requests\Repuve;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class FileStoreRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'required|string|max:255',
|
||||||
|
'file' => 'required|file|mimes:jpeg,png,jpg,pdf|max:10240',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name.required' => 'El nombre es obligatorio',
|
||||||
|
'file.required' => 'El archivo es obligatorio',
|
||||||
|
'file.mimes' => 'El archivo debe ser de tipo: jpeg, png, jpg',
|
||||||
|
'file.max' => 'El archivo no debe superar los 10MB',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
class VehicleRequest extends FormRequest
|
class VehicleStoreRequest extends FormRequest
|
||||||
{
|
{
|
||||||
|
|
||||||
public function authorize(): bool
|
public function authorize(): bool
|
||||||
@ -14,6 +14,10 @@ public function rules(): array
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'folio' => ['required', 'string', 'max:50'],
|
'folio' => ['required', 'string', 'max:50'],
|
||||||
|
'files' => ['nullable', 'array', 'min:1'],
|
||||||
|
'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'],
|
||||||
|
'file_names' => ['nullable', 'array'],
|
||||||
|
'file_names.*' => ['string', 'max:255'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,6 +27,10 @@ public function messages(): array
|
|||||||
'folio.required' => 'El folio es requerido',
|
'folio.required' => 'El folio es requerido',
|
||||||
'folio.string' => 'El folio debe ser una cadena de texto',
|
'folio.string' => 'El folio debe ser una cadena de texto',
|
||||||
'folio.max' => 'El folio no puede exceder 50 caracteres',
|
'folio.max' => 'El folio no puede exceder 50 caracteres',
|
||||||
|
'files.array' => 'Los archivos deben ser un array',
|
||||||
|
'files.*.file' => 'Cada elemento debe ser un archivo válido',
|
||||||
|
'files.*.mimes' => 'Los archivos deben ser de tipo: jpeg, png, jpg, pdf',
|
||||||
|
'files.*.max' => 'Cada archivo no debe superar los 10MB',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,4 +22,16 @@ class File extends Model
|
|||||||
'url',
|
'url',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
public function record()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Record::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function url(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: fn () => Storage::url($this->path),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,4 +22,11 @@ class Owner extends Model
|
|||||||
protected $appends = [
|
protected $appends = [
|
||||||
'full_name',
|
'full_name',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
protected function fullName(): Attribute
|
||||||
|
{
|
||||||
|
return Attribute::make(
|
||||||
|
get: fn() => trim("{$this->name} {$this->paternal} {$this->maternal}")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,4 +16,18 @@ class Record extends Model
|
|||||||
'error_id',
|
'error_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function vehicle()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Vehicle::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function files()
|
||||||
|
{
|
||||||
|
return $this->hasMany(File::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,11 +12,40 @@ class Vehicle extends Model
|
|||||||
protected $table = 'vehicle';
|
protected $table = 'vehicle';
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
'anio_placa',
|
||||||
'placa',
|
'placa',
|
||||||
'modelo',
|
'numero_serie',
|
||||||
|
'rfc',
|
||||||
|
'vigencia',
|
||||||
|
'fecha_impresion',
|
||||||
|
'qr_hash',
|
||||||
|
'valido',
|
||||||
|
'foliotemp',
|
||||||
|
'nombre',
|
||||||
|
'nombre2',
|
||||||
|
'municipio',
|
||||||
|
'localidad',
|
||||||
|
'calle',
|
||||||
|
'calle2',
|
||||||
|
'tipo',
|
||||||
|
'tipo_servicio',
|
||||||
'marca',
|
'marca',
|
||||||
|
'linea',
|
||||||
|
'sublinea',
|
||||||
|
'modelo',
|
||||||
|
'numero_motor',
|
||||||
|
'descripcion_origen',
|
||||||
|
'color',
|
||||||
|
'codigo_postal',
|
||||||
|
'serie_folio',
|
||||||
|
'sfolio',
|
||||||
'nrpv',
|
'nrpv',
|
||||||
'owner_id',
|
'owner_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
public function owner()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Owner::class);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,12 +7,12 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.3",
|
"php": "^8.3",
|
||||||
|
"barryvdh/laravel-dompdf": "*",
|
||||||
"laravel/framework": "^12.0",
|
"laravel/framework": "^12.0",
|
||||||
"laravel/passport": "^12.4",
|
"laravel/passport": "^12.4",
|
||||||
"laravel/pulse": "^1.4",
|
"laravel/pulse": "^1.4",
|
||||||
"laravel/reverb": "^1.4",
|
"laravel/reverb": "^1.4",
|
||||||
"laravel/tinker": "^2.10",
|
"laravel/tinker": "^2.10",
|
||||||
"mongodb/laravel-mongodb": "^5.0",
|
|
||||||
"notsoweb/laravel-core": "dev-main",
|
"notsoweb/laravel-core": "dev-main",
|
||||||
"spatie/laravel-permission": "^6.16",
|
"spatie/laravel-permission": "^6.16",
|
||||||
"tightenco/ziggy": "^2.5"
|
"tightenco/ziggy": "^2.5"
|
||||||
|
|||||||
1781
composer.lock
generated
1781
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -112,22 +112,6 @@
|
|||||||
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
|
// 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'),
|
||||||
],
|
],
|
||||||
|
|
||||||
'mongodb' => [
|
|
||||||
'driver' => 'mongodb',
|
|
||||||
'dsn' => env('MONGO_DSN', null),
|
|
||||||
'host' => env('MONGO_HOST', '127.0.0.1'),
|
|
||||||
'port' => env('MONGO_PORT', '27017'),
|
|
||||||
'database' => env('MONGO_DATABASE', 'holos'),
|
|
||||||
'username' => env('MONGO_USERNAME', null),
|
|
||||||
'password' => env('MONGO_PASSWORD', null),
|
|
||||||
'options' => [
|
|
||||||
'ssl' => env('MONGO_SSL', true),
|
|
||||||
'authSource' => env('MONGO_AUTH_SOURCE', 'admin'),
|
|
||||||
'retryWrites' => env('MONGO_RETRY_WRITES', true),
|
|
||||||
'w' => env('MONGO_W', 'majority'),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -15,6 +15,7 @@ services:
|
|||||||
- /var/www/repuve-v1/vendor
|
- /var/www/repuve-v1/vendor
|
||||||
networks:
|
networks:
|
||||||
- repuve-network
|
- repuve-network
|
||||||
|
mem_limit: 400m
|
||||||
depends_on:
|
depends_on:
|
||||||
mysql:
|
mysql:
|
||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
@ -28,6 +29,7 @@ services:
|
|||||||
- ./Docker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
- ./Docker/nginx/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||||
networks:
|
networks:
|
||||||
- repuve-network
|
- repuve-network
|
||||||
|
mem_limit: 400m
|
||||||
depends_on:
|
depends_on:
|
||||||
- repuve-backend
|
- repuve-backend
|
||||||
|
|
||||||
@ -44,25 +46,12 @@ services:
|
|||||||
- mysql_data:/var/lib/mysql
|
- mysql_data:/var/lib/mysql
|
||||||
networks:
|
networks:
|
||||||
- repuve-network
|
- repuve-network
|
||||||
|
mem_limit: 400m
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
||||||
timeout: 15s
|
timeout: 15s
|
||||||
retries: 10
|
retries: 10
|
||||||
|
|
||||||
mongodb:
|
|
||||||
image: mongo:7.0
|
|
||||||
environment:
|
|
||||||
MONGO_INITDB_ROOT_USERNAME: ${MONGO_USERNAME}
|
|
||||||
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_PASSWORD}
|
|
||||||
MONGO_INITDB_DATABASE: ${MONGO_DATABASE}
|
|
||||||
ports:
|
|
||||||
- "${MONGO_PORT}:27017"
|
|
||||||
volumes:
|
|
||||||
- mongodb_data:/data/db
|
|
||||||
- mongodb_config:/data/configdb
|
|
||||||
networks:
|
|
||||||
- repuve-network
|
|
||||||
|
|
||||||
phpmyadmin:
|
phpmyadmin:
|
||||||
image: phpmyadmin/phpmyadmin
|
image: phpmyadmin/phpmyadmin
|
||||||
environment:
|
environment:
|
||||||
@ -74,14 +63,11 @@ services:
|
|||||||
- mysql
|
- mysql
|
||||||
networks:
|
networks:
|
||||||
- repuve-network
|
- repuve-network
|
||||||
|
mem_limit: 400m
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
mysql_data:
|
mysql_data:
|
||||||
driver: local
|
driver: local
|
||||||
mongodb_data:
|
|
||||||
driver: local
|
|
||||||
mongodb_config:
|
|
||||||
driver: local
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
repuve-network:
|
repuve-network:
|
||||||
|
|||||||
@ -21,8 +21,6 @@ RUN apt-get update && apt-get install -y\
|
|||||||
|
|
||||||
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
|
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip
|
||||||
|
|
||||||
RUN pecl install mongodb && docker-php-ext-enable mongodb
|
|
||||||
|
|
||||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
COPY composer.json composer.lock ./
|
COPY composer.json composer.lock ./
|
||||||
|
|||||||
2948
package-lock.json
generated
2948
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"build": "vite build",
|
|
||||||
"dev": "vite"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"autoprefixer": "^10.4.20",
|
|
||||||
"axios": "^1.7.4",
|
|
||||||
"concurrently": "^9.0.1",
|
|
||||||
"laravel-echo": "^1.17.1",
|
|
||||||
"laravel-vite-plugin": "^1.0",
|
|
||||||
"postcss": "^8.4.47",
|
|
||||||
"pusher-js": "^8.4.0-rc2",
|
|
||||||
"tailwindcss": "^3.4.13",
|
|
||||||
"vite": "^5.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
269
resources/views/pdfs/record.blade.php
Normal file
269
resources/views/pdfs/record.blade.php
Normal file
@ -0,0 +1,269 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Registro Público Vehicular</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 {
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: 2px solid #000;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header img {
|
||||||
|
max-height: 110px;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logos-container {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-left {
|
||||||
|
display: table-cell;
|
||||||
|
width: 25%;
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-right {
|
||||||
|
display: table-cell;
|
||||||
|
width: 25%;
|
||||||
|
text-align: right;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-center {
|
||||||
|
display: table-cell;
|
||||||
|
width: 50%;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.government-section {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.government-section h2,
|
||||||
|
.government-section h3 {
|
||||||
|
font-size: 11pt;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 2px 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legal-text {
|
||||||
|
text-align: justify;
|
||||||
|
font-size: 10pt;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legal-text p {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-number {
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-number span {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-row {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-label {
|
||||||
|
font-weight: bold;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.address-row {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-cell {
|
||||||
|
width: 50%;
|
||||||
|
text-align: center;
|
||||||
|
vertical-align: bottom;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-line {
|
||||||
|
border-top: 2px solid #000;
|
||||||
|
width: 250px;
|
||||||
|
margin: 0 auto 8px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-name {
|
||||||
|
font-size: 10pt;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 3px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signature-role {
|
||||||
|
font-size: 10pt;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<!-- Header con logo centrado -->
|
||||||
|
<div class="header">
|
||||||
|
<?php
|
||||||
|
$imagePath = storage_path('app/images/logo-seguridad.png');
|
||||||
|
$imageData = base64_encode(file_get_contents($imagePath));
|
||||||
|
$imageSrc = 'data:image/png;base64,' . $imageData;
|
||||||
|
?>
|
||||||
|
<img src="{{ $imageSrc }}" alt="Logo Seguridad">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Encabezados gubernamentales -->
|
||||||
|
<div class="government-section">
|
||||||
|
<h2>GOBIERNO DEL ESTADO DE TABASCO</h2>
|
||||||
|
<h3>SECRETARÍA DE FINANZAS</h3>
|
||||||
|
<h3>DIRECCIÓN DE INGRESOS</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Texto legal -->
|
||||||
|
<div class="legal-text">
|
||||||
|
<p>
|
||||||
|
CON FUNDAMENTO EN LOS ARTÍCULOS 1 Y 7 DE LA LEY DEL REGISTRO PÚBLICO VEHICULAR PUBLICADA EN EL DIARIO
|
||||||
|
OFICIAL DE LA FEDERACIÓN DE FECHA 1 DE SEPTIEMBRE DE 2004, Y ARTÍCULO 22 DEL REGLAMENTO INTERIOR DE LA
|
||||||
|
SECRETARÍA DE FINANZAS DEL GOBIERNO DEL ESTADO DE TABASCO, PUBLICADO EN EL PERIÓDICO OFICIAL DEL
|
||||||
|
ESTADO DE TABASCO DEL 2 DE JUNIO DE 2010, DOY MI MÁS AMPLIO CONSENTIMIENTO A ESTA DEPENDENCIA PARA
|
||||||
|
QUE SEA VERIFICADO FÍSICAMENTE MI VEHÍCULO CON PLACAS DE CIRCULACIÓN:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="vehicle-number">
|
||||||
|
PLACA <span>{{ $record->vehicle->placa }}</span> / NÚMERO DE SERIE <span>{{ $record->vehicle->nrpv }}</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
,ACEPTANDO LAS CONSECUENCIAS QUE DE ESTA VERIFICACIÓN SURJAN, CON OBJETO DE QUE SE ME OTORGUE
|
||||||
|
CONSTANCIA DE INSCRIPCIÓN Y SE ADHIERA EN MI VEHÍCULO EL HOLOGRAMA DEL REGISTRO PÚBLICO VEHICULAR.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 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>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data-row">
|
||||||
|
<span class="data-label">NOMBRE:</span>
|
||||||
|
<span>{{$record->vehicle->owner->full_name }} </span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="address-row">
|
||||||
|
<span class="data-label">CALLE:</span>
|
||||||
|
<span>{{ $record->vehicle->owner->address }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data-row">
|
||||||
|
<span class="data-label">COLONIA:</span>
|
||||||
|
<span>{{ $record->vehicle->owner->address }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="data-row">
|
||||||
|
<span class="data-label">MUNICIPIO:</span>
|
||||||
|
<span>{{ $record->vehicle->municipio }}</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>
|
||||||
|
|
||||||
|
<!-- Firmas -->
|
||||||
|
<div class="signatures">
|
||||||
|
<table class="signature-table">
|
||||||
|
<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>
|
||||||
|
</td>
|
||||||
|
<td class="signature-cell">
|
||||||
|
<div class="signature-line">{{ $record->user->full_name }}</div>
|
||||||
|
<p class="signature-role">Operador</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use App\Http\Controllers\Repuve\RepuveController;
|
use App\Http\Controllers\Repuve\RepuveController;
|
||||||
|
use App\Http\Controllers\Repuve\RecordController;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rutas del núcleo de la aplicación.
|
* Rutas del núcleo de la aplicación.
|
||||||
@ -19,7 +20,13 @@
|
|||||||
/** Rutas protegidas (requieren autenticación) */
|
/** Rutas protegidas (requieren autenticación) */
|
||||||
Route::middleware('auth:api')->group(function() {
|
Route::middleware('auth:api')->group(function() {
|
||||||
// Tus rutas protegidas
|
// Tus rutas protegidas
|
||||||
Route::post('consultar', [RepuveController::class, 'consultarVehiculo']);
|
Route::post('inscripcion', [RepuveController::class, 'inscripcionVehiculo']);
|
||||||
|
Route::post('consulta', [RepuveController::class, 'consultaExpediente']);
|
||||||
|
|
||||||
|
Route::get('expediente/{id}/pdf', [RecordController::class, 'generatePdf']);
|
||||||
|
Route::get('expediente/{recordId}/documentos', [RecordController::class, 'getFile']);
|
||||||
|
Route::post('expediente/documentos', [RecordController::class, 'uploadFile']);
|
||||||
|
Route::delete('expediente/documentos/{fileId}', [RecordController::class, 'deleteFile']);
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Rutas públicas */
|
/** Rutas públicas */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user