# app/modules/tenancy/routes/api/merchant.py """ Tenancy module merchant API routes. Provides merchant-facing API endpoints for the merchant portal: - /account/stores - List merchant's stores - /account/profile - Get/update merchant 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 logger = logging.getLogger(__name__) router = APIRouter() ROUTE_CONFIG = { "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 # ============================================================================ # ENDPOINTS # ============================================================================ @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} @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, } @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, }