Automated Action 4e60587fda Create simple inventory management app with FastAPI and SQLite
- Set up project structure with FastAPI
- Implement SQLAlchemy models for inventory items and categories
- Create database connection with SQLite
- Add CRUD operations for inventory management
- Implement API endpoints for categories, items, and inventory transactions
- Add health check endpoint
- Set up Alembic for database migrations
- Update README with documentation
2025-06-17 03:54:50 +00:00

227 lines
6.4 KiB
Python

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