Files
2025-12-05 20:07:01 +01:00

166 lines
6.0 KiB
Python

"""
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)}")