REFACTOR ALL APIs

This commit is contained in:
Urtzi Alfaro
2025-10-06 15:27:01 +02:00
parent dc8221bd2f
commit 38fb98bc27
166 changed files with 18454 additions and 13605 deletions

View File

@@ -1,10 +1,10 @@
# services/suppliers/app/api/suppliers.py
"""
Supplier API endpoints
Supplier CRUD API endpoints (ATOMIC)
"""
from fastapi import APIRouter, Depends, HTTPException, Query, Path, Request
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query, Path
from typing import List, Optional, Dict, Any
from uuid import UUID
import structlog
@@ -13,23 +13,28 @@ 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
SupplierSearchParams
)
from shared.auth.decorators import get_current_user_dep
from typing import Dict, Any
from shared.routing import RouteBuilder
from shared.auth.access_control import require_user_role
router = APIRouter(prefix="/tenants/{tenant_id}/suppliers", tags=["suppliers"])
# Create route builder for consistent URL structure
route_builder = RouteBuilder('suppliers')
router = APIRouter(tags=["suppliers"])
logger = structlog.get_logger()
@router.post("", response_model=SupplierResponse)
@router.post("/", response_model=SupplierResponse)
@router.post(route_builder.build_base_route("suppliers"), response_model=SupplierResponse)
@require_user_role(['admin', 'owner', 'member'])
async def create_supplier(
supplier_data: SupplierCreate,
tenant_id: str = Path(..., description="Tenant ID"),
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: AsyncSession = Depends(get_db)
):
"""Create a new supplier"""
try:
service = SupplierService(db)
supplier = await service.create_supplier(
@@ -45,8 +50,7 @@ async def create_supplier(
raise HTTPException(status_code=500, detail="Failed to create supplier")
@router.get("", response_model=List[SupplierSummary])
@router.get("/", response_model=List[SupplierSummary])
@router.get(route_builder.build_base_route("suppliers"), response_model=List[SupplierSummary])
async def list_suppliers(
tenant_id: str = Path(..., description="Tenant ID"),
search_term: Optional[str] = Query(None, description="Search term"),
@@ -57,8 +61,6 @@ async def list_suppliers(
db: AsyncSession = Depends(get_db)
):
"""List suppliers with optional filters"""
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
search_params = SupplierSearchParams(
@@ -78,94 +80,20 @@ async def list_suppliers(
raise HTTPException(status_code=500, detail="Failed to retrieve suppliers")
@router.get("/statistics", response_model=SupplierStatistics)
async def get_supplier_statistics(
tenant_id: str = Path(..., description="Tenant ID"),
db: AsyncSession = Depends(get_db)
):
"""Get supplier statistics for dashboard"""
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
stats = await service.get_supplier_statistics(UUID(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(
tenant_id: str = Path(..., description="Tenant ID"),
db: AsyncSession = Depends(get_db)
):
"""Get all active suppliers"""
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
suppliers = await service.get_active_suppliers(UUID(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(
tenant_id: str = Path(..., description="Tenant ID"),
limit: int = Query(10, ge=1, le=50, description="Number of top suppliers to return"),
db: AsyncSession = Depends(get_db)
):
"""Get top performing suppliers"""
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
suppliers = await service.get_top_suppliers(UUID(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(
tenant_id: str = Path(..., description="Tenant ID"),
days_since_last_order: int = Query(30, ge=1, le=365, description="Days since last order"),
db: AsyncSession = Depends(get_db)
):
"""Get suppliers that may need performance review"""
# require_permissions(current_user, ["suppliers:read"])
try:
service = SupplierService(db)
suppliers = await service.get_suppliers_needing_review(
UUID(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)
@router.get(route_builder.build_resource_detail_route("suppliers", "supplier_id"), response_model=SupplierResponse)
async def get_supplier(
supplier_id: UUID = Path(..., description="Supplier ID"),
tenant_id: str = Path(..., description="Tenant ID"),
db: AsyncSession = Depends(get_db)
):
"""Get supplier by ID"""
# 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")
return SupplierResponse.from_orm(supplier)
except HTTPException:
raise
@@ -174,7 +102,8 @@ async def get_supplier(
raise HTTPException(status_code=500, detail="Failed to retrieve supplier")
@router.put("/{supplier_id}", response_model=SupplierResponse)
@router.put(route_builder.build_resource_detail_route("suppliers", "supplier_id"), response_model=SupplierResponse)
@require_user_role(['admin', 'owner', 'member'])
async def update_supplier(
supplier_data: SupplierUpdate,
supplier_id: UUID = Path(..., description="Supplier ID"),
@@ -182,25 +111,23 @@ async def update_supplier(
db: AsyncSession = Depends(get_db)
):
"""Update supplier information"""
# require_permissions(current_user, ["suppliers:update"])
try:
service = SupplierService(db)
# Check supplier exists
existing_supplier = await service.get_supplier(supplier_id)
if not existing_supplier:
raise HTTPException(status_code=404, detail="Supplier not found")
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
@@ -211,103 +138,28 @@ async def update_supplier(
raise HTTPException(status_code=500, detail="Failed to update supplier")
@router.delete("/{supplier_id}")
@router.delete(route_builder.build_resource_detail_route("suppliers", "supplier_id"))
@require_user_role(['admin', 'owner'])
async def delete_supplier(
supplier_id: UUID = Path(..., description="Supplier ID"),
db: AsyncSession = Depends(get_db)
):
"""Delete supplier (soft delete)"""
# require_permissions(current_user, ["suppliers:delete"])
try:
service = SupplierService(db)
# Check supplier exists
existing_supplier = await service.get_supplier(supplier_id)
if not existing_supplier:
raise HTTPException(status_code=404, detail="Supplier not found")
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"),
current_user: Dict[str, Any] = Depends(get_current_user_dep),
db: AsyncSession = Depends(get_db)
):
"""Approve or reject a pending supplier"""
# require_permissions(current_user, ["suppliers:approve"])
try:
service = SupplierService(db)
# Check supplier exists
existing_supplier = await service.get_supplier(supplier_id)
if not existing_supplier:
raise HTTPException(status_code=404, detail="Supplier not found")
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"),
tenant_id: str = Path(..., description="Tenant ID"),
db: AsyncSession = Depends(get_db)
):
"""Get suppliers by type"""
# 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(UUID(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")
raise HTTPException(status_code=500, detail="Failed to delete supplier")