from typing import Any, List, Optional from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from app import crud from app.db.session import get_db from app.schemas.item import ( Item, ItemCreate, ItemUpdate, InventoryTransaction, InventoryTransactionCreate ) router = APIRouter() @router.get("/", response_model=List[Item]) def read_items( db: Session = Depends(get_db), skip: int = 0, limit: int = 100, category_id: Optional[int] = None, search: Optional[str] = None, low_stock: Optional[bool] = False, low_stock_threshold: Optional[int] = 10, ) -> Any: """ Retrieve items with filtering options. """ if category_id: # Filter by category items = crud.item.get_by_category(db, category_id=category_id, skip=skip, limit=limit) elif search: # Search by name or description items = crud.item.search_items(db, query=search, skip=skip, limit=limit) elif low_stock: # Get low stock items items = crud.item.get_low_stock_items(db, threshold=low_stock_threshold, skip=skip, limit=limit) else: # Get all items items = crud.item.get_multi(db, skip=skip, limit=limit) return items @router.post("/", response_model=Item) def create_item( *, db: Session = Depends(get_db), item_in: ItemCreate, ) -> Any: """ Create new item. """ # Check if SKU exists if provided if item_in.sku: existing_item = crud.item.get_by_sku(db, sku=item_in.sku) if existing_item: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Item with this SKU already exists", ) # Check if category exists if provided if item_in.category_id: category = crud.category.get(db, id=item_in.category_id) if not category: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Category not found", ) # Create item with transaction if initial quantity > 0 item = crud.item.create_with_transaction(db, obj_in=item_in) return item @router.get("/{item_id}", response_model=Item) def read_item( *, db: Session = Depends(get_db), item_id: int, ) -> Any: """ Get item by ID. """ item = crud.item.get(db, id=item_id) if not item: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Item not found", ) return item @router.put("/{item_id}", response_model=Item) def update_item( *, db: Session = Depends(get_db), item_id: int, item_in: ItemUpdate, ) -> Any: """ Update an item. """ item = crud.item.get(db, id=item_id) if not item: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Item not found", ) # Check if SKU is being updated and if it already exists if item_in.sku and item_in.sku != item.sku: existing_item = crud.item.get_by_sku(db, sku=item_in.sku) if existing_item: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Item with this SKU already exists", ) # Check if category exists if being updated if item_in.category_id and item_in.category_id != item.category_id: category = crud.category.get(db, id=item_in.category_id) if not category: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Category not found", ) # If quantity is being updated, create a transaction if item_in.quantity is not None and item_in.quantity != item.quantity: quantity_change = item_in.quantity - item.quantity if item.quantity + quantity_change < 0: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Cannot reduce quantity below zero", ) # Update quantity with transaction return crud.item.update_quantity( db, db_obj=item, quantity_change=quantity_change, transaction_type="adjustment", notes="Quantity updated via API" ) else: # Regular update without quantity change return crud.item.update(db, db_obj=item, obj_in=item_in) @router.delete("/{item_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None) def delete_item( *, db: Session = Depends(get_db), item_id: int, ) -> None: """ Delete an item. """ item = crud.item.get(db, id=item_id) if not item: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Item not found", ) crud.item.remove(db, id=item_id) return None @router.post("/{item_id}/transactions", response_model=Item) def add_inventory_transaction( *, db: Session = Depends(get_db), item_id: int, transaction_in: InventoryTransactionCreate, ) -> Any: """ Add inventory transaction (stock in/out). """ item = crud.item.get(db, id=item_id) if not item: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Item not found", ) # Update item quantity with transaction try: updated_item = crud.item.update_quantity( db, db_obj=item, quantity_change=transaction_in.quantity_change, transaction_type=transaction_in.transaction_type, notes=transaction_in.notes ) return updated_item except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e), ) @router.get("/{item_id}/transactions", response_model=List[InventoryTransaction]) def get_item_transactions( *, db: Session = Depends(get_db), item_id: int, skip: int = 0, limit: int = 100, ) -> Any: """ Get transaction history for an item. """ # Check if item exists item = crud.item.get(db, id=item_id) if not item: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Item not found", ) transactions = crud.inventory_transaction.get_by_item( db, item_id=item_id, skip=skip, limit=limit ) return transactions