From 3a25257dc592965cb696b3e80742596a3afe932d Mon Sep 17 00:00:00 2001 From: Juan Felipe Zapata Moreno Date: Tue, 28 Oct 2025 16:50:40 -0600 Subject: [PATCH] WIP --- .../Repuve/InscriptionController.php | 13 +- .../Controllers/Repuve/RecordController.php | 4 +- .../Controllers/Repuve/TagsController.php | 17 ++ .../Controllers/Repuve/UpdateController.php | 203 ++++++++++-------- .../Requests/Repuve/VehicleUpdateRequest.php | 14 +- app/Models/Tag.php | 51 +++++ routes/api.php | 10 +- 7 files changed, 204 insertions(+), 108 deletions(-) diff --git a/app/Http/Controllers/Repuve/InscriptionController.php b/app/Http/Controllers/Repuve/InscriptionController.php index b165234..1dd26f4 100644 --- a/app/Http/Controllers/Repuve/InscriptionController.php +++ b/app/Http/Controllers/Repuve/InscriptionController.php @@ -113,10 +113,7 @@ public function vehicleInscription(VehicleStoreRequest $request) ]); // Asignar Tag al vehículo - $tag->update([ - 'vehicle_id' => $vehicle->id, - 'folio' => $folio, - ]); + $tag->markAsAssigned($vehicle->id, $folio); // Crear registro $record = Record::create([ @@ -132,12 +129,15 @@ public function vehicleInscription(VehicleStoreRequest $request) $fileNames = $request->input('names', []); foreach ($files as $index => $file) { - $fileName = uniqid() . '_' . time() . '_' . $file->getClientOriginalName(); + $customName = $fileNames[$index] ?? "archivo_" . ($index + 1); + $customName = str_replace(' ', '_', $customName); + $extension = $file->getClientOriginalExtension(); + $fileName = $customName . '_' . time() . '.' . $extension; $path = $file->storeAs('/', $fileName, 'records'); $md5 = md5_file($file->getRealPath()); $fileRecord = File::create([ - 'name' => $fileNames[$index] ?? "Archivo " . ($index + 1), + 'name' => $customName, 'path' => $path, 'md5' => $md5, 'record_id' => $record->id, @@ -240,6 +240,7 @@ public function vehicleInscription(VehicleStoreRequest $request) 'tag' => [ 'id' => $tag->id, 'folio' => $tag->folio, + 'status' => $tag->status, ], 'files' => $uploadedFiles, 'total_files' => count($uploadedFiles), diff --git a/app/Http/Controllers/Repuve/RecordController.php b/app/Http/Controllers/Repuve/RecordController.php index d234e15..c097c07 100644 --- a/app/Http/Controllers/Repuve/RecordController.php +++ b/app/Http/Controllers/Repuve/RecordController.php @@ -78,7 +78,6 @@ public function generatePdfImages($id) $pdf->SetAutoPageBreak(false); $pdf->SetMargins(10, 10, 10); - $totalImages = $record->files->count(); $currentImage = 0; foreach ($record->files as $file) { @@ -137,6 +136,9 @@ public function generatePdfImages($id) case IMAGETYPE_JPEG: $imageExtension = 'JPEG'; break; + case IMAGETYPE_JPEG: + $imageExtension = 'JPG'; + break; case IMAGETYPE_PNG: $imageExtension = 'PNG'; break; diff --git a/app/Http/Controllers/Repuve/TagsController.php b/app/Http/Controllers/Repuve/TagsController.php index 814fe5c..a4802c8 100644 --- a/app/Http/Controllers/Repuve/TagsController.php +++ b/app/Http/Controllers/Repuve/TagsController.php @@ -56,4 +56,21 @@ public function show(Tag $tag) 'tag' => $tag, ]); } + + public function destroy(Tag $tag) + { + try{ + $tag->delete(); + return ApiResponse::OK->response([ + 'message' => 'Tag eliminado correctamente.', + ]); + + }catch(\Exception $e){ + return ApiResponse::INTERNAL_ERROR->response([ + 'message' => 'Error al eliminar el tag.', + 'error' => $e->getMessage(), + ]); + } + + } } diff --git a/app/Http/Controllers/Repuve/UpdateController.php b/app/Http/Controllers/Repuve/UpdateController.php index 9790f6f..370bfdd 100644 --- a/app/Http/Controllers/Repuve/UpdateController.php +++ b/app/Http/Controllers/Repuve/UpdateController.php @@ -20,36 +20,36 @@ class UpdateController extends Controller public function vehicleUpdate(VehicleUpdateRequest $request) { try { - $folio = $request->input('folio'); - $tagId = $request->input('tag_id'); + $recordId = $request->input('record_id'); // Buscar vehículo por folio y tag - $tag = Tag::with('vehicle.owner')->findOrFail($tagId); + $record = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'error'])->find($recordId); - // Validar que el tag corresponda al folio - if ($tag->folio !== $folio) { + // Validar que el tag + if (!$record) { return ApiResponse::BAD_REQUEST->response([ - 'message' => 'El folio no coincide con el tag RFID proporcionado', - 'folio_request' => $folio, - 'folio_tag' => $tag->folio, + 'message' => 'No se encontró el expediente', + 'record_id' => $recordId, ]); } - $vehicle = $tag->vehicle; + $vehicle = $record->vehicle; + $tag = $vehicle->tag; - if (!$vehicle) { + if (!$tag) { return ApiResponse::NOT_FOUND->response([ - 'message' => 'No se encontró un vehículo asociado al tag', + 'message' => 'El vehículo no tiene tag asociado', + 'vehicle_id' => $vehicle->placa, ]); } // Consultar API Repuve Nacional (verificar robo) - $isStolen = $this->checkIfStolen($folio); + $isStolen = $this->checkIfStolen($record->folio); if ($isStolen) { return ApiResponse::FORBIDDEN->response([ - 'folio' => $folio, - 'tag_id' => $tagId, + 'record_id' => $recordId, + 'tag_number' => $tag->tag_number, 'stolen' => true, 'message' => 'El vehículo reporta robo. No se puede continuar con la actualización.', ]); @@ -80,7 +80,7 @@ public function vehicleUpdate(VehicleUpdateRequest $request) // NO actualizar 'placa' - es UNIQUE // NO actualizar 'numero_serie' - es UNIQUE (NIV/VIN) 'rfc' => $vehicleData['RFC'], - 'folio' => $folio, + // NO actualizar 'folio' => $folio, 'vigencia' => $vehicleData['VIGENCIA'], 'fecha_impresion' => $vehicleData['FECHA_IMPRESION'], 'qr_hash' => $vehicleData['QR_HASH'], @@ -107,16 +107,40 @@ public function vehicleUpdate(VehicleUpdateRequest $request) 'owner_id' => $owner->id, ]); - $record = Record::firstOrCreate( - ['vehicle_id' => $vehicle->id], - [ - 'folio' => $folio, - 'user_id' => Auth::id(), - ] - ); + // Procesar archivos si existen + $uploadedFiles = []; + if ($request->hasFile('files')) { + $files = $request->file('files'); + $fileNames = $request->input('names', []); + + foreach ($files as $index => $file) { + $customName = $fileNames[$index] ?? "archivo_" . ($index + 1); + $customName = str_replace(' ', '_', $customName); + $extension = $file->getClientOriginalExtension(); + $fileName = $customName . '_' . time() . '.' . $extension; + $path = $file->storeAs('/', $fileName, 'records'); + $md5 = md5_file($file->getRealPath()); + + $fileRecord = File::create([ + 'name' => $customName, + 'path' => $path, + 'md5' => $md5, + 'record_id' => $record->id, + ]); + + $uploadedFiles[] = [ + 'id' => $fileRecord->id, + 'name' => $fileRecord->name, + 'path' => $fileRecord->path, + 'url' => $fileRecord->url, + ]; + } + } // Enviar datos a API Repuve Nacional - $apiResponse = $this->sendToRepuveNacional($folio, $tagId, $vehicleData); + $apiResponse = $this->sendToRepuveNacional($vehicle->numero_serie); + $apiResponse["repuve_response"]["folio_ci"] = $record->folio; + $apiResponse["repuve_response"]["identificador_ci"] = $tag->tag_number; // Procesar respuesta de la API if (isset($apiResponse['has_error']) && $apiResponse['has_error']) { @@ -135,7 +159,7 @@ public function vehicleUpdate(VehicleUpdateRequest $request) // guarda error $record->update([ 'error_id' => $error->id, - 'api_response' => $apiResponse, // Guarda respuesta con error + 'api_response' => $apiResponse, 'error_occurred_at' => now(), ]); @@ -146,7 +170,7 @@ public function vehicleUpdate(VehicleUpdateRequest $request) return ApiResponse::OK->response([ 'message' => 'Datos guardados con error. Corrija los datos y vuelva a enviar.', 'has_error' => true, - 'can_retry' => true, + 'can_update' => true, 'error' => [ 'code' => $error->code, 'description' => $error->description, @@ -158,6 +182,16 @@ public function vehicleUpdate(VehicleUpdateRequest $request) ], 'vehicle' => $vehicleData, 'owner' => $ownerData, + 'new_files' => $uploadedFiles, + 'total_new_files' => count($uploadedFiles), + 'existing_files' => $record->files->map(function ($file) { + return [ + 'id' => $file->id, + 'name' => $file->name, + 'path' => $file->path, + 'url' => $file->url, + ]; + }), ]); } @@ -165,36 +199,9 @@ public function vehicleUpdate(VehicleUpdateRequest $request) $record->update([ 'error_id' => null, 'api_response' => $apiResponse, - 'error_occurred_at' => null, + 'error_occurred_at' => null, ]); - // Procesar archivos si existen - $uploadedFiles = []; - if ($request->hasFile('files')) { - $files = $request->file('files'); - $fileNames = $request->input('names', []); - - foreach ($files as $index => $file) { - $fileName = uniqid() . '_' . time() . '_' . $file->getClientOriginalName(); - $path = $file->storeAs('records', $fileName, 'public'); - $md5 = md5_file($file->getRealPath()); - - $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, - ]; - } - } - DB::commit(); return ApiResponse::OK->response([ @@ -203,6 +210,8 @@ public function vehicleUpdate(VehicleUpdateRequest $request) 'record' => [ 'id' => $record->id, 'folio' => $record->folio, + 'vehicle_id' => $vehicle->id, + 'user_id' => $record->user_id, 'updated_at' => $record->updated_at->toDateTimeString(), ], 'vehicle' => [ @@ -211,6 +220,7 @@ public function vehicleUpdate(VehicleUpdateRequest $request) 'numero_serie' => $vehicle->numero_serie, 'marca' => $vehicle->marca, 'modelo' => $vehicle->modelo, + 'color' => $vehicle->color, ], 'owner' => [ 'id' => $owner->id, @@ -220,11 +230,20 @@ public function vehicleUpdate(VehicleUpdateRequest $request) 'tag' => [ 'id' => $tag->id, 'folio' => $tag->folio, + 'tag_number' => $tag->tag_number, + 'status' => $tag->status, ], 'new_files' => $uploadedFiles, 'total_new_files' => count($uploadedFiles), + 'existing_files' => $record->files->map(function ($file) { + return [ + 'id' => $file->id, + 'name' => $file->name, + 'url' => $file->url, + ]; + }), + 'total_files' => $record->files->count() + count($uploadedFiles), ]); - } catch (\Exception $e) { DB::rollBack(); return ApiResponse::BAD_REQUEST->response([ @@ -241,55 +260,57 @@ private function checkIfStolen(string $folio): bool 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 // 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 con mock igual que InscriptionController - $hasError = (bool) rand(0, 1); + // 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 "; - if ($hasError) { - // Obtener un error aleatorio de la tabla errors - $error = Error::inRandomOrder()->first(); - - if (!$error) { - // Si no hay errores en la tabla, retornar error genérico - return [ - 'has_error' => true, - 'error_code' => 'ERR_UNKNOWN', - 'error_message' => 'No hay errores registrados en el catálogo', - 'timestamp' => now()->toDateTimeString(), - 'folio' => $folio, - 'tag_id' => $tagId, - 'response_data' => null, - ]; - } - - return [ - 'has_error' => true, - 'error_code' => $error->code, - 'error_message' => $error->description, - 'timestamp' => now()->toDateTimeString(), - 'folio' => $folio, - 'tag_id' => $tagId, - 'response_data' => null, - ]; + // 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 [ 'has_error' => false, 'error_code' => null, 'error_message' => null, 'timestamp' => now()->toDateTimeString(), - 'folio' => $folio, - 'tag_id' => $tagId, - 'response_data' => [ - 'status' => 'success', - 'repuve_id' => 'REPUVE-' . strtoupper(uniqid()), - 'validated' => true, - ], + 'vin' => $vin, + 'repuve_response' => $jsonResponse, ]; } diff --git a/app/Http/Requests/Repuve/VehicleUpdateRequest.php b/app/Http/Requests/Repuve/VehicleUpdateRequest.php index 0d73552..d616543 100644 --- a/app/Http/Requests/Repuve/VehicleUpdateRequest.php +++ b/app/Http/Requests/Repuve/VehicleUpdateRequest.php @@ -13,13 +13,21 @@ public function authorize(): bool public function rules(): array { return [ - 'folio' => ['required', 'string', 'max:50'], - 'tag_id' => ['required', 'exists:tags,id'], + 'record_id' => ['required', 'integer', 'exists:records,id'], 'files' => ['nullable', 'array', 'min:1'], 'files.*' => ['file', 'mimes:jpeg,png,jpg,pdf', 'max:10240'], 'names' => ['nullable', 'array'], 'names.*' => ['string', 'max:255'], - 'replace_files' => ['nullable', 'boolean'], + ]; + } + + public function messages(): array + { + return [ + 'record_id.required' => 'El id del expediente es requerido', + 'record_id.exists' => 'El expediente no existe en el sistema', + 'files.*.mimes' => 'Solo se permiten archivos JPG, PNG o JPEG', + 'files.*.max' => 'El archivo no debe superar 3MB', ]; } } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 7ced8ff..f6a4f39 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -9,11 +9,18 @@ class Tag extends Model { use HasFactory; + // Constantes de status + const STATUS_AVAILABLE = 'available'; + const STATUS_ASSIGNED = 'assigned'; + const STATUS_CANCELLED = 'cancelled'; + const STATUS_LOST = 'lost'; + protected $fillable = [ 'folio', 'tag_number', 'vehicle_id', 'package_id', + 'status', ]; public function vehicle() @@ -35,4 +42,48 @@ public function scanHistories() { return $this->hasMany(ScanHistory::class); } + + /** + * Marcar tag como asignado a un vehículo + */ + public function markAsAssigned(int $vehicleId, string $folio): void + { + $this->update([ + 'vehicle_id' => $vehicleId, + 'folio' => $folio, + 'status' => self::STATUS_ASSIGNED, + ]); + } + + /** + * Verificar si el tag está disponible + */ + public function isAvailable(): bool + { + return $this->status === self::STATUS_AVAILABLE; + } + + /** + * Verificar si el tag está asignado + */ + public function isAssigned(): bool + { + return $this->status === self::STATUS_ASSIGNED; + } + + /** + * Verificar si el tag está cancelado + */ + public function isCancelled(): bool + { + return $this->status === self::STATUS_CANCELLED; + } + + /** + * Verificar si el tag está perdido + */ + public function isLost(): bool + { + return $this->status === self::STATUS_LOST; + } } diff --git a/routes/api.php b/routes/api.php index c11bb28..0a8311c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -37,7 +37,7 @@ Route::get('expediente/{id}/pdfImagenes', [RecordController::class, 'generatePdfImages']); //Rutas de Actualización - Route::put('actualizar-vehiculo', [UpdateController::class, 'vehicleUpdate']); + Route::post('actualizar', [UpdateController::class, 'vehicleUpdate']); // Rutas de cancelación de constancias Route::post('cancelacion/cancelar', [CancellationController::class, 'cancelarConstancia']); @@ -58,12 +58,8 @@ Route::put('/devices/{id}', [DeviceController::class, 'update']); Route::delete('/devices/{id}', [DeviceController::class, 'destroy']); - //Ruta de paquetes - Route::get('/packages', [PackageController::class, 'index']); - Route::post('/packages-create', [PackageController::class, 'store']); - Route::put('/packages-update/{id}', [PackageController::class, 'update']); - Route::delete('/packages/{id}', [PackageController::class, 'destroy']); - + //Ruta de paquetes CRUD + Route::resource('packages', PackageController::class); //Ruta CRUD Tags Route::resource('tags', TagsController::class);