"""Organization management API routes (users only).""" import uuid from fastapi import APIRouter, Depends, HTTPException from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.core.security import get_current_admin, get_current_user from app.database import get_db from app.models.user import User, Identity from app.schemas.schemas import UserOut, UserUpdate from sqlalchemy.orm import selectinload router = APIRouter(prefix="/org", tags=["organization"]) # ─── Users Management ────────────────────────────────── @router.get("/users", response_model=list[UserOut]) async def list_users( tenant_id: uuid.UUID | None = None, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): """List users, optionally filtered by tenant.""" query = ( select(User) .options(selectinload(User.identity)) .where(User.is_active == True) ) target_tenant_id = current_user.tenant_id if current_user.role in ("platform_admin", "org_admin") and tenant_id: target_tenant_id = tenant_id if target_tenant_id: query = query.where(User.tenant_id == target_tenant_id) query = query.order_by(User.display_name) result = await db.execute(query) return [UserOut.model_validate(u) for u in result.scalars().all()] @router.patch("/users/{user_id}", response_model=UserOut) async def admin_update_user( user_id: uuid.UUID, data: UserUpdate, current_user: User = Depends(get_current_admin), db: AsyncSession = Depends(get_db), ): """Admin update user profile.""" result = await db.execute( select(User) .options(selectinload(User.identity)) .where(User.id == user_id) ) user = result.scalar_one_or_none() if not user: raise HTTPException(status_code=404, detail="User not found") update_data = data.model_dump(exclude_unset=True) # Validate email uniqueness within tenant if changing if "email" in update_data and update_data["email"] != user.email: existing = await db.execute( select(User) .join(Identity, User.identity_id == Identity.id) .where( Identity.email == update_data["email"], User.tenant_id == user.tenant_id, User.id != user.id, ) ) if existing.scalar_one_or_none(): raise HTTPException(status_code=409, detail="Email already registered") # Validate mobile uniqueness within tenant if changing if "primary_mobile" in update_data and update_data["primary_mobile"] != user.primary_mobile: existing = await db.execute( select(User) .join(Identity, User.identity_id == Identity.id) .where( Identity.phone == update_data["primary_mobile"], User.tenant_id == user.tenant_id, User.id != user.id, ) ) if existing.scalar_one_or_none(): raise HTTPException(status_code=409, detail="Mobile already registered") for field, value in update_data.items(): setattr(user, field, value) await db.flush() # Sync email/phone to OrgMember if changed if "email" in update_data or "primary_mobile" in update_data: from app.services.registration_service import registration_service await registration_service.sync_org_member_contact_from_user( db, user, sync_email="email" in update_data, sync_phone="primary_mobile" in update_data, ) return UserOut.model_validate(user)