Files
bakery-ia/services/suppliers/app/api/suppliers.py

324 lines
13 KiB
Python
Raw Normal View History

# services/suppliers/app/api/suppliers.py
"""
Supplier API endpoints
"""
from fastapi import APIRouter, Depends, HTTPException, Query, Path
from typing import List, Optional
from uuid import UUID
import structlog
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.services.supplier_service import SupplierService
from app.schemas.suppliers import (
SupplierCreate, SupplierUpdate, SupplierResponse, SupplierSummary,
SupplierSearchParams, SupplierApproval, SupplierStatistics
)
2025-08-15 22:40:19 +02:00
from shared.auth.decorators import get_current_user_dep
from typing import Dict, Any
router = APIRouter(prefix="/suppliers", tags=["suppliers"])
logger = structlog.get_logger()
@router.post("/", response_model=SupplierResponse)
async def create_supplier(
supplier_data: SupplierCreate,
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Create a new supplier"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:create"])
try:
service = SupplierService(db)
supplier = await service.create_supplier(
tenant_id=current_user.tenant_id,
supplier_data=supplier_data,
created_by=current_user.user_id
)
return SupplierResponse.from_orm(supplier)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error("Error creating supplier", error=str(e))
raise HTTPException(status_code=500, detail="Failed to create supplier")
@router.get("/", response_model=List[SupplierSummary])
async def list_suppliers(
search_term: Optional[str] = Query(None, description="Search term"),
supplier_type: Optional[str] = Query(None, description="Supplier type filter"),
status: Optional[str] = Query(None, description="Status filter"),
limit: int = Query(50, ge=1, le=1000, description="Number of results to return"),
offset: int = Query(0, ge=0, description="Number of results to skip"),
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""List suppliers with optional filters"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
search_params = SupplierSearchParams(
search_term=search_term,
supplier_type=supplier_type,
status=status,
limit=limit,
offset=offset
)
suppliers = await service.search_suppliers(
tenant_id=current_user.tenant_id,
search_params=search_params
)
return [SupplierSummary.from_orm(supplier) for supplier in suppliers]
except Exception as e:
logger.error("Error listing suppliers", error=str(e))
raise HTTPException(status_code=500, detail="Failed to retrieve suppliers")
@router.get("/statistics", response_model=SupplierStatistics)
async def get_supplier_statistics(
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Get supplier statistics for dashboard"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
stats = await service.get_supplier_statistics(current_user.tenant_id)
return SupplierStatistics(**stats)
except Exception as e:
logger.error("Error getting supplier statistics", error=str(e))
raise HTTPException(status_code=500, detail="Failed to retrieve statistics")
@router.get("/active", response_model=List[SupplierSummary])
async def get_active_suppliers(
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Get all active suppliers"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
suppliers = await service.get_active_suppliers(current_user.tenant_id)
return [SupplierSummary.from_orm(supplier) for supplier in suppliers]
except Exception as e:
logger.error("Error getting active suppliers", error=str(e))
raise HTTPException(status_code=500, detail="Failed to retrieve active suppliers")
@router.get("/top", response_model=List[SupplierSummary])
async def get_top_suppliers(
limit: int = Query(10, ge=1, le=50, description="Number of top suppliers to return"),
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Get top performing suppliers"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
suppliers = await service.get_top_suppliers(current_user.tenant_id, limit)
return [SupplierSummary.from_orm(supplier) for supplier in suppliers]
except Exception as e:
logger.error("Error getting top suppliers", error=str(e))
raise HTTPException(status_code=500, detail="Failed to retrieve top suppliers")
@router.get("/pending-review", response_model=List[SupplierSummary])
async def get_suppliers_needing_review(
days_since_last_order: int = Query(30, ge=1, le=365, description="Days since last order"),
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Get suppliers that may need performance review"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
suppliers = await service.get_suppliers_needing_review(
current_user.tenant_id, days_since_last_order
)
return [SupplierSummary.from_orm(supplier) for supplier in suppliers]
except Exception as e:
logger.error("Error getting suppliers needing review", error=str(e))
raise HTTPException(status_code=500, detail="Failed to retrieve suppliers needing review")
@router.get("/{supplier_id}", response_model=SupplierResponse)
async def get_supplier(
supplier_id: UUID = Path(..., description="Supplier ID"),
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Get supplier by ID"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
supplier = await service.get_supplier(supplier_id)
if not supplier:
raise HTTPException(status_code=404, detail="Supplier not found")
# Check tenant access
if supplier.tenant_id != current_user.tenant_id:
raise HTTPException(status_code=403, detail="Access denied")
return SupplierResponse.from_orm(supplier)
except HTTPException:
raise
except Exception as e:
logger.error("Error getting supplier", supplier_id=str(supplier_id), error=str(e))
raise HTTPException(status_code=500, detail="Failed to retrieve supplier")
@router.put("/{supplier_id}", response_model=SupplierResponse)
async def update_supplier(
supplier_data: SupplierUpdate,
supplier_id: UUID = Path(..., description="Supplier ID"),
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Update supplier information"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:update"])
try:
service = SupplierService(db)
# Check supplier exists and belongs to tenant
existing_supplier = await service.get_supplier(supplier_id)
if not existing_supplier:
raise HTTPException(status_code=404, detail="Supplier not found")
if existing_supplier.tenant_id != current_user.tenant_id:
raise HTTPException(status_code=403, detail="Access denied")
supplier = await service.update_supplier(
supplier_id=supplier_id,
supplier_data=supplier_data,
updated_by=current_user.user_id
)
if not supplier:
raise HTTPException(status_code=404, detail="Supplier not found")
return SupplierResponse.from_orm(supplier)
except HTTPException:
raise
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
except Exception as e:
logger.error("Error updating supplier", supplier_id=str(supplier_id), error=str(e))
raise HTTPException(status_code=500, detail="Failed to update supplier")
@router.delete("/{supplier_id}")
async def delete_supplier(
supplier_id: UUID = Path(..., description="Supplier ID"),
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Delete supplier (soft delete)"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:delete"])
try:
service = SupplierService(db)
# Check supplier exists and belongs to tenant
existing_supplier = await service.get_supplier(supplier_id)
if not existing_supplier:
raise HTTPException(status_code=404, detail="Supplier not found")
if existing_supplier.tenant_id != current_user.tenant_id:
raise HTTPException(status_code=403, detail="Access denied")
success = await service.delete_supplier(supplier_id)
if not success:
raise HTTPException(status_code=404, detail="Supplier not found")
return {"message": "Supplier deleted successfully"}
except HTTPException:
raise
except Exception as e:
logger.error("Error deleting supplier", supplier_id=str(supplier_id), error=str(e))
raise HTTPException(status_code=500, detail="Failed to delete supplier")
@router.post("/{supplier_id}/approve", response_model=SupplierResponse)
async def approve_supplier(
approval_data: SupplierApproval,
supplier_id: UUID = Path(..., description="Supplier ID"),
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Approve or reject a pending supplier"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:approve"])
try:
service = SupplierService(db)
# Check supplier exists and belongs to tenant
existing_supplier = await service.get_supplier(supplier_id)
if not existing_supplier:
raise HTTPException(status_code=404, detail="Supplier not found")
if existing_supplier.tenant_id != current_user.tenant_id:
raise HTTPException(status_code=403, detail="Access denied")
if approval_data.action == "approve":
supplier = await service.approve_supplier(
supplier_id=supplier_id,
approved_by=current_user.user_id,
notes=approval_data.notes
)
elif approval_data.action == "reject":
if not approval_data.notes:
raise HTTPException(status_code=400, detail="Rejection reason is required")
supplier = await service.reject_supplier(
supplier_id=supplier_id,
rejection_reason=approval_data.notes,
rejected_by=current_user.user_id
)
else:
raise HTTPException(status_code=400, detail="Invalid action")
if not supplier:
raise HTTPException(status_code=400, detail="Supplier is not in pending approval status")
return SupplierResponse.from_orm(supplier)
except HTTPException:
raise
except Exception as e:
logger.error("Error processing supplier approval", supplier_id=str(supplier_id), error=str(e))
raise HTTPException(status_code=500, detail="Failed to process supplier approval")
@router.get("/types/{supplier_type}", response_model=List[SupplierSummary])
async def get_suppliers_by_type(
supplier_type: str = Path(..., description="Supplier type"),
2025-08-15 22:40:19 +02:00
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: Session = Depends(get_db)
):
"""Get suppliers by type"""
2025-08-15 22:40:19 +02:00
# require_permissions(current_user, ["suppliers:read"])
try:
from app.models.suppliers import SupplierType
# Validate supplier type
try:
type_enum = SupplierType(supplier_type.upper())
except ValueError:
raise HTTPException(status_code=400, detail="Invalid supplier type")
service = SupplierService(db)
suppliers = await service.get_suppliers_by_type(current_user.tenant_id, type_enum)
return [SupplierSummary.from_orm(supplier) for supplier in suppliers]
except HTTPException:
raise
except Exception as e:
logger.error("Error getting suppliers by type", supplier_type=supplier_type, error=str(e))
raise HTTPException(status_code=500, detail="Failed to retrieve suppliers by type")