import re from fastapi import APIRouter, Depends, HTTPException, status from app.core.deps import current_principal, current_user, require_client_roles, Principal from app.models import Organization, OrgMember, Plan, Subscription, StorageUsage, AuditLog from app.models.enums import ClientRole from app.schemas.common import OrgCreate router = APIRouter(tags=["organization"]) @router.get("/me") async def me(p: Principal = Depends(current_principal)): """Who am I — used by the frontend to route admins vs clients.""" return { "id": str(p.user.id), "name": p.user.name, "email": p.user.email, "email_verified": p.user.email_verified, "is_product_admin": p.user.is_product_admin, "product_admin_role": p.admin_role, "client_role": p.client_role, "has_org": p.org is not None, "org_name": p.org.name if p.org else None, } def _slug(name: str) -> str: base = re.sub(r"[^a-z0-9]+", "-", name.lower()).strip("-") return base or "org" @router.post("/organization", status_code=201) async def create_org(body: OrgCreate, user=Depends(current_user)): existing = await OrgMember.find_one(OrgMember.user_id == str(user.id)) if existing: raise HTTPException(status.HTTP_409_CONFLICT, "User already belongs to an organization") free = await Plan.find_one(Plan.name == "Free") slug = _slug(body.name) if await Organization.find_one(Organization.slug == slug): slug = f"{slug}-{str(user.id)[-4:]}" org = Organization(name=body.name, slug=slug, gstin=body.gstin, pan=body.pan, contact_email=body.contact_email or user.email,address=body.address, phone=body.phone, website=body.website, plan_id=str(free.id) if free else None) await org.insert() await OrgMember(org_id=str(org.id), user_id=str(user.id), role=ClientRole.admin, status="active").insert() await StorageUsage(org_id=str(org.id), total_bytes=(free.storage_gb if free else 5) * 1024 ** 3).insert() if free: await Subscription(org_id=str(org.id), plan_id=str(free.id), plan_name=free.name).insert() await AuditLog(org_id=str(org.id), user_id=str(user.id), user_name=user.name, action="org.create", module="organization", details=f"Created org {org.name}").insert() return {"id": str(org.id), "name": org.name, "slug": org.slug} @router.get("/organization") async def get_org(p: Principal = Depends(current_principal)): if not p.org: raise HTTPException(status.HTTP_404_NOT_FOUND, "No organization") plan = await Plan.get(p.org.plan_id) if p.org.plan_id else None return { "id": str(p.org.id), "name": p.org.name, "slug": p.org.slug, "logo_url": p.org.logo_url, "gstin": p.org.gstin, "pan": p.org.pan, "timezone": p.org.timezone, "language": p.org.language, "subscription_status": p.org.subscription_status,"address": p.org.address, "phone": p.org.phone, "website": p.org.website, "contact_email": p.org.contact_email, "address":p.org.address,"country": p.org.country, "product_count": p.org.product_count, "user_count": p.org.user_count, "plan": {"name": plan.name, "product_limit": plan.product_limit, "user_limit": plan.user_limit, "price": plan.price} if plan else None, "your_role": p.client_role, } @router.put("/organization") async def update_org(body: dict, p: Principal = Depends(require_client_roles(ClientRole.admin))): allowed = {"name", "logo_url", "gstin", "pan", "address", "contact_email","country", "phone", "website", "timezone", "language"} for k, v in body.items(): if k in allowed: setattr(p.org, k, v) await p.org.save() await AuditLog( org_id=str(p.org.id), user_id=str(p.user.id), user_name=p.user.name, user_role=p.client_role, action="organization.update", module="organization", details=f"Updated organization {p.org.name}", ).insert() return {"message": "updated"} from datetime import datetime from typing import Optional @router.get("/audit-logs") async def org_audit_logs( p: Principal = Depends(require_client_roles(ClientRole.admin)), limit: int = 50, module: Optional[str] = None, user: Optional[str] = None, from_date: Optional[str] = None, to_date: Optional[str] = None, ): query = AuditLog.find(AuditLog.org_id == str(p.org.id)) if module: query = query.find(AuditLog.module == module) if user: query = query.find(AuditLog.user_name == user) if from_date: query = query.find( AuditLog.created_at >= datetime.fromisoformat(from_date) ) if to_date: query = query.find( AuditLog.created_at <= datetime.fromisoformat(to_date + "T23:59:59") ) logs = await query.sort(-AuditLog.created_at).limit(limit).to_list() return [ { "at": l.created_at, "user": l.user_name, "role": l.user_role, "action": l.action, "module": l.module, "details": l.details, "ip": l.ip, } for l in logs ] @router.get("/roles") async def list_roles(): return [ {"role": "admin", "label": "Admin", "description": "Full access within the organization: products, QRs, batches, compliance, users, billing."}, {"role": "finance", "label": "Finance", "description": "Billing, invoices, payment methods, subscription within the org only."}, {"role": "user", "label": "User", "description": "Create products, generate QRs, manage batches, view analytics. No billing/invites."}, ]