Files
bakery-ia/services/orders/app/api/orders.py
2025-10-06 15:27:01 +02:00

262 lines
8.2 KiB
Python

# ================================================================
# services/orders/app/api/orders.py
# ================================================================
"""
Orders API endpoints - ATOMIC CRUD operations only
"""
from datetime import date
from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Path, Query, status
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
)
logger = structlog.get_logger()
# Create route builder for consistent URL structure
route_builder = RouteBuilder('orders')
router = APIRouter()
# ===== Dependency Injection =====
async def get_orders_service(db = Depends(get_db)) -> OrdersService:
"""Get orders service with dependencies"""
from app.repositories.order_repository import (
OrderRepository,
CustomerRepository,
OrderItemRepository,
OrderStatusHistoryRepository
)
from shared.clients import (
get_inventory_client,
get_production_client,
get_sales_client
)
return OrdersService(
order_repo=OrderRepository(),
customer_repo=CustomerRepository(),
order_item_repo=OrderItemRepository(),
status_history_repo=OrderStatusHistoryRepository(),
inventory_client=get_inventory_client(),
production_client=get_production_client(),
sales_client=get_sales_client()
)
# ===== Order CRUD Endpoints =====
@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(...),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Create a new customer order"""
try:
# Ensure tenant_id matches
order_data.tenant_id = tenant_id
order = await orders_service.create_order(
db,
order_data,
user_id=UUID(current_user["sub"])
)
logger.info("Order created successfully",
order_id=str(order.id),
order_number=order.order_number)
return order
except ValueError as e:
logger.warning("Invalid order data", error=str(e))
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e)
)
except Exception as e:
logger.error("Error creating order", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to create order"
)
@router.get(
route_builder.build_base_route("{order_id}"), response_model=OrderResponse)
async def get_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)
):
"""Get order details with items"""
try:
order = await orders_service.get_order_with_items(db, order_id, tenant_id)
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Order not found"
)
return order
except HTTPException:
raise
except Exception as e:
logger.error("Error getting order",
order_id=str(order_id),
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to retrieve order"
)
@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"),
start_date: Optional[date] = Query(None, description="Start date for date range filter"),
end_date: Optional[date] = Query(None, description="End date for date range filter"),
skip: int = Query(0, ge=0, description="Number of orders to skip"),
limit: int = Query(100, ge=1, le=1000, description="Number of orders to return"),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Get orders with filtering and pagination"""
try:
# Determine which repository method to use based on filters
if status_filter:
orders = await orders_service.order_repo.get_orders_by_status(
db, tenant_id, status_filter, skip, limit
)
elif start_date and end_date:
orders = await orders_service.order_repo.get_orders_by_date_range(
db, tenant_id, start_date, end_date, skip, limit
)
else:
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(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to retrieve orders"
)
@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(...),
current_user: dict = Depends(get_current_user_dep),
orders_service: OrdersService = Depends(get_orders_service),
db = Depends(get_db)
):
"""Update order information"""
try:
# 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"
)
# 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",
order_id=str(order_id),
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to update order"
)
@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)
):
"""Delete an order (soft delete)"""
try:
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"
)
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 deleting order",
order_id=str(order_id),
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to delete order"
)