# app/api/v1/admin/companies.py """ Company management endpoints for admin. """ import logging from datetime import UTC, datetime from fastapi import APIRouter, Body, Depends, Path, Query from sqlalchemy.orm import Session from app.api.deps import get_current_admin_api from app.core.database import get_db from app.exceptions import CompanyHasVendorsException, ConfirmationRequiredException from app.services.company_service import company_service from models.database.user import User from models.schema.company import ( CompanyCreate, CompanyCreateResponse, CompanyDetailResponse, CompanyListResponse, CompanyResponse, CompanyTransferOwnership, CompanyTransferOwnershipResponse, CompanyUpdate, ) router = APIRouter(prefix="/companies") logger = logging.getLogger(__name__) @router.post("", response_model=CompanyCreateResponse) def create_company_with_owner( company_data: CompanyCreate, db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Create a new company with owner user account (Admin only). This endpoint: 1. Creates a new company record 2. Creates an owner user account with owner_email (if not exists) 3. Returns credentials (temporary password shown ONCE if new user created) **Email Fields:** - `owner_email`: Used for owner's login/authentication (stored in users.email) - `contact_email`: Public business contact (stored in companies.contact_email) Returns company details with owner credentials. """ company, owner_user, temp_password = company_service.create_company_with_owner( db, company_data ) db.commit() # ✅ ARCH: Commit at API level for transaction control return CompanyCreateResponse( company=CompanyResponse( id=company.id, name=company.name, description=company.description, owner_user_id=company.owner_user_id, contact_email=company.contact_email, contact_phone=company.contact_phone, website=company.website, business_address=company.business_address, tax_number=company.tax_number, is_active=company.is_active, is_verified=company.is_verified, created_at=company.created_at.isoformat(), updated_at=company.updated_at.isoformat(), ), owner_user_id=owner_user.id, owner_username=owner_user.username, owner_email=owner_user.email, temporary_password=temp_password or "N/A (Existing user)", login_url="http://localhost:8000/admin/login", ) @router.get("", response_model=CompanyListResponse) def get_all_companies( skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=1000), search: str | None = Query(None, description="Search by company name"), is_active: bool | None = Query(None), is_verified: bool | None = Query(None), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """Get all companies with filtering (Admin only).""" companies, total = company_service.get_companies( db, skip=skip, limit=limit, search=search, is_active=is_active, is_verified=is_verified, ) return CompanyListResponse( companies=[ CompanyResponse( id=c.id, name=c.name, description=c.description, owner_user_id=c.owner_user_id, contact_email=c.contact_email, contact_phone=c.contact_phone, website=c.website, business_address=c.business_address, tax_number=c.tax_number, is_active=c.is_active, is_verified=c.is_verified, created_at=c.created_at.isoformat(), updated_at=c.updated_at.isoformat(), ) for c in companies ], total=total, skip=skip, limit=limit, ) @router.get("/{company_id}", response_model=CompanyDetailResponse) def get_company_details( company_id: int = Path(..., description="Company ID"), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Get detailed company information including vendor counts (Admin only). """ company = company_service.get_company_by_id(db, company_id) # Count vendors vendor_count = len(company.vendors) active_vendor_count = sum(1 for v in company.vendors if v.is_active) # Build vendors list for detail view vendors_list = [ { "id": v.id, "vendor_code": v.vendor_code, "name": v.name, "subdomain": v.subdomain, "is_active": v.is_active, "is_verified": v.is_verified, } for v in company.vendors ] return CompanyDetailResponse( id=company.id, name=company.name, description=company.description, owner_user_id=company.owner_user_id, owner_email=company.owner.email if company.owner else None, owner_username=company.owner.username if company.owner else None, contact_email=company.contact_email, contact_phone=company.contact_phone, website=company.website, business_address=company.business_address, tax_number=company.tax_number, is_active=company.is_active, is_verified=company.is_verified, created_at=company.created_at.isoformat(), updated_at=company.updated_at.isoformat(), vendor_count=vendor_count, active_vendor_count=active_vendor_count, vendors=vendors_list, ) @router.put("/{company_id}", response_model=CompanyResponse) def update_company( company_id: int = Path(..., description="Company ID"), company_update: CompanyUpdate = Body(...), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Update company information (Admin only). **Can update:** - Basic info: name, description - Business contact: contact_email, contact_phone, website - Business details: business_address, tax_number - Status: is_active, is_verified **Cannot update:** - `owner_user_id` (would require ownership transfer feature) """ company = company_service.update_company(db, company_id, company_update) db.commit() # ✅ ARCH: Commit at API level for transaction control return CompanyResponse( id=company.id, name=company.name, description=company.description, owner_user_id=company.owner_user_id, contact_email=company.contact_email, contact_phone=company.contact_phone, website=company.website, business_address=company.business_address, tax_number=company.tax_number, is_active=company.is_active, is_verified=company.is_verified, created_at=company.created_at.isoformat(), updated_at=company.updated_at.isoformat(), ) @router.put("/{company_id}/verification", response_model=CompanyResponse) def toggle_company_verification( company_id: int = Path(..., description="Company ID"), verification_data: dict = Body(..., example={"is_verified": True}), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Toggle company verification status (Admin only). Request body: { "is_verified": true/false } """ is_verified = verification_data.get("is_verified", False) company = company_service.toggle_verification(db, company_id, is_verified) db.commit() # ✅ ARCH: Commit at API level for transaction control return CompanyResponse( id=company.id, name=company.name, description=company.description, owner_user_id=company.owner_user_id, contact_email=company.contact_email, contact_phone=company.contact_phone, website=company.website, business_address=company.business_address, tax_number=company.tax_number, is_active=company.is_active, is_verified=company.is_verified, created_at=company.created_at.isoformat(), updated_at=company.updated_at.isoformat(), ) @router.put("/{company_id}/status", response_model=CompanyResponse) def toggle_company_status( company_id: int = Path(..., description="Company ID"), status_data: dict = Body(..., example={"is_active": True}), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Toggle company active status (Admin only). Request body: { "is_active": true/false } """ is_active = status_data.get("is_active", True) company = company_service.toggle_active(db, company_id, is_active) db.commit() # ✅ ARCH: Commit at API level for transaction control return CompanyResponse( id=company.id, name=company.name, description=company.description, owner_user_id=company.owner_user_id, contact_email=company.contact_email, contact_phone=company.contact_phone, website=company.website, business_address=company.business_address, tax_number=company.tax_number, is_active=company.is_active, is_verified=company.is_verified, created_at=company.created_at.isoformat(), updated_at=company.updated_at.isoformat(), ) @router.post( "/{company_id}/transfer-ownership", response_model=CompanyTransferOwnershipResponse, ) def transfer_company_ownership( company_id: int = Path(..., description="Company ID"), transfer_data: CompanyTransferOwnership = Body(...), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Transfer company ownership to another user (Admin only). **This is a critical operation that:** - Changes the company's owner_user_id - Updates all associated vendors' owner_user_id - Creates audit trail ⚠️ **This action is logged and should be used carefully.** **Requires:** - `new_owner_user_id`: ID of user who will become owner - `confirm_transfer`: Must be true - `transfer_reason`: Optional reason for audit trail """ company, old_owner, new_owner = company_service.transfer_ownership( db, company_id, transfer_data ) db.commit() # ✅ ARCH: Commit at API level for transaction control return CompanyTransferOwnershipResponse( message="Ownership transferred successfully", company_id=company.id, company_name=company.name, old_owner={ "id": old_owner.id, "username": old_owner.username, "email": old_owner.email, }, new_owner={ "id": new_owner.id, "username": new_owner.username, "email": new_owner.email, }, transferred_at=datetime.now(UTC), transfer_reason=transfer_data.transfer_reason, ) @router.delete("/{company_id}") def delete_company( company_id: int = Path(..., description="Company ID"), confirm: bool = Query(False, description="Must be true to confirm deletion"), db: Session = Depends(get_db), current_admin: User = Depends(get_current_admin_api), ): """ Delete company and all associated vendors (Admin only). ⚠️ **WARNING: This is destructive and will delete:** - Company account - All vendors under this company - All products under those vendors - All orders, customers, team members Requires confirmation parameter: `confirm=true` """ if not confirm: raise ConfirmationRequiredException( operation="delete_company", message="Deletion requires confirmation parameter: confirm=true", ) # Get company to check vendor count company = company_service.get_company_by_id(db, company_id) vendor_count = len(company.vendors) if vendor_count > 0: raise CompanyHasVendorsException(company_id, vendor_count) company_service.delete_company(db, company_id) db.commit() # ✅ ARCH: Commit at API level for transaction control return {"message": f"Company {company_id} deleted successfully"}