""" Shipment API endpoints for distribution service """ from fastapi import APIRouter, Depends, HTTPException, Query from typing import List, Optional from datetime import date, timedelta 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 router = APIRouter() # Initialize route builder for distribution service route_builder = RouteBuilder('distribution') @router.get(route_builder.build_base_route("shipments")) async def get_shipments( tenant_id: str, date_from: Optional[date] = Query(None, description="Start date for shipment filtering"), date_to: Optional[date] = Query(None, description="End date for shipment filtering"), status: Optional[str] = Query(None, description="Filter by shipment status"), distribution_service: object = Depends(get_distribution_service), verified_tenant: str = Depends(verify_tenant_access_dep) ): """ List shipments 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 shipments = [] current_date = date_from while current_date <= date_to: daily_shipments = await distribution_service.get_shipments_for_date(tenant_id, current_date) shipments.extend(daily_shipments) current_date = current_date + timedelta(days=1) if status: shipments = [s for s in shipments if s.get('status') == status] return {"shipments": shipments} except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get shipments: {str(e)}") @router.put(route_builder.build_base_route("shipments/{shipment_id}/status")) async def update_shipment_status( tenant_id: str, shipment_id: str, status_update: dict, # Should be a proper Pydantic model distribution_service: object = Depends(get_distribution_service), verified_tenant: str = Depends(verify_tenant_access_dep) ): """ Update shipment status """ try: new_status = status_update.get('status') if not new_status: raise HTTPException(status_code=400, detail="Status is required") user_id = "temp_user_id" # Would come from auth context result = await distribution_service.update_shipment_status( shipment_id=shipment_id, new_status=new_status, user_id=user_id, metadata=status_update.get('metadata') ) return result except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to update shipment status: {str(e)}") @router.post(route_builder.build_base_route("shipments/{shipment_id}/delivery-proof")) async def upload_delivery_proof( tenant_id: str, shipment_id: str, delivery_proof: dict, # Should be a proper Pydantic model distribution_service: object = Depends(get_distribution_service), verified_tenant: str = Depends(verify_tenant_access_dep) ): """ Upload delivery proof (signature, photo, etc.) Expected delivery_proof fields: - signature: Base64 encoded signature image or signature data - photo_url: URL to uploaded delivery photo - received_by_name: Name of person who received delivery - delivery_notes: Optional notes about delivery """ try: user_id = "temp_user_id" # Would come from auth context # Prepare metadata for shipment update metadata = {} if 'signature' in delivery_proof: metadata['signature'] = delivery_proof['signature'] if 'photo_url' in delivery_proof: metadata['photo_url'] = delivery_proof['photo_url'] if 'received_by_name' in delivery_proof: metadata['received_by_name'] = delivery_proof['received_by_name'] if 'delivery_notes' in delivery_proof: metadata['delivery_notes'] = delivery_proof['delivery_notes'] # Update shipment with delivery proof result = await distribution_service.update_shipment_status( shipment_id=shipment_id, new_status='delivered', # Automatically mark as delivered when proof uploaded user_id=user_id, metadata=metadata ) if not result: raise HTTPException(status_code=404, detail="Shipment not found") return { "message": "Delivery proof uploaded successfully", "shipment_id": shipment_id, "status": "delivered" } except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to upload delivery proof: {str(e)}") @router.get(route_builder.build_base_route("shipments/{shipment_id}")) async def get_shipment_detail( tenant_id: str, shipment_id: str, distribution_service: object = Depends(get_distribution_service), verified_tenant: str = Depends(verify_tenant_access_dep) ): """ Get detailed information about a specific shipment including: - Basic shipment info (number, date, status) - Parent and child tenant details - Delivery route assignment - Purchase order reference - Delivery proof (signature, photo, received by) - Location tracking data """ try: # Access the shipment repository from the distribution service shipment = await distribution_service.shipment_repository.get_shipment_by_id(shipment_id) if not shipment: raise HTTPException(status_code=404, detail="Shipment not found") # Verify tenant access if str(shipment.get('tenant_id')) != tenant_id: raise HTTPException(status_code=403, detail="Access denied to this shipment") return shipment except HTTPException: raise except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to get shipment details: {str(e)}")