Update Tag Model, add TagController and routes, fix InscriptionControlle

This commit is contained in:
Edgar Méndez Mendoza 2025-10-27 09:25:30 -06:00
parent e97d124967
commit 843d3404e9
6 changed files with 130 additions and 36 deletions

View File

@ -28,35 +28,35 @@ public function vehicleInscription(VehicleStoreRequest $request)
{ {
try { try {
$folio = $request->input('folio'); $folio = $request->input('folio');
$tagId = $request->input('tag_id'); $tagNumber = $request->input('tag_number');
$vin = $request->input('vin');
// Buscar Tag y validar que NO tenga vehículo asignado // Buscar Tag y validar que NO tenga vehículo asignado
$tag = Tag::findOrFail($tagId); $tag = Tag::where('folio', $folio)->where('tag_number', $tagNumber)->first();
if (!$tag) {
return ApiResponse::NOT_FOUND->response([
'message' => 'No se encontró el tag con el folio y tag_number proporcionados.',
'folio' => $folio,
'tag_number' => $tagNumber,
]);
}
if ($tag->vehicle_id) { if ($tag->vehicle_id) {
return ApiResponse::BAD_REQUEST->response([ return ApiResponse::BAD_REQUEST->response([
'message' => 'El tag ya está asignado a un vehículo. Use actualizar en su lugar.', 'message' => 'El tag ya está asignado a un vehículo. Use actualizar en su lugar.',
'tag_id' => $tagId, 'tag_number' => $tagNumber,
'vehicle_id' => $tag->vehicle_id, 'vehicle_id' => $tag->vehicle_id,
]); ]);
} }
// Validar que el folio del tag coincida
if ($tag->folio !== $folio) {
return ApiResponse::BAD_REQUEST->response([
'message' => 'El folio no coincide con el tag RFID proporcionado',
'folio_request' => $folio,
'folio_tag' => $tag->folio,
]);
}
// Verificar robo (API Repuve Nacional) // Verificar robo (API Repuve Nacional)
$isStolen = $this->checkIfStolen($folio); $isStolen = $this->checkIfStolen($folio);
if ($isStolen) { if ($isStolen) {
return ApiResponse::FORBIDDEN->response([ return ApiResponse::FORBIDDEN->response([
'folio' => $folio, 'folio' => $folio,
'tag_id' => $tagId, 'tag_number' => $tagNumber,
'stolen' => true, 'stolen' => true,
'message' => 'El vehículo reporta robo. No se puede continuar con la inscripción.', 'message' => 'El vehículo reporta robo. No se puede continuar con la inscripción.',
]); ]);
@ -85,9 +85,9 @@ public function vehicleInscription(VehicleStoreRequest $request)
$vehicle = Vehicle::create([ $vehicle = Vehicle::create([
'anio_placa' => $vehicleData['ANIO_PLACA'], 'anio_placa' => $vehicleData['ANIO_PLACA'],
'placa' => $vehicleData['PLACA'], 'placa' => $vehicleData['PLACA'],
'numero_serie' => $vehicleData['NO_SERIE'], 'numero_serie' => $vin,
'rfc' => $vehicleData['RFC'], 'rfc' => $vehicleData['RFC'],
'folio' => $folio, 'folio' => $folio,
'vigencia' => $vehicleData['VIGENCIA'], 'vigencia' => $vehicleData['VIGENCIA'],
'fecha_impresion' => $vehicleData['FECHA_IMPRESION'], 'fecha_impresion' => $vehicleData['FECHA_IMPRESION'],
'qr_hash' => $vehicleData['QR_HASH'], 'qr_hash' => $vehicleData['QR_HASH'],
@ -155,7 +155,10 @@ public function vehicleInscription(VehicleStoreRequest $request)
} }
// Enviar a API Repuve Nacional // Enviar a API Repuve Nacional
$apiResponse = $this->sendToRepuveNacional($folio, $tagId, $vehicleData); $apiResponse = $this->sendToRepuveNacional($vin);
$apiResponse["repuve_response"]["folio_ci"] = $folio;
$apiResponse["repuve_response"]["identificador_ci"] = $tagNumber;
// Procesar respuesta // Procesar respuesta
if (isset($apiResponse['has_error']) && $apiResponse['has_error']) { if (isset($apiResponse['has_error']) && $apiResponse['has_error']) {
@ -260,13 +263,13 @@ private function checkIfStolen(string $folio): bool
return (bool) rand(0, 1); return (bool) rand(0, 1);
} }
private function sendToRepuveNacional(string $folio, int $tagId, array $vehicleData): array private function sendToRepuveNacional(string $vin): array
{ {
// Enviar datos a API Repuve Nacional // Enviar datos a API Repuve Nacional
// Aquí se haría la llamada real a la API de Repuve Nacional // Aquí se haría la llamada real a la API de Repuve Nacional
// Por ahora simulamos respuestas aleatorias usando la tabla errors // Por ahora simulamos respuestas aleatorias usando la tabla errors
$hasError = (bool) rand(0, 1); /* $hasError = (bool) rand(0, 1);
if ($hasError) { if ($hasError) {
// Obtener un error aleatorio de la tabla errors // Obtener un error aleatorio de la tabla errors
@ -279,8 +282,7 @@ private function sendToRepuveNacional(string $folio, int $tagId, array $vehicleD
'error_code' => 'ERR_UNKNOWN', 'error_code' => 'ERR_UNKNOWN',
'error_message' => 'No hay errores registrados en el catálogo', 'error_message' => 'No hay errores registrados en el catálogo',
'timestamp' => now()->toDateTimeString(), 'timestamp' => now()->toDateTimeString(),
'folio' => $folio, 'vin' => $vin,
'tag_id' => $tagId,
'response_data' => null, 'response_data' => null,
]; ];
} }
@ -290,25 +292,57 @@ private function sendToRepuveNacional(string $folio, int $tagId, array $vehicleD
'error_code' => $error->code, 'error_code' => $error->code,
'error_message' => $error->description, 'error_message' => $error->description,
'timestamp' => now()->toDateTimeString(), 'timestamp' => now()->toDateTimeString(),
'folio' => $folio, 'vin' => $vin,
'tag_id' => $tagId,
'response_data' => null, 'response_data' => null,
]; ];
} */
// Respuesta exitosa mockup REPUVE
$mockResponse = "OK:SIN INFORMACION|OTROS|SIN INFORMACION|10/07/2023|CENTRO|$vin|WSA548B|HR16777934V|2023|BLANCO|ADVANCE|TABASCO|NISSAN|MARCH|PARTICULAR|AUTOMOVIL|ACTIVO|null||null|0|null|0|10337954|E2003412012BB0C130FAD04D|Sin observaciones
";
// Parsear la cadena a JSON
// Parsear la cadena a JSON usando los campos oficiales
$fields = [
'marca',
'submarca',
'tipo_vehiculo',
'fecha_expedicion',
'oficina',
'niv',
'placa',
'motor',
'modelo',
'color',
'version',
'entidad',
'marca_padron',
'submarca_padron',
'tipo_uso_padron',
'tipo_vehiculo_padron',
'estatus_registro',
'aduana',
'nombre_aduana',
'patente',
'pedimento',
'fecha_pedimento',
'clave_importador',
'folio_ci',
'identificador_ci',
'observaciones'
];
$values = explode('|', str_replace('OK:', '', $mockResponse));
$jsonResponse = [];
foreach ($fields as $i => $field) {
$jsonResponse[$field] = $values[$i] ?? null;
} }
// Respuesta exitosa
return [ return [
'has_error' => false, 'has_error' => false,
'error_code' => null, 'error_code' => null,
'error_message' => null, 'error_message' => null,
'timestamp' => now()->toDateTimeString(), 'timestamp' => now()->toDateTimeString(),
'folio' => $folio, 'vin' => $vin,
'tag_id' => $tagId, 'repuve_response' => $jsonResponse,
'response_data' => [
'status' => 'success',
'repuve_id' => 'REPUVE-' . strtoupper(uniqid()),
'validated' => true,
],
]; ];
} }
@ -379,7 +413,7 @@ public function searchRecord(Request $request)
'full_name' => $record->vehicle->owner->full_name, 'full_name' => $record->vehicle->owner->full_name,
'rfc' => $record->vehicle->owner->rfc, 'rfc' => $record->vehicle->owner->rfc,
], ],
'tag' =>[ 'tag' => [
'id' => $record->vehicle->tag ? $record->vehicle->tag->id : null, 'id' => $record->vehicle->tag ? $record->vehicle->tag->id : null,
'folio' => $record->vehicle->tag ? $record->vehicle->tag->folio : null, 'folio' => $record->vehicle->tag ? $record->vehicle->tag->folio : null,
'status' => $record->vehicle->tag ? $record->vehicle->tag->status : null, 'status' => $record->vehicle->tag ? $record->vehicle->tag->status : null,
@ -548,7 +582,6 @@ public function listTags(Request $request)
'to' => $tags->lastItem(), 'to' => $tags->lastItem(),
], ],
]); ]);
} catch (ValidationException $e) { } catch (ValidationException $e) {
return ApiResponse::BAD_REQUEST->response([ return ApiResponse::BAD_REQUEST->response([
'message' => 'Error de validación', 'message' => 'Error de validación',
@ -565,5 +598,4 @@ public function listTags(Request $request)
]); ]);
} }
} }
} }

