Create new services: inventory, recipes, suppliers
This commit is contained in:
167
services/inventory/app/api/stock.py
Normal file
167
services/inventory/app/api/stock.py
Normal file
@@ -0,0 +1,167 @@
|
||||
# services/inventory/app/api/stock.py
|
||||
"""
|
||||
API endpoints for stock management
|
||||
"""
|
||||
|
||||
from typing import List, Optional
|
||||
from uuid import UUID
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.database import get_db
|
||||
from app.services.inventory_service import InventoryService
|
||||
from app.schemas.inventory import (
|
||||
StockCreate,
|
||||
StockUpdate,
|
||||
StockResponse,
|
||||
StockMovementCreate,
|
||||
StockMovementResponse,
|
||||
StockFilter
|
||||
)
|
||||
from shared.auth.decorators import get_current_user_dep
|
||||
from shared.auth.tenant_access import verify_tenant_access_dep
|
||||
|
||||
router = APIRouter(prefix="/stock", tags=["stock"])
|
||||
|
||||
# Helper function to extract user ID from user object
|
||||
def get_current_user_id(current_user: dict = Depends(get_current_user_dep)) -> UUID:
|
||||
"""Extract user ID from current user context"""
|
||||
user_id = current_user.get('user_id')
|
||||
if not user_id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="User ID not found in context"
|
||||
)
|
||||
return UUID(user_id)
|
||||
|
||||
|
||||
@router.post("/", response_model=StockResponse)
|
||||
async def add_stock(
|
||||
stock_data: StockCreate,
|
||||
tenant_id: UUID = Depends(verify_tenant_access_dep),
|
||||
user_id: UUID = Depends(get_current_user_id),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Add new stock entry"""
|
||||
try:
|
||||
service = InventoryService()
|
||||
stock = await service.add_stock(stock_data, tenant_id, user_id)
|
||||
return stock
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e)
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to add stock"
|
||||
)
|
||||
|
||||
|
||||
@router.post("/consume")
|
||||
async def consume_stock(
|
||||
ingredient_id: UUID,
|
||||
quantity: float = Query(..., gt=0, description="Quantity to consume"),
|
||||
reference_number: Optional[str] = Query(None, description="Reference number (e.g., production order)"),
|
||||
notes: Optional[str] = Query(None, description="Additional notes"),
|
||||
fifo: bool = Query(True, description="Use FIFO (First In, First Out) method"),
|
||||
tenant_id: UUID = Depends(verify_tenant_access_dep),
|
||||
user_id: UUID = Depends(get_current_user_id),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Consume stock for production"""
|
||||
try:
|
||||
service = InventoryService()
|
||||
consumed_items = await service.consume_stock(
|
||||
ingredient_id, quantity, tenant_id, user_id, reference_number, notes, fifo
|
||||
)
|
||||
return {
|
||||
"ingredient_id": str(ingredient_id),
|
||||
"total_quantity_consumed": quantity,
|
||||
"consumed_items": consumed_items,
|
||||
"method": "FIFO" if fifo else "LIFO"
|
||||
}
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=str(e)
|
||||
)
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to consume stock"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/ingredient/{ingredient_id}", response_model=List[StockResponse])
|
||||
async def get_ingredient_stock(
|
||||
ingredient_id: UUID,
|
||||
include_unavailable: bool = Query(False, description="Include unavailable stock"),
|
||||
tenant_id: UUID = Depends(verify_tenant_access_dep),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Get stock entries for an ingredient"""
|
||||
try:
|
||||
service = InventoryService()
|
||||
stock_entries = await service.get_stock_by_ingredient(
|
||||
ingredient_id, tenant_id, include_unavailable
|
||||
)
|
||||
return stock_entries
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to get ingredient stock"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/expiring", response_model=List[dict])
|
||||
async def get_expiring_stock(
|
||||
days_ahead: int = Query(7, ge=1, le=365, description="Days ahead to check for expiring items"),
|
||||
tenant_id: UUID = Depends(verify_tenant_access_dep),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Get stock items expiring within specified days"""
|
||||
try:
|
||||
service = InventoryService()
|
||||
expiring_items = await service.check_expiration_alerts(tenant_id, days_ahead)
|
||||
return expiring_items
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to get expiring stock"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/low-stock", response_model=List[dict])
|
||||
async def get_low_stock(
|
||||
tenant_id: UUID = Depends(verify_tenant_access_dep),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Get ingredients with low stock levels"""
|
||||
try:
|
||||
service = InventoryService()
|
||||
low_stock_items = await service.check_low_stock_alerts(tenant_id)
|
||||
return low_stock_items
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to get low stock items"
|
||||
)
|
||||
|
||||
|
||||
@router.get("/summary", response_model=dict)
|
||||
async def get_stock_summary(
|
||||
tenant_id: UUID = Depends(verify_tenant_access_dep),
|
||||
db: AsyncSession = Depends(get_db)
|
||||
):
|
||||
"""Get stock summary for dashboard"""
|
||||
try:
|
||||
service = InventoryService()
|
||||
summary = await service.get_inventory_summary(tenant_id)
|
||||
return summary.dict()
|
||||
except Exception as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Failed to get stock summary"
|
||||
)
|
||||
Reference in New Issue
Block a user