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

@@ -2,33 +2,31 @@
# services/orders/app/api/orders.py
# ================================================================
"""
Orders API endpoints for Orders Service
Orders API endpoints - ATOMIC CRUD operations only
"""
from datetime import date, datetime
from datetime import date
from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Path, Query, status
from fastapi.responses import JSONResponse
import structlog
from shared.auth.decorators import get_current_user_dep
from shared.auth.access_control import require_user_role
from shared.routing import RouteBuilder
from app.core.database import get_db
from app.services.orders_service import OrdersService
from app.schemas.order_schemas import (
OrderCreate,
OrderUpdate,
OrderResponse,
CustomerCreate,
CustomerUpdate,
CustomerResponse,
OrdersDashboardSummary,
DemandRequirements,
ProcurementPlanningData
OrderResponse
)
logger = structlog.get_logger()
# Create route builder for consistent URL structure
route_builder = RouteBuilder('orders')
router = APIRouter()
@@ -47,7 +45,7 @@ async def get_orders_service(db = Depends(get_db)) -> OrdersService:
get_production_client,
get_sales_client
)
return OrdersService(
order_repo=OrderRepository(),
customer_repo=CustomerRepository(),
@@ -59,67 +57,14 @@ async def get_orders_service(db = Depends(get_db)) -> OrdersService:
)
# ===== Dashboard and Analytics Endpoints =====
# ===== Order CRUD Endpoints =====
@router.get("/tenants/{tenant_id}/orders/dashboard-summary", response_model=OrdersDashboardSummary)
async def get_dashboard_summary(
tenant_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Get comprehensive dashboard summary for orders"""
try:
summary = await orders_service.get_dashboard_summary(db, tenant_id)
logger.info("Dashboard summary retrieved",
tenant_id=str(tenant_id),
total_orders=summary.total_orders_today)
return summary
except Exception as e:
logger.error("Error getting dashboard summary",
tenant_id=str(tenant_id),
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to retrieve dashboard summary"
)
@router.get("/tenants/{tenant_id}/orders/demand-requirements", response_model=DemandRequirements)
async def get_demand_requirements(
tenant_id: UUID = Path(...),
target_date: date = Query(..., description="Date for demand analysis"),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Get demand requirements for production planning"""
try:
requirements = await orders_service.get_demand_requirements(db, tenant_id, target_date)
logger.info("Demand requirements calculated",
tenant_id=str(tenant_id),
target_date=str(target_date),
total_orders=requirements.total_orders)
return requirements
except Exception as e:
logger.error("Error getting demand requirements",
tenant_id=str(tenant_id),
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to calculate demand requirements"
)
# ===== Order Management Endpoints =====
@router.post("/tenants/{tenant_id}/orders", response_model=OrderResponse, status_code=status.HTTP_201_CREATED)
@router.post(
route_builder.build_base_route("orders"),
response_model=OrderResponse,
status_code=status.HTTP_201_CREATED
)
@require_user_role(['admin', 'owner', 'member'])
async def create_order(
order_data: OrderCreate,
tenant_id: UUID = Path(...),
@@ -158,7 +103,8 @@ async def create_order(
)
@router.get("/tenants/{tenant_id}/orders/{order_id}", response_model=OrderResponse)
@router.get(
route_builder.build_base_route("{order_id}"), response_model=OrderResponse)
async def get_order(
tenant_id: UUID = Path(...),
order_id: UUID = Path(...),
@@ -189,7 +135,10 @@ async def get_order(
)
@router.get("/tenants/{tenant_id}/orders", response_model=List[OrderResponse])
@router.get(
route_builder.build_base_route("orders"),
response_model=List[OrderResponse]
)
async def get_orders(
tenant_id: UUID = Path(...),
status_filter: Optional[str] = Query(None, description="Filter by order status"),
@@ -216,9 +165,9 @@ async def get_orders(
orders = await orders_service.order_repo.get_multi(
db, tenant_id, skip, limit, order_by="order_date", order_desc=True
)
return [OrderResponse.from_orm(order) for order in orders]
except Exception as e:
logger.error("Error getting orders", error=str(e))
raise HTTPException(
@@ -227,214 +176,87 @@ async def get_orders(
)
@router.put("/tenants/{tenant_id}/orders/{order_id}/status", response_model=OrderResponse)
async def update_order_status(
new_status: str,
@router.put(
route_builder.build_base_route("{order_id}"),
response_model=OrderResponse
)
@require_user_role(['admin', 'owner', 'member'])
async def update_order(
order_data: OrderUpdate,
tenant_id: UUID = Path(...),
order_id: UUID = Path(...),
reason: Optional[str] = Query(None, description="Reason for status change"),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Update order status"""
"""Update order information"""
try:
# Validate status
valid_statuses = ["pending", "confirmed", "in_production", "ready", "out_for_delivery", "delivered", "cancelled", "failed"]
if new_status not in valid_statuses:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Invalid status. Must be one of: {', '.join(valid_statuses)}"
)
order = await orders_service.update_order_status(
db,
order_id,
tenant_id,
new_status,
user_id=UUID(current_user["sub"]),
reason=reason
)
# Get existing order
order = await orders_service.order_repo.get(db, order_id, tenant_id)
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Order not found"
)
logger.info("Order status updated",
order_id=str(order_id),
new_status=new_status)
return order
# Update order
updated_order = await orders_service.order_repo.update(
db,
db_obj=order,
obj_in=order_data.dict(exclude_unset=True),
updated_by=UUID(current_user["sub"])
)
logger.info("Order updated successfully",
order_id=str(order_id))
return OrderResponse.from_orm(updated_order)
except HTTPException:
raise
except Exception as e:
logger.error("Error updating order status",
order_id=str(order_id),
logger.error("Error updating order",
order_id=str(order_id),
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to update order status"
detail="Failed to update order"
)
# ===== Customer Management Endpoints =====
@router.post("/tenants/{tenant_id}/customers", response_model=CustomerResponse, status_code=status.HTTP_201_CREATED)
async def create_customer(
customer_data: CustomerCreate,
@router.delete(
route_builder.build_base_route("{order_id}"),
status_code=status.HTTP_204_NO_CONTENT
)
@require_user_role(['admin', 'owner'])
async def delete_order(
tenant_id: UUID = Path(...),
order_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Create a new customer"""
"""Delete an order (soft delete)"""
try:
# Ensure tenant_id matches
customer_data.tenant_id = tenant_id
# Check if customer code already exists
existing_customer = await orders_service.customer_repo.get_by_customer_code(
db, customer_data.customer_code, tenant_id
)
if existing_customer:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Customer code already exists"
)
customer = await orders_service.customer_repo.create(
db,
obj_in=customer_data.dict(),
created_by=UUID(current_user["sub"])
)
logger.info("Customer created successfully",
customer_id=str(customer.id),
customer_code=customer.customer_code)
return CustomerResponse.from_orm(customer)
except HTTPException:
raise
except Exception as e:
logger.error("Error creating customer", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to create customer"
)
@router.get("/tenants/{tenant_id}/customers", response_model=List[CustomerResponse])
async def get_customers(
tenant_id: UUID = Path(...),
active_only: bool = Query(True, description="Filter for active customers only"),
skip: int = Query(0, ge=0, description="Number of customers to skip"),
limit: int = Query(100, ge=1, le=1000, description="Number of customers to return"),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Get customers with filtering and pagination"""
try:
if active_only:
customers = await orders_service.customer_repo.get_active_customers(
db, tenant_id, skip, limit
)
else:
customers = await orders_service.customer_repo.get_multi(
db, tenant_id, skip, limit, order_by="name"
)
return [CustomerResponse.from_orm(customer) for customer in customers]
except Exception as e:
logger.error("Error getting customers", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to retrieve customers"
)
@router.get("/tenants/{tenant_id}/customers/{customer_id}", response_model=CustomerResponse)
async def get_customer(
tenant_id: UUID = Path(...),
customer_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Get customer details"""
try:
customer = await orders_service.customer_repo.get(db, customer_id, tenant_id)
if not customer:
order = await orders_service.order_repo.get(db, order_id, tenant_id)
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Customer not found"
detail="Order not found"
)
return CustomerResponse.from_orm(customer)
await orders_service.order_repo.delete(db, order_id, tenant_id)
logger.info("Order deleted successfully",
order_id=str(order_id))
except HTTPException:
raise
except Exception as e:
logger.error("Error getting customer",
customer_id=str(customer_id),
logger.error("Error deleting order",
order_id=str(order_id),
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to retrieve customer"
)
# ===== Business Intelligence Endpoints =====
@router.get("/tenants/{tenant_id}/orders/business-model")
async def detect_business_model(
tenant_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Detect business model based on order patterns"""
try:
business_model = await orders_service.detect_business_model(db, tenant_id)
return {
"business_model": business_model,
"confidence": "high" if business_model else "unknown",
"detected_at": datetime.now().isoformat()
}
except Exception as e:
logger.error("Error detecting business model", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to detect business model"
)
# ===== Health and Status Endpoints =====
@router.get("/tenants/{tenant_id}/orders/status")
async def get_service_status(
tenant_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep)
):
"""Get orders service status"""
try:
return {
"service": "orders-service",
"status": "healthy",
"timestamp": datetime.now().isoformat(),
"tenant_id": str(tenant_id)
}
except Exception as e:
logger.error("Error getting service status", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to get service status"
detail="Failed to delete order"
)