View File

@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers;
/**
* @copyright (c) 2025 Notsoweb Software (https://notsoweb.com) - All Rights Reserved
*/
use Illuminate\Http\Request;
use App\Models\Tag;
use Notsoweb\ApiResponse\Enums\ApiResponse;
/**
* Descripción
*
* @author Moisés Cortés C. <moises.cortes@notsoweb.com>
*
* @version 1.0.0
*/
class TagsController extends Controller
{
public function index()
{
$tags = Tag::all();
return ApiResponse::OK->response([
'data' => $tags,
]);
}
public function store(Request $request)
{
$validated = $request->validate([
'folio' => 'required|string|max:255',
'package_id' => 'required|integer|exists:packages,id',
'tag_number' => 'required|string|max:255',
]);
$tag = Tag::create($validated);
return ApiResponse::CREATED->response([
'tag' => $tag,
]);
}
public function show(Tag $tag)
{
return ApiResponse::OK->response([
'tag' => $tag,
]);
}
}

View File

@ -14,7 +14,8 @@ public function rules(): array
{ {
return [ return [
'folio' => ['required', 'string', 'max:50'], 'folio' => ['required', 'string', 'max:50'],
'tag_id' => ['required', 'exists:tags,id'], 'tag_number' => ['required', 'string', 'exists:tags,tag_number'],
'vin' => ['required', 'string', 'max:30'],
'files' => ['nullable', 'array', 'min:1'], 'files' => ['nullable', 'array', 'min:1'],
'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'], 'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'],
'names' => ['nullable', 'array'], 'names' => ['nullable', 'array'],
@ -27,8 +28,10 @@ public function messages(): array
return [ return [
'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',
'tag_id.required' => 'El tag_id es requerido', 'tag_number.required' => 'El tag_number es requerido',
'tag_id.exists' => 'El tag_id no existe en el sistema', 'tag_number.exists' => 'El tag_number no existe en el sistema',
'vin.required' => 'El VIN es requerido',
'vin.string' => 'El VIN debe ser una cadena de texto',
'files.array' => 'Los archivos deben ser un array', 'files.array' => 'Los archivos deben ser un array',
'files.*.file' => 'Cada elemento debe ser un archivo válido', 'files.*.file' => 'Cada elemento debe ser un archivo válido',
'files.*.mimes' => 'Los archivos deben ser de tipo: jpeg, png, jpg, pdf', 'files.*.mimes' => 'Los archivos deben ser de tipo: jpeg, png, jpg, pdf',

View File

@ -11,6 +11,7 @@ class Tag extends Model
protected $fillable = [ protected $fillable = [
'folio', 'folio',
'tag_number',
'vehicle_id', 'vehicle_id',
'package_id', 'package_id',
]; ];

View File

@ -14,6 +14,7 @@ public function up(): void
Schema::create('tags', function (Blueprint $table) { Schema::create('tags', function (Blueprint $table) {
$table->id(); $table->id();
$table->string('folio')->unique(); $table->string('folio')->unique();
$table->string('tag_number')->unique()->nullable();
$table->foreignId('vehicle_id')->nullable()->unique()->constrained('vehicle')->nullOnDelete(); $table->foreignId('vehicle_id')->nullable()->unique()->constrained('vehicle')->nullOnDelete();
$table->foreignId('package_id')->nullable()->constrained('packages')->nullOnDelete(); $table->foreignId('package_id')->nullable()->constrained('packages')->nullOnDelete();
$table->enum('status', ['available', 'assigned', 'cancelled', 'lost'])->default('available'); $table->enum('status', ['available', 'assigned', 'cancelled', 'lost'])->default('available');

View File

@ -8,6 +8,7 @@
use App\Http\Controllers\Admin\RoleController; use App\Http\Controllers\Admin\RoleController;
use App\Http\Controllers\Repuve\DeviceController; use App\Http\Controllers\Repuve\DeviceController;
use App\Http\Controllers\Repuve\PackageController; use App\Http\Controllers\Repuve\PackageController;
use App\Http\Controllers\TagsController;
/** /**
* Rutas del núcleo de la aplicación. * Rutas del núcleo de la aplicación.
@ -63,6 +64,10 @@
Route::put('/packages-update/{id}', [PackageController::class, 'update']); Route::put('/packages-update/{id}', [PackageController::class, 'update']);
Route::delete('/packages/{id}', [PackageController::class, 'destroy']); Route::delete('/packages/{id}', [PackageController::class, 'destroy']);
//Ruta CRUD Tags
Route::resource('tags', TagsController::class);
}); });
/** Rutas públicas */ /** Rutas públicas */