input('folio'); $tagNumber = $request->input('tag_number'); $vin = $request->input('vin'); // 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($folio); 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(); $ownerData = $this->getOwner(); // 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(), ]); // Procesar archivos $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); $catalogName = CatalogNameImg::firstOrCreate(['name' => $customName]); $extension = $file->getClientOriginalExtension(); $fileName = $customName . '_' . date('dmY_His') . '.' . $extension; $path = $file->storeAs('records', $fileName, 'public'); $md5 = md5_file($file->getRealPath()); $fileRecord = File::create([ 'name_id' => $catalogName->id, '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($vin); $apiResponse["repuve_response"]["folio_ci"] = $folio; $apiResponse["repuve_response"]["identificador_ci"] = $tagNumber; // 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) { DB::rollBack(); return ApiResponse::BAD_REQUEST->response([ 'message' => 'Código de error no encontrado en el catálogo', 'error_code' => $apiResponse['error_code'], 'error_message' => $apiResponse['error_message'], ]); } // 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(); // 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' => $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, 'folio' => $tag->folio, 'status' => $tag->status, ], '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 $folio): bool { // Aquí api servicio de REPUVE Nacional // simulamos con random return (bool) rand(0, 1); } 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 /* $hasError = (bool) rand(0, 1); 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(), 'vin' => $vin, 'response_data' => null, ]; } return [ 'has_error' => true, 'error_code' => $error->code, 'error_message' => $error->description, 'timestamp' => now()->toDateTimeString(), 'vin' => $vin, '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; } return [ 'has_error' => false, 'error_code' => null, 'error_message' => null, 'timestamp' => now()->toDateTimeString(), 'vin' => $vin, 'repuve_response' => $jsonResponse, ]; } 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.' ]); $query = Record::with(['vehicle.owner', 'vehicle.tag', 'files', 'user', 'error'])->orderBy('id', 'ASC'); if ($request->filled('folio')) { $query->whereHas('vehicle.tag', function ($q) use ($request) { $q->where('folio', 'LIKE', '%' . $request->input('folio') . '%'); }); } elseif ($request->filled('placa')) { $query->whereHas('vehicle', function ($q) use ($request) { $q->where('placa', 'LIKE', '%' . $request->input('placa') . '%'); }); } elseif ($request->filled('vin')) { $query->whereHas('vehicle', function ($q) use ($request) { $q->where('niv', 'LIKE', '%' . $request->input('vin') . '%'); }); } $perPage = $request->input('per_page', 20); $records = $query->paginate($perPage); if ($records->isEmpty()) { return ApiResponse::NOT_FOUND->response([ 'message' => 'No se encontraron expedientes con los criterios proporcionados.', 'records' => [], 'pagination' => [ 'current_page' => 1, 'total_pages' => 0, 'total_records' => 0, 'per_page' => $perPage, ], ]); } return ApiResponse::OK->response([ 'message' => 'Expedientes encontrados exitosamente', 'records' => $records->map(function ($record) { return [ 'id' => $record->id, 'folio' => $record->folio, 'created_at' => $record->created_at->toDateTimeString(), 'vehicle' => [ 'id' => $record->vehicle->id, 'placa' => $record->vehicle->placa, 'niv' => $record->vehicle->niv, 'marca' => $record->vehicle->marca, 'modelo' => $record->vehicle->modelo, 'color' => $record->vehicle->color, 'clase_veh' => $record->vehicle->clase_veh, ], 'owner' => [ 'id' => $record->vehicle->owner->id, 'full_name' => $record->vehicle->owner->full_name, 'rfc' => $record->vehicle->owner->rfc, ], 'tag' => [ 'id' => $record->vehicle->tag ? $record->vehicle->tag->id : null, 'folio' => $record->vehicle->tag ? $record->vehicle->tag->folio : null, 'status' => $record->vehicle->tag ? $record->vehicle->tag->status : null, ], 'files' => $record->files->map(function ($file) { return [ 'id' => $file->id, 'name' => $file->name, 'path' => $file->path, 'url' => $file->url, ]; }), ]; }), 'pagination' => [ 'current_page' => $records->currentPage(), 'total_pages' => $records->lastPage(), 'total_records' => $records->total(), 'per_page' => $records->perPage(), 'from' => $records->firstItem(), 'to' => $records->lastItem(), ], ]); } private function getVehicle(): array { $apiResponse = [ "error" => 0, "datos" => [[ "entidadfederativa" => null, "ofcexp" => "INTERNET", "fechaexp" => "04/01/2022", "placa" => "WNU700B", "tarjetacir" => "2020-WNU700B", "marca" => "CHEVROLET G.M.C.", "submarca" => "AVEO", "version" => "PAQ. \"A\" LS", "clase_veh" => "AUTOMOVIL", "tipo_veh" => "SEDAN", "tipo_uso" => "PARTICULAR", "modelo" => "2022", "color" => "BLANCO", "motor" => "H. EN WUHANLL,SGM", "niv" => "LSGHD52H0ND032457", "rfv" => null, "numptas" => "4", "observac" => null, "tipopers" => 1, "curp" => null, "rfc" => "GME111116GJA", "pasaporte" => null, "licencia" => null, "nombre" => "GOLSYSTEMS DE MEXICO S DE RL DE CV", "ap_paterno" => null, "ap_materno" => null, "ent_fed" => "27", "munic" => "04", "callep" => "C BUGAMBILIAS ", "num_ext" => "118", "num_int" => null, "colonia" => "FRACC. BLANCAS MARIPOSAS", "cp" => "86179", "cve_vehi" => "0037804", "nrpv" => "5BK9MDO0", "fe_act" => "10/01/2025", "tipo_mov" => "1" ]] ]; $data = $apiResponse['datos'][0]; // Retornar datos mapeados para el vehículo return [ 'placa' => $data['placa'], 'niv' => $data['niv'], 'marca' => $data['marca'], 'linea' => $data['submarca'], 'sublinea' => $data['version'], 'modelo' => $data['modelo'], 'color' => $data['color'], 'numero_motor' => $data['motor'], 'clase_veh' => $data['clase_veh'], 'tipo_servicio' => $data['tipo_uso'], 'rfv' => $data['rfv'], 'rfc' => $data['rfc'], 'ofcexpedicion' => $data['ofcexp'], 'fechaexpedicion' => $data['fechaexp'], 'tipo_veh' => $data['tipo_veh'], 'numptas' => $data['numptas'], 'observac' => $data['observac'], 'cve_vehi' => $data['cve_vehi'], 'tipo_mov' => $data['tipo_mov'], ]; } private function getOwner(): array { $apiResponse = [ "error" => 0, "datos" => [[ "entidadfederativa" => null, "ofcexp" => "INTERNET", "fechaexp" => "04/01/2022", "placa" => "WNU700B", "tarjetacir" => "2020-WNU700B", "marca" => "CHEVROLET G.M.C.", "submarca" => "AVEO", "version" => "PAQ. \"A\" LS", "clase_veh" => "AUTOMOVIL", "tipo_veh" => "SEDAN", "tipo_uso" => "PARTICULAR", "modelo" => "2022", "color" => "BLANCO", "motor" => "H. EN WUHANLL,SGM", "niv" => "LSGHD52H0ND032457", "rfv" => null, "numptas" => "4", "observac" => null, "tipopers" => 1, "curp" => null, "rfc" => "GME111116GJA", "pasaporte" => null, "licencia" => null, "nombre" => "GOLSYSTEMS DE MEXICO S DE RL DE CV", "ap_paterno" => null, "ap_materno" => null, "ent_fed" => "27", "munic" => "04", "callep" => "C BUGAMBILIAS ", "num_ext" => "118", "num_int" => null, "colonia" => "FRACC. BLANCAS MARIPOSAS", "cp" => "86179", "cve_vehi" => "0037804", "nrpv" => "5BK9MDO0", "fe_act" => "10/01/2025", "tipo_mov" => "1" ]] ]; $data = $apiResponse['datos'][0]; // Construir dirección completa $addressParts = array_filter([ $data['callep'], $data['num_ext'] ? "Num {$data['num_ext']}" : null, $data['num_int'] ? "Int {$data['num_int']}" : null, $data['colonia'], $data['cp'] ? "CP {$data['cp']}" : null, $data['munic'] ? "Mun {$data['munic']}" : null, $data['ent_fed'] ? "Edo {$data['ent_fed']}" : null, ]); $address = implode(', ', $addressParts); // Retornar datos mapeados para el propietario return [ 'name' => $data['nombre'], 'paternal' => $data['ap_paterno'], 'maternal' => $data['ap_materno'], 'rfc' => $data['rfc'], 'curp' => $data['curp'], 'address' => $address, 'tipopers' => $data['tipopers'], 'pasaporte' => $data['pasaporte'], 'licencia' => $data['licencia'], 'ent_fed' => $data['ent_fed'], 'munic' => $data['munic'], 'callep' => $data['callep'], 'num_ext' => $data['num_ext'], 'num_int' => $data['num_int'], 'colonia' => $data['colonia'], 'cp' => $data['cp'], ]; } }