#!/usr/bin/env python3 """ Repair script for user gy.krisztina76@gmail.com (ID 100). This user went through light registration + email verification but the KYC completion failed because of two bugs: 1. verify_email() used Person.user_id (NULL) instead of User.person_id 2. GamificationService.award_points() did internal commit conflicting with outer transaction This script manually creates all the missing infrastructure that should have been created by complete_kyc(): - Activate Person - Create Organization (personal) - Create Branch - Create OrganizationMember - Create Wallet - Create UserStats - Set user.scope_id """ import asyncio import logging import sys from datetime import datetime, timezone # Add the backend directory to the path sys.path.insert(0, '/app/backend') from sqlalchemy import select, and_ from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker from app.core.config import settings # Setup logging logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s') logger = logging.getLogger("repair_kyc") # Database URL from settings DATABASE_URL = settings.DATABASE_URL logger.info(f"Using database: {DATABASE_URL}") # Create engine and session engine = create_async_engine(DATABASE_URL, echo=False) AsyncSessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) async def repair_user(): async with AsyncSessionLocal() as db: try: # 1. Find the user from app.models.identity.identity import User, Person, Wallet from app.models.marketplace.organization import Organization, OrganizationMember, Branch from app.models.gamification.gamification import UserStats, PointsLedger stmt = select(User).where(User.email == 'gy.krisztina76@gmail.com') user = (await db.execute(stmt)).scalar_one_or_none() if not user: logger.error("User not found!") return logger.info(f"Found user: ID={user.id}, email={user.email}, person_id={user.person_id}") logger.info(f" is_active={user.is_active}, scope_id={user.scope_id}") # 2. Find and activate Person person_stmt = select(Person).where(Person.id == user.person_id) person = (await db.execute(person_stmt)).scalar_one_or_none() if person: logger.info(f"Found Person: ID={person.id}, is_active={person.is_active}, user_id={person.user_id}") person.is_active = True person.user_id = user.id logger.info(f" -> Activated Person, set user_id={user.id}") else: logger.warning(f"Person with id={user.person_id} not found!") # 3. Check if Organization already exists org_stmt = select(Organization).where(Organization.owner_id == user.id) existing_org = (await db.execute(org_stmt)).scalar_one_or_none() if existing_org: logger.info(f"Organization already exists: ID={existing_org.id}, name={existing_org.name}") new_org = existing_org else: # Create personal Organization from app.core.security import generate_secure_slug org_name = f"{person.last_name} Flotta" if person else "Personal Flotta" folder_slug = generate_secure_slug(12) new_org = Organization( name=org_name, full_name=org_name, display_name=org_name, folder_slug=folder_slug, org_type="individual", status="ACTIVE", is_deleted=False, is_active=True, is_verified=False, is_anonymized=False, default_currency="HUF", country_code="HU", language="hu", subscription_plan="FREE", base_asset_limit=3, purchased_extra_slots=0, notification_settings={}, external_integration_config={}, visual_settings={}, is_ownership_transferable=False, owner_id=user.id, first_registered_at=datetime.now(timezone.utc), current_lifecycle_started_at=datetime.now(timezone.utc), lifecycle_index=1, created_at=datetime.now(timezone.utc) ) db.add(new_org) await db.flush() logger.info(f"Created Organization: ID={new_org.id}, name={org_name}") # 4. Check if Branch already exists branch_stmt = select(Branch).where(Branch.organization_id == new_org.id) existing_branch = (await db.execute(branch_stmt)).scalar_one_or_none() if existing_branch: logger.info(f"Branch already exists: ID={existing_branch.id}, name={existing_branch.name}") else: import uuid branch = Branch( id=uuid.uuid4(), organization_id=new_org.id, name="Home Base", is_main=True, opening_hours={}, branch_rating=0.0, status="ACTIVE", is_deleted=False, created_at=datetime.now(timezone.utc) ) db.add(branch) await db.flush() logger.info(f"Created Branch: ID={branch.id}") # 5. Check if OrganizationMember already exists member_stmt = select(OrganizationMember).where( and_( OrganizationMember.organization_id == new_org.id, OrganizationMember.user_id == user.id ) ) existing_member = (await db.execute(member_stmt)).scalar_one_or_none() if existing_member: logger.info(f"OrganizationMember already exists: ID={existing_member.id}") else: member = OrganizationMember( organization_id=new_org.id, user_id=user.id, role="OWNER", permissions={}, is_permanent=True, is_verified=True ) db.add(member) await db.flush() logger.info(f"Created OrganizationMember: user_id={user.id}, org_id={new_org.id}") # 6. Check if Wallet already exists wallet_stmt = select(Wallet).where(Wallet.user_id == user.id) existing_wallet = (await db.execute(wallet_stmt)).scalar_one_or_none() if existing_wallet: logger.info(f"Wallet already exists: ID={existing_wallet.id}") else: wallet = Wallet( user_id=user.id, earned_credits=0, purchased_credits=0, service_coins=0, currency="HUF" ) db.add(wallet) await db.flush() logger.info(f"Created Wallet for user_id={user.id}") # 7. Check if UserStats already exists stats_stmt = select(UserStats).where(UserStats.user_id == user.id) existing_stats = (await db.execute(stats_stmt)).scalar_one_or_none() if existing_stats: logger.info(f"UserStats already exists: user_id={existing_stats.user_id}") else: stats = UserStats( user_id=user.id, total_xp=500, # KYC bonus social_points=0, current_level=1, penalty_points=0, restriction_level=0, updated_at=datetime.now(timezone.utc), penalty_quota_remaining=0, places_discovered=0, places_validated=0 ) db.add(stats) # Add PointsLedger entry for KYC bonus ledger = PointsLedger( user_id=user.id, points=500, reason="KYC_VERIFICATION_REPAIR" ) db.add(ledger) await db.flush() logger.info(f"Created UserStats and PointsLedger for user_id={user.id}") # 8. Set user.scope_id if not set if not user.scope_id: user.scope_id = str(new_org.id) logger.info(f"Set user.scope_id = {new_org.id}") # 9. Ensure user.is_active is True if not user.is_active: user.is_active = True logger.info("Activated user") await db.commit() logger.info("=" * 50) logger.info("REPAIR COMPLETED SUCCESSFULLY!") logger.info("=" * 50) # Verify logger.info("\n--- Verification ---") logger.info(f"User: ID={user.id}, is_active={user.is_active}, scope_id={user.scope_id}") if person: logger.info(f"Person: ID={person.id}, is_active={person.is_active}, user_id={person.user_id}") logger.info(f"Organization: ID={new_org.id}, name={new_org.name}") except Exception as e: await db.rollback() logger.error(f"Repair failed: {e}") import traceback traceback.print_exc() finally: await db.close() await engine.dispose() if __name__ == "__main__": asyncio.run(repair_user())