validate([ 'folio' => 'required|string|exists:records,folio', 'tag_number' => 'required|string|exists:tags,tag_number', 'niv' => 'required|string|exists:vehicle,niv' ]); $folio = $request->input('folio'); $tagNumber = $request->input('tag_number'); $niv = $request->input('niv'); $isStolen = $this->checkIfStolen($folio); if ($isStolen) { return ApiResponse::FORBIDDEN->response([ 'message' => 'El vehículo reporta robo. No se puede continuar con la actualización.', ]); } $record = 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', 'files:id,record_id,name_id,path,md5', 'files.catalogName:id,name', 'user:id,name,email', 'error:id,code,description' ]) ->select([ 'id', 'folio', 'vehicle_id', 'user_id', 'error_id', 'created_at', 'updated_at' ])->where('folio', $folio) ->first(); if (!$record) { return ApiResponse::NOT_FOUND->response([ 'message' => 'No se encontró el expediente', ]); } $vehicle = $record->vehicle; $tag = $vehicle->tag; if (!$tag || $tag->tag_number !== $tagNumber) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'El tag_number no coincide con el registrado en el expediente', ]); } if ($vehicle->niv !== $niv) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'El NIV no coincide con el registrado en el expediente', ]); } return ApiResponse::OK->response([ 'message' => 'Datos del vehículo obtenidos exitosamente', 'records' => $record, ]); } catch (Exception $e) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'Error de validación', 'error' => $e->getMessage(), ]); } } public function vehicleUpdate(VehicleUpdateRequest $request) { try { $folio = $request->input('folio'); $tagNumber = $request->input('tag_number'); $niv = $request->input('niv'); $isStolen = $this->checkIfStolen($folio); if ($isStolen) { return ApiResponse::FORBIDDEN->response([ 'niv' => $niv, 'stolen' => true, 'message' => 'El vehículo reporta robo. No se puede continuar con la actualización.', ]); } $record = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'error']) ->where('folio', $folio) ->first(); if (!$record) { return ApiResponse::NOT_FOUND->response([ 'message' => 'No se encontró el expediente', 'folio' => $folio, ]); } $vehicle = $record->vehicle; $tag = $vehicle->tag; if (!$tag || $tag->tag_number !== $tagNumber) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'El tag_number no coincide con el registrado en el expediente', ]); } if ($vehicle->niv !== $niv) { return ApiResponse::BAD_REQUEST->response([ 'message' => 'El NIV no coincide con el registrado en el expediente', ]); } DB::beginTransaction(); $ownerData = $this->getOwner(); $owner = Owner::updateOrCreate( ['rfc' => $ownerData['rfc']], [ 'name' => $ownerData['name'], 'paternal' => $ownerData['paternal'], 'maternal' => $ownerData['maternal'], 'curp' => $ownerData['curp'], 'address' => $ownerData['address'], ] ); $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::BAD_REQUEST->response([ 'message' => 'Algunos ids del catálogo de nombres no son válidos', 'provided_id' => $nameIds, 'valid_id' => $validIds, ]); } } foreach ($files as $indx => $file) { $nameId = $nameIds[$indx] ?? null; if ($nameId === null) { DB::rollBack(); return ApiResponse::BAD_REQUEST->response([ 'message' => "Falta el name_id para el archivo", ]); } // Buscar si ya existe un archivo con este name_id para este record $existingFile = File::where('record_id', $record->id) ->where('name_id', $nameId) ->first(); if ($existingFile) { // Eliminar el archivo físico anterior Storage::disk('public')->delete($existingFile->path); $replacedFiles[] = [ 'id' => $existingFile->id, 'name_id' => $nameId, 'old_path' => $existingFile->path, ]; // Eliminar el registro de la BD $existingFile->delete(); } // 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, 'replaced' => $existingFile !== null, ]; } } //Envio de datos $apiResponse = $this->sendToRepuveNacional($niv); $apiResponse["repuve_response"]["folio_ci"] = $record->folio; $apiResponse["repuve_response"]["identificador_ci"] = $tag->tag_number; if (isset($apiResponse['has_error']) && $apiResponse['has_error']) { $error = Error::where('code', $apiResponse['error_code'])->first(); if (!$error) { DB::rollBack(); return ApiResponse::INTERNAL_ERROR->response([ 'message' => 'Código de error no encontrado en el catálogo', 'error_code' => $apiResponse['error_code'], 'error_message' => $apiResponse['error_message'], ]); } $record->update([ 'error_id' => $error->id, 'api_response' => $apiResponse, 'error_occurred_at' => now(), ]); DB::commit(); return ApiResponse::NOT_ACCEPTABLE->response([ 'message' => 'Datos guardados con error. Corrija y vuelva a enviar.', 'has_error' => true, 'can_retry' => true, 'error' => [ 'code' => $error->code, 'description' => $error->description, 'occurred_at' => $record->error_occurred_at->toDateTimeString(), ], 'record' => [ 'id' => $record->id, 'folio' => $record->folio, ], ]); } $record->update([ 'error_id' => null, 'api_response' => $apiResponse, 'error_occurred_at' => null, ]); DB::commit(); return ApiResponse::OK->response([ 'message' => 'Vehículo actualizado exitosamente', 'has_error' => false, 'record' => [ 'id' => $record->id, 'folio' => $record->folio, 'updated_at' => $record->updated_at->toDateTimeString(), ], 'vehicle' => [ 'id' => $vehicle->id, 'placa' => $vehicle->placa, 'niv' => $vehicle->niv, 'marca' => $vehicle->marca, 'modelo' => $vehicle->modelo, 'color' => $vehicle->color, ], 'owner' => [ 'id' => $owner->id, 'full_name' => $owner->full_name, 'rfc' => $owner->rfc, ], 'tag' => [ 'id' => $tag->id, 'tag_number' => $tag->tag_number, 'status' => $tag->status, ], 'uploaded_files' => $uploadedFiles, 'replaced_count' => count($replacedFiles), 'total_files' => File::where('record_id', $record->id)->count(), ]); } catch (Exception $e) { return ApiResponse::INTERNAL_ERROR->response([ 'message' => 'Error al actualizar el vehículo', 'error' => $e->getMessage(), ]); } } private function checkIfStolen(string $folio): bool { // Aquí api servicio de REPUVE Nacional // simulamos con random return (bool) rand(0, 1); } private function sendToRepuveNacional(string $niv): array { // Enviar datos a API Repuve Nacional // Aquí se haría la llamada real a la API de Repuve Nacional // Por ahora simulamos con mock igual que InscriptionController // Respuesta exitosa mockup REPUVE $mockResponse = "OK:SIN INFORMACION|OTROS|SIN INFORMACION|10/07/2023|CENTRO|$niv|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 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; } return [ 'has_error' => false, 'error_code' => null, 'error_message' => null, 'timestamp' => now()->toDateTimeString(), 'niv' => $niv, 'repuve_response' => $jsonResponse, ]; } private function getOwner(): array { return [ 'name' => 'Nicolas', 'paternal' => 'Hernandez', 'maternal' => 'Castillo', 'rfc' => 'HECN660509HTCRSC01', 'curp' => 'HECN660509HTCRSC01', 'address' => 'Fracc Pomoca, Calle Armadillo MZ9 LT28', ]; } }