""" API Routes for Distribution Service """ from fastapi import APIRouter, Depends, HTTPException, Query, Header from typing import List, Optional, Dict, Any from datetime import date, timedelta import structlog import os from app.api.dependencies import get_distribution_service from shared.auth.tenant_access import verify_tenant_access_dep from shared.routing.route_builder import RouteBuilder from app.core.config import settings logger = structlog.get_logger() # Initialize route builder for distribution service route_builder = RouteBuilder('distribution') # ✅ Security: Internal API key system removed # All authentication now handled via JWT service tokens at gateway level router = APIRouter() @router.post(route_builder.build_base_route("plans/generate")) async def generate_daily_distribution_plan( tenant_id: str, target_date: date = Query(..., description="Date for which to generate distribution plan"), vehicle_capacity_kg: float = Query(1000.0, description="Vehicle capacity in kg"), distribution_service: object = Depends(get_distribution_service), verified_tenant: str = Depends(verify_tenant_access_dep) ): """ Generate daily distribution plan for internal transfers **Enterprise Tier Feature**: Distribution and routing require Enterprise subscription. """ try: # Validate subscription tier for distribution features from shared.subscription.plans import PlanFeatures from shared.clients import get_tenant_client tenant_client = get_tenant_client(config=settings, service_name="distribution-service") subscription = await tenant_client.get_tenant_subscription(tenant_id) if not subscription: raise HTTPException( status_code=403, detail="No active subscription found. Distribution routing requires Enterprise tier." ) # Check if tier has distribution feature (enterprise only) tier = subscription.get("plan", "starter") if not PlanFeatures.has_feature(tier, "distribution_management"): raise HTTPException( status_code=403, detail=f"Distribution routing requires Enterprise tier. Current tier: {tier}" ) result = await distribution_service.generate_daily_distribution_plan( parent_tenant_id=tenant_id, target_date=target_date, vehicle_capacity_kg=vehicle_capacity_kg ) return result except HTTPException: raise except Exception as e: logger.error("Error generating distribution plan", error=str(e), exc_info=True) raise HTTPException(status_code=500, detail=f"Failed to generate distribution plan: {str(e)}") @router.get(route_builder.build_base_route("routes")) async def get_delivery_routes( tenant_id: str, date_from: Optional[date] = Query(None, description="Start date for route filtering"), date_to: Optional[date] = Query(None, description="End date for route filtering"), status: Optional[str] = Query(None, description="Filter by route status"), distribution_service: object = Depends(get_distribution_service), verified_tenant: str = Depends(verify_tenant_access_dep) ): """ Get delivery routes with optional filtering """ try: # If no date range specified, default to today if not date_from and not date_to: date_from = date.today() date_to = date.today() elif not date_to: date_to = date_from routes = [] current_date = date_from while current_date <= date_to: daily_routes = await distribution_service.get_delivery_routes_for_date(tenant_id, current_date) routes.extend(daily_routes) current_date = current_date + timedelta(days=1) if status: routes = [r for r in routes if r.get('status') == status] return {"routes": routes} except Exception as e: logger.error("Error getting delivery routes", error=str(e), exc_info=True) raise HTTPException(status_code=500, detail=f"Failed to get delivery routes: {str(e)}") @router.get(route_builder.build_base_route("routes/{route_id}")) async def get_route_detail( tenant_id: str, route_id: str, distribution_service: object = Depends(get_distribution_service), verified_tenant: str = Depends(verify_tenant_access_dep) ): """ Get delivery route details """ try: # Implementation would fetch detailed route information # For now, return a simple response routes = await distribution_service.get_delivery_routes_for_date(tenant_id, date.today()) route = next((r for r in routes if r.get('id') == route_id), None) if not route: raise HTTPException(status_code=404, detail="Route not found") return route except HTTPException: raise except Exception as e: logger.error("Error getting route detail", error=str(e), exc_info=True) raise HTTPException(status_code=500, detail=f"Failed to get route detail: {str(e)}")