repuveService = $repuveService; $this->padronEstatalService = $padronEstatalService; } /* * Inscripción de vehículo al REPUVE */ public function vehicleInscription(VehicleStoreRequest $request) { try { $folio = $request->input('folio'); $tagNumber = $request->input('tag_number'); $niv = $request->input('niv'); // Buscar Tag y validar que NO tenga vehículo asignado $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) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'El tag ya está asignado a un vehículo. Use actualizar en su lugar.', 'tag_number' => $tagNumber, 'vehicle_id' => $tag->vehicle_id, ]); } // Verificar robo (API Repuve Nacional) $isStolen = $this->checkIfStolen($niv); if ($isStolen) { return ApiResponse::FORBIDDEN->response([ 'folio' => $folio, 'tag_number' => $tagNumber, 'stolen' => true, 'message' => 'El vehículo reporta robo. No se puede continuar con la inscripción.', ]); } // Iniciar transacción DB::beginTransaction(); // Obtener 37 datos de API Estatal $vehicleData = $this->getVehicle($niv); $ownerData = $this->getOwner($niv); // Crear propietario $owner = Owner::updateOrCreate( ['rfc' => $ownerData['rfc']], [ 'name' => $ownerData['name'], 'paternal' => $ownerData['paternal'], 'maternal' => $ownerData['maternal'], 'curp' => $ownerData['curp'], 'address' => $ownerData['address'], 'tipopers' => $ownerData['tipopers'], 'pasaporte' => $ownerData['pasaporte'], 'licencia' => $ownerData['licencia'], 'ent_fed' => $ownerData['ent_fed'], 'munic' => $ownerData['munic'], 'callep' => $ownerData['callep'], 'num_ext' => $ownerData['num_ext'], 'num_int' => $ownerData['num_int'], 'colonia' => $ownerData['colonia'], 'cp' => $ownerData['cp'], ] ); // Crear vehículo $vehicle = Vehicle::create([ 'placa' => $vehicleData['placa'], 'niv' => $vehicleData['niv'], 'marca' => $vehicleData['marca'], 'linea' => $vehicleData['linea'], 'sublinea' => $vehicleData['sublinea'], 'modelo' => $vehicleData['modelo'], 'color' => $vehicleData['color'], 'numero_motor' => $vehicleData['numero_motor'], 'clase_veh' => $vehicleData['clase_veh'], 'tipo_servicio' => $vehicleData['tipo_servicio'], 'rfv' => $vehicleData['rfv'], 'rfc' => $vehicleData['rfc'], 'ofcexpedicion' => $vehicleData['ofcexpedicion'], 'fechaexpedicion' => $vehicleData['fechaexpedicion'], 'tipo_veh' => $vehicleData['tipo_veh'], 'numptas' => $vehicleData['numptas'], 'observac' => $vehicleData['observac'], 'cve_vehi' => $vehicleData['cve_vehi'], 'tipo_mov' => $vehicleData['tipo_mov'], 'owner_id' => $owner->id, ]); // Asignar Tag al vehículo $tag->markAsAssigned($vehicle->id, $folio); // Crear registro $record = Record::create([ 'folio' => $folio, 'vehicle_id' => $vehicle->id, 'user_id' => Auth::id(), 'module_id' => Auth::user()->module_id, ]); // Procesar archivos $uploadedFiles = []; if ($request->hasFile('files')) { $files = $request->file('files'); $nameIds = $request->input('name_id', []); if (!empty($nameIds)) { $validIds = CatalogNameImg::whereIn('id', $nameIds)->pluck('id')->toArray(); if (count($validIds) !== count($nameIds)) { DB::rollBack(); return ApiResponse::INTERNAL_ERROR->response([ 'message' => 'Algunos IDs del catálogo de nombres no son válidos', 'provided_id' => $nameIds, 'valid_id' => $validIds, ]); } } foreach ($files as $index => $file) { // Obtener el name_id del request o usar null como fallback $nameId = $nameIds[$index] ?? null; if ($nameId === null) { DB::rollBack(); return ApiResponse::INTERNAL_ERROR->response([ 'message' => "Falta el name_id para el archivo en el índice {$index}", 'file_index' => $index, ]); } // Obtener el nombre del catálogo para el nombre del archivo $catalogName = CatalogNameImg::find($nameId); $extension = $file->getClientOriginalExtension(); $fileName = $catalogName->name . '_' . date('dmY_His') . '.' . $extension; $path = $file->storeAs("records/{$record->folio}", $fileName, 'public'); $md5 = md5_file($file->getRealPath()); $fileRecord = File::create([ 'name_id' => $nameId, 'path' => $path, 'md5' => $md5, 'record_id' => $record->id, ]); $uploadedFiles[] = [ 'id' => $fileRecord->id, 'name' => $catalogName->name, 'path' => $fileRecord->path, 'url' => $fileRecord->url, ]; } } // Enviar a API Repuve Nacional $apiResponse = $this->sendToRepuveNacional($niv); $apiResponse["repuve_response"]["folio_ci"] = $folio; //harcodeado cambiar despues $apiResponse["repuve_response"]["identificador_ci"] = $tagNumber; //harcodeado cambiar despues // Procesar respuesta if (isset($apiResponse['has_error']) && $apiResponse['has_error']) { // Si hay error, buscar el error en la BD $error = Error::where('code', $apiResponse['error_code'])->first(); if (!$error) { // Usar error genérico si no se encuentra el código logger()->warning('Código de error REPUVE no catalogado', [ 'error_code' => $apiResponse['error_code'], 'error_message' => $apiResponse['error_message'], 'niv' => $niv, ]); // Buscar error genérico -1 (Error Interno) $error = Error::where('code', '-1')->first(); if (!$error) { // Si ni siquiera existe el error genérico, crear uno temporal $error = Error::create([ 'code' => '-1', 'name' => 'Error Interno', 'description' => 'Error interno del web service. Problemas con la base de datos o en la red.', 'type' => 'Error de Conexión', ]); } } // Guardar error en Record $record->update([ 'error_id' => $error->id, 'api_response' => $apiResponse, 'error_occurred_at' => now(), ]); DB::commit(); // Retornar con error return ApiResponse::OK->response([ 'message' => 'Vehículo inscrito con error. Corrija los datos usando la función de actualización.', 'has_error' => true, 'can_update' => true, 'record_id' => $record->id, 'error' => [ 'code' => $error->code, 'description' => $error->description, 'occurred_at' => $record->error_occurred_at->toDateTimeString(), ], 'record' => [ 'id' => $record->id, 'folio' => $record->folio, ], 'vehicle' => $vehicleData, 'owner' => $ownerData, 'files' => $uploadedFiles, 'total_files' => count($uploadedFiles), ]); } // Si NO hay error, guardar respuesta exitosa $record->update([ 'error_id' => null, 'api_response' => $apiResponse, 'error_occurred_at' => null, ]); DB::commit(); $record->load(['vehicle.owner', 'vehicle.tag', 'files', 'user']); // Responder con éxito return ApiResponse::OK->response([ 'message' => 'Vehículo inscrito exitosamente', 'has_error' => false, 'stolen' => false, 'record' => [ 'id' => $record->id, 'folio' => $record->folio, 'vehicle_id' => $vehicle->id, 'user_id' => $record->user_id, 'created_at' => $record->created_at->toDateTimeString(), ], 'vehicle' => [ 'id' => $record->vehicle->id, 'placa' => $record->vehicle->placa, 'niv' => $record->vehicle->niv, 'marca' => $record->vehicle->marca, 'linea' => $record->vehicle->linea, 'modelo' => $record->vehicle->modelo, 'color' => $record->vehicle->color, ], 'owner' => [ 'id' => $record->vehicle->owner->id, 'full_name' => $record->vehicle->owner->full_name, 'rfc' => $record->vehicle->owner->rfc, ], 'tag' => [ 'id' => $record->vehicle->tag->id, 'folio' => $record->vehicle->tag->folio, 'status' => $record->vehicle->tag->status->name, ], 'files' => $uploadedFiles, 'total_files' => count($uploadedFiles), ]); } catch (\Exception $e) { DB::rollBack(); return ApiResponse::BAD_REQUEST->response([ 'message' => 'Error al procesar la inscripción del vehículo', 'error' => $e->getMessage(), ]); } } 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, ], ]); } private function sendToRepuveNacional(string $niv): array { // Obtener los datos completos del padrón estatal $datosCompletos = $this->padronEstatalService->getVehiculoByNiv($niv); // Enviar a inscripción de REPUVE Federal return $this->repuveService->inscribirVehiculo($datosCompletos); } public function searchRecord(Request $request) { $request->validate([ 'folio' => 'nullable|string', 'placa' => 'nullable|string', 'vin' => 'nullable|string', ], [ 'required_without_all' => 'Debe proporcionar al menos uno de los siguientes: folio, placa o vin.' ]); $records = Record::with([ 'vehicle:id,owner_id,placa,niv,marca,linea,modelo,color', 'vehicle.owner:id,name,paternal,maternal,rfc', 'vehicle.tag:id,vehicle_id,folio,tag_number,status_id', 'vehicle.tag.status:id,code,name', 'files:id,record_id,name_id,path,md5', 'files.catalogName:id,name', 'user:id,name,email', 'error:id,code,description' ])->orderBy('id', 'ASC'); if ($request->filled('folio')) { $records->whereHas('vehicle.tag', function ($q) use ($request) { $q->where('folio', 'LIKE', '%' . $request->input('folio') . '%'); }); } elseif ($request->filled('placa')) { $records->whereHas('vehicle', function ($q) use ($request) { $q->where('placa', 'LIKE', '%' . $request->input('placa') . '%'); }); } elseif ($request->filled('vin')) { $records->whereHas('vehicle', function ($q) use ($request) { $q->where('niv', 'LIKE', '%' . $request->input('vin') . '%'); }); } return ApiResponse::OK->response([ 'records' => $records->select([ 'id', 'folio', 'vehicle_id', 'user_id', 'created_at' ])->paginate(config('app.pagination')) ]); } private function getVehicle(string $niv): array { $datos = $this->padronEstatalService->getVehiculoByNiv($niv); return $this->padronEstatalService->extraerDatosVehiculo($datos); } private function getOwner(string $niv): array { $datos = $this->padronEstatalService->getVehiculoByNiv($niv); return $this->padronEstatalService->extraerDatosPropietario($datos); } }