# app/api/v1/shop/orders.py """ Shop Orders API (Customer authenticated) Endpoints for managing customer orders in shop frontend. Uses vendor from request.state (injected by VendorContextMiddleware). Requires customer authentication - get_current_customer_api validates that customer token vendor_id matches the URL vendor. Customer Context: get_current_customer_api returns Customer directly (not User), with vendor validation already performed. """ import logging from fastapi import APIRouter, Depends, Path, Query, Request from sqlalchemy.orm import Session from app.api.deps import get_current_customer_api from app.core.database import get_db from app.exceptions import VendorNotFoundException from app.services.order_service import order_service from models.database.customer import Customer from models.schema.order import ( OrderCreate, OrderDetailResponse, OrderListResponse, OrderResponse, ) router = APIRouter() logger = logging.getLogger(__name__) @router.post("/orders", response_model=OrderResponse) def place_order( request: Request, order_data: OrderCreate, customer: Customer = Depends(get_current_customer_api), db: Session = Depends(get_db), ): """ Place a new order for current vendor. Vendor is automatically determined from request context. Customer must be authenticated to place an order. Creates an order from the customer's cart. Request Body: - Order data including shipping address, payment method, etc. """ # Get vendor from middleware (already validated by get_current_customer_api) vendor = getattr(request.state, "vendor", None) if not vendor: raise VendorNotFoundException("context", identifier_type="subdomain") logger.debug( f"[SHOP_API] place_order for customer {customer.id}", extra={ "vendor_id": vendor.id, "vendor_code": vendor.subdomain, "customer_id": customer.id, }, ) # Create order order = order_service.create_order( db=db, vendor_id=vendor.id, order_data=order_data ) db.commit() logger.info( f"Order {order.order_number} placed for vendor {vendor.subdomain}, " f"total: €{order.total_amount:.2f}", extra={ "order_id": order.id, "order_number": order.order_number, "customer_id": customer.id, "total_amount": float(order.total_amount), }, ) # TODO: Update customer stats # TODO: Clear cart # TODO: Send order confirmation email return OrderResponse.model_validate(order) @router.get("/orders", response_model=OrderListResponse) def get_my_orders( request: Request, skip: int = Query(0, ge=0), limit: int = Query(50, ge=1, le=100), customer: Customer = Depends(get_current_customer_api), db: Session = Depends(get_db), ): """ Get order history for authenticated customer. Vendor is automatically determined from request context. Returns all orders placed by the authenticated customer. Query Parameters: - skip: Number of orders to skip (pagination) - limit: Maximum number of orders to return """ # Get vendor from middleware (already validated by get_current_customer_api) vendor = getattr(request.state, "vendor", None) if not vendor: raise VendorNotFoundException("context", identifier_type="subdomain") logger.debug( f"[SHOP_API] get_my_orders for customer {customer.id}", extra={ "vendor_id": vendor.id, "vendor_code": vendor.subdomain, "customer_id": customer.id, "skip": skip, "limit": limit, }, ) # Get orders orders, total = order_service.get_customer_orders( db=db, vendor_id=vendor.id, customer_id=customer.id, skip=skip, limit=limit ) return OrderListResponse( orders=[OrderResponse.model_validate(o) for o in orders], total=total, skip=skip, limit=limit, ) @router.get("/orders/{order_id}", response_model=OrderDetailResponse) def get_order_details( request: Request, order_id: int = Path(..., description="Order ID", gt=0), customer: Customer = Depends(get_current_customer_api), db: Session = Depends(get_db), ): """ Get detailed order information for authenticated customer. Vendor is automatically determined from request context. Customer can only view their own orders. Path Parameters: - order_id: ID of the order to retrieve """ # Get vendor from middleware (already validated by get_current_customer_api) vendor = getattr(request.state, "vendor", None) if not vendor: raise VendorNotFoundException("context", identifier_type="subdomain") logger.debug( f"[SHOP_API] get_order_details: order {order_id}", extra={ "vendor_id": vendor.id, "vendor_code": vendor.subdomain, "customer_id": customer.id, "order_id": order_id, }, ) # Get order order = order_service.get_order(db=db, vendor_id=vendor.id, order_id=order_id) # Verify order belongs to customer if order.customer_id != customer.id: from app.exceptions import OrderNotFoundException raise OrderNotFoundException(str(order_id)) return OrderDetailResponse.model_validate(order)