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

251 lines
8.3 KiB
Python

# services/inventory/app/api/food_safety_compliance.py
"""
Food Safety Compliance API - ATOMIC CRUD operations on FoodSafetyCompliance model
"""
from typing import List, Optional
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException, Query, Path, status
from sqlalchemy.ext.asyncio import AsyncSession
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.food_safety_service import FoodSafetyService
from app.schemas.food_safety import (
FoodSafetyComplianceCreate,
FoodSafetyComplianceUpdate,
FoodSafetyComplianceResponse
)
logger = structlog.get_logger()
route_builder = RouteBuilder('inventory')
router = APIRouter(tags=["food-safety-compliance"])
async def get_food_safety_service() -> FoodSafetyService:
"""Get food safety service instance"""
return FoodSafetyService()
@router.post(
route_builder.build_base_route("food-safety/compliance"),
response_model=FoodSafetyComplianceResponse,
status_code=status.HTTP_201_CREATED
)
@require_user_role(['admin', 'owner', 'member'])
async def create_compliance_record(
compliance_data: FoodSafetyComplianceCreate,
tenant_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
food_safety_service: FoodSafetyService = Depends(get_food_safety_service),
db: AsyncSession = Depends(get_db)
):
"""Create a new food safety compliance record"""
try:
compliance_data.tenant_id = tenant_id
compliance = await food_safety_service.create_compliance_record(
db,
compliance_data,
user_id=UUID(current_user["sub"])
)
logger.info("Compliance record created",
compliance_id=str(compliance.id),
standard=compliance.standard)
return compliance
except ValueError as e:
logger.warning("Invalid compliance data", error=str(e))
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e)
)
except Exception as e:
logger.error("Error creating compliance record", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to create compliance record"
)
@router.get(
route_builder.build_base_route("food-safety/compliance"),
response_model=List[FoodSafetyComplianceResponse]
)
async def get_compliance_records(
tenant_id: UUID = Path(...),
ingredient_id: Optional[UUID] = Query(None, description="Filter by ingredient ID"),
standard: Optional[str] = Query(None, description="Filter by compliance standard"),
status_filter: Optional[str] = Query(None, description="Filter by compliance status"),
skip: int = Query(0, ge=0, description="Number of records to skip"),
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
current_user: dict = Depends(get_current_user_dep),
db: AsyncSession = Depends(get_db)
):
"""Get compliance records with filtering"""
try:
filters = {}
if ingredient_id:
filters["ingredient_id"] = ingredient_id
if standard:
filters["standard"] = standard
if status_filter:
filters["compliance_status"] = status_filter
query = """
SELECT * FROM food_safety_compliance
WHERE tenant_id = :tenant_id AND is_active = true
"""
params = {"tenant_id": tenant_id}
if filters:
for key, value in filters.items():
query += f" AND {key} = :{key}"
params[key] = value
query += " ORDER BY created_at DESC LIMIT :limit OFFSET :skip"
params.update({"limit": limit, "skip": skip})
result = await db.execute(query, params)
records = result.fetchall()
return [
FoodSafetyComplianceResponse(**dict(record))
for record in records
]
except Exception as e:
logger.error("Error getting compliance records", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to retrieve compliance records"
)
@router.get(
route_builder.build_resource_detail_route("food-safety/compliance", "compliance_id"),
response_model=FoodSafetyComplianceResponse
)
async def get_compliance_record(
compliance_id: UUID = Path(...),
tenant_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
db: AsyncSession = Depends(get_db)
):
"""Get specific compliance record"""
try:
query = "SELECT * FROM food_safety_compliance WHERE id = :compliance_id AND tenant_id = :tenant_id"
result = await db.execute(query, {"compliance_id": compliance_id, "tenant_id": tenant_id})
record = result.fetchone()
if not record:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Compliance record not found"
)
return FoodSafetyComplianceResponse(**dict(record))
except HTTPException:
raise
except Exception as e:
logger.error("Error getting compliance record", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to retrieve compliance record"
)
@router.put(
route_builder.build_resource_detail_route("food-safety/compliance", "compliance_id"),
response_model=FoodSafetyComplianceResponse
)
@require_user_role(['admin', 'owner', 'member'])
async def update_compliance_record(
compliance_data: FoodSafetyComplianceUpdate,
tenant_id: UUID = Path(...),
compliance_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
food_safety_service: FoodSafetyService = Depends(get_food_safety_service),
db: AsyncSession = Depends(get_db)
):
"""Update an existing compliance record"""
try:
compliance = await food_safety_service.update_compliance_record(
db,
compliance_id,
tenant_id,
compliance_data,
user_id=UUID(current_user["sub"])
)
if not compliance:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Compliance record not found"
)
logger.info("Compliance record updated",
compliance_id=str(compliance.id))
return compliance
except HTTPException:
raise
except Exception as e:
logger.error("Error updating compliance record",
compliance_id=str(compliance_id),
error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to update compliance record"
)
@router.delete(
route_builder.build_resource_detail_route("food-safety/compliance", "compliance_id"),
status_code=status.HTTP_204_NO_CONTENT
)
@require_user_role(['admin', 'owner'])
async def delete_compliance_record(
compliance_id: UUID = Path(...),
tenant_id: UUID = Path(...),
current_user: dict = Depends(get_current_user_dep),
db: AsyncSession = Depends(get_db)
):
"""Delete (soft delete) compliance record"""
try:
query = """
UPDATE food_safety_compliance
SET is_active = false, updated_at = NOW(), updated_by = :user_id
WHERE id = :compliance_id AND tenant_id = :tenant_id
"""
result = await db.execute(query, {
"compliance_id": compliance_id,
"tenant_id": tenant_id,
"user_id": UUID(current_user["sub"])
})
if result.rowcount == 0:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Compliance record not found"
)
await db.commit()
return None
except HTTPException:
raise
except Exception as e:
logger.error("Error deleting compliance record", error=str(e))
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to delete compliance record"
)