# app/modules/tenancy/routes/api/merchant.py """ Tenancy module merchant API routes. Aggregates all merchant tenancy routes: - /auth/* - Merchant authentication (login, logout, /me) - /account/* - Merchant account management (stores, profile) Auto-discovered by the route system (merchant.py in routes/api/). """ import logging from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Request from pydantic import BaseModel, EmailStr from sqlalchemy.orm import Session from app.api.deps import get_current_merchant_from_cookie_or_header from app.core.database import get_db from app.modules.tenancy.models import Merchant from models.schema.auth import UserContext from .merchant_auth import merchant_auth_router logger = logging.getLogger(__name__) router = APIRouter() # Include auth routes (/auth/login, /auth/logout, /auth/me) router.include_router(merchant_auth_router, tags=["merchant-auth"]) # Account routes are defined below with /account prefix _account_router = APIRouter(prefix="/account") # ============================================================================ # SCHEMAS # ============================================================================ class MerchantProfileUpdate(BaseModel): """Schema for updating merchant profile information.""" name: str | None = None contact_email: EmailStr | None = None contact_phone: str | None = None website: str | None = None business_address: str | None = None tax_number: str | None = None # ============================================================================ # HELPERS # ============================================================================ def _get_user_merchant(db: Session, user_context: UserContext) -> Merchant: """ Get the first active merchant owned by the authenticated user. Args: db: Database session user_context: Authenticated user context Returns: Merchant: The user's active merchant Raises: HTTPException: 404 if user does not own any active merchant """ merchant = ( db.query(Merchant) .filter( Merchant.owner_user_id == user_context.id, Merchant.is_active == True, # noqa: E712 ) .first() ) if not merchant: raise HTTPException(status_code=404, detail="Merchant not found") return merchant # ============================================================================ # ACCOUNT ENDPOINTS # ============================================================================ @_account_router.get("/stores") async def merchant_stores( request: Request, current_user: UserContext = Depends(get_current_merchant_from_cookie_or_header), db: Session = Depends(get_db), ): """ List all stores belonging to the merchant. Returns a list of store summary dicts with basic info for each store owned by the authenticated merchant. """ merchant = _get_user_merchant(db, current_user) stores = [] for store in merchant.stores: stores.append( { "id": store.id, "name": store.name, "store_code": store.store_code, "is_active": store.is_active, "created_at": store.created_at.isoformat() if store.created_at else None, } ) return {"stores": stores} @_account_router.get("/profile") async def merchant_profile( request: Request, current_user: UserContext = Depends(get_current_merchant_from_cookie_or_header), db: Session = Depends(get_db), ): """ Get the authenticated merchant's profile information. Returns merchant details including contact info, business details, and verification status. """ merchant = _get_user_merchant(db, current_user) return { "id": merchant.id, "name": merchant.name, "contact_email": merchant.contact_email, "contact_phone": merchant.contact_phone, "website": merchant.website, "business_address": merchant.business_address, "tax_number": merchant.tax_number, "is_verified": merchant.is_verified, } @_account_router.put("/profile") async def update_merchant_profile( request: Request, profile_data: MerchantProfileUpdate, current_user: UserContext = Depends(get_current_merchant_from_cookie_or_header), db: Session = Depends(get_db), ): """ Update the authenticated merchant's profile information. Accepts partial updates - only provided fields are changed. """ merchant = _get_user_merchant(db, current_user) # Apply only the fields that were explicitly provided update_data = profile_data.model_dump(exclude_unset=True) for field_name, value in update_data.items(): setattr(merchant, field_name, value) db.commit() db.refresh(merchant) logger.info( f"Merchant profile updated: merchant_id={merchant.id}, " f"user={current_user.username}, fields={list(update_data.keys())}" ) return { "id": merchant.id, "name": merchant.name, "contact_email": merchant.contact_email, "contact_phone": merchant.contact_phone, "website": merchant.website, "business_address": merchant.business_address, "tax_number": merchant.tax_number, "is_verified": merchant.is_verified, } # Include account routes in main router router.include_router(_account_router, tags=["merchant-account"])