
This commit includes: - Project structure setup with FastAPI and SQLite - Database models and schemas for inventory management - CRUD operations for all entities - API endpoints for product, category, supplier, and inventory management - User authentication with JWT tokens - Initial database migration - Comprehensive README with setup instructions
200 lines
6.7 KiB
Python
200 lines
6.7 KiB
Python
from typing import Any, List, Optional
|
|
from datetime import datetime
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Body
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app import crud, models, schemas
|
|
from app.api import deps
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/", response_model=List[schemas.Inventory])
|
|
def read_inventories(
|
|
db: Session = Depends(deps.get_db),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
location: Optional[str] = None,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Retrieve inventory items with optional filtering by location.
|
|
"""
|
|
if location:
|
|
inventories = crud.inventory.get_by_location(db, location=location, skip=skip, limit=limit)
|
|
else:
|
|
inventories = crud.inventory.get_multi(db, skip=skip, limit=limit)
|
|
return inventories
|
|
|
|
|
|
@router.get("/product/{product_id}", response_model=schemas.Inventory)
|
|
def read_inventory_by_product(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
product_id: int,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Get inventory by product ID.
|
|
"""
|
|
inventory = crud.inventory.get_by_product_id(db, product_id=product_id)
|
|
if not inventory:
|
|
raise HTTPException(status_code=404, detail="Inventory not found for this product")
|
|
return inventory
|
|
|
|
|
|
@router.get("/low-stock", response_model=schemas.InventorySummaryList)
|
|
def read_low_stock(
|
|
db: Session = Depends(deps.get_db),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Get products with inventory below minimum stock level.
|
|
"""
|
|
low_stock_items = crud.inventory.get_low_stock(db, skip=skip, limit=limit)
|
|
return {"inventories": low_stock_items}
|
|
|
|
|
|
@router.get("/summary", response_model=schemas.InventorySummaryList)
|
|
def read_inventory_summary(
|
|
db: Session = Depends(deps.get_db),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Get inventory summary for all products.
|
|
"""
|
|
summary = crud.inventory.get_inventory_summary(db, skip=skip, limit=limit)
|
|
return {"inventories": summary}
|
|
|
|
|
|
@router.put("/adjust/{product_id}", response_model=schemas.Inventory)
|
|
def adjust_inventory(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
product_id: int,
|
|
quantity_change: int = Body(..., description="Amount to add or subtract from inventory"),
|
|
location: Optional[str] = Body(None, description="Location of the inventory"),
|
|
notes: Optional[str] = Body(None, description="Notes about the adjustment"),
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Adjust inventory quantity for a product.
|
|
Positive values increase stock, negative values decrease stock.
|
|
"""
|
|
# Verify the product exists
|
|
product = crud.product.get(db, id=product_id)
|
|
if not product:
|
|
raise HTTPException(status_code=404, detail="Product not found")
|
|
|
|
# Update inventory and create transaction record
|
|
result = crud.inventory.update_stock(
|
|
db=db,
|
|
product_id=product_id,
|
|
quantity_change=quantity_change,
|
|
user_id=current_user.id,
|
|
transaction_type="adjustment",
|
|
notes=notes
|
|
)
|
|
|
|
# Update location if provided
|
|
if location and location != result["inventory"].location:
|
|
inventory_update = schemas.InventoryUpdate(
|
|
product_id=product_id,
|
|
quantity=result["inventory"].quantity,
|
|
location=location
|
|
)
|
|
inventory = crud.inventory.update(db, db_obj=result["inventory"], obj_in=inventory_update)
|
|
return inventory
|
|
|
|
return result["inventory"]
|
|
|
|
|
|
@router.post("/transactions/", response_model=schemas.InventoryTransaction)
|
|
def create_inventory_transaction(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
transaction_in: schemas.InventoryTransactionCreate,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Create a new inventory transaction and update inventory accordingly.
|
|
"""
|
|
# Verify the product exists
|
|
product = crud.product.get(db, id=transaction_in.product_id)
|
|
if not product:
|
|
raise HTTPException(status_code=404, detail="Product not found")
|
|
|
|
# Validate transaction type
|
|
valid_types = ["purchase", "sale", "adjustment", "return", "transfer"]
|
|
if transaction_in.transaction_type not in valid_types:
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Invalid transaction type. Must be one of: {', '.join(valid_types)}"
|
|
)
|
|
|
|
# Update inventory and create transaction record
|
|
result = crud.inventory.update_stock(
|
|
db=db,
|
|
product_id=transaction_in.product_id,
|
|
quantity_change=transaction_in.quantity,
|
|
user_id=current_user.id,
|
|
transaction_type=transaction_in.transaction_type,
|
|
reference=transaction_in.reference,
|
|
unit_price=transaction_in.unit_price,
|
|
notes=transaction_in.notes
|
|
)
|
|
|
|
return result["transaction"]
|
|
|
|
|
|
@router.get("/transactions/", response_model=List[schemas.InventoryTransaction])
|
|
def read_inventory_transactions(
|
|
db: Session = Depends(deps.get_db),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
product_id: Optional[int] = None,
|
|
transaction_type: Optional[str] = None,
|
|
start_date: Optional[datetime] = None,
|
|
end_date: Optional[datetime] = None,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Retrieve inventory transactions with optional filtering.
|
|
"""
|
|
if product_id:
|
|
transactions = crud.inventory_transaction.get_by_product_id(
|
|
db, product_id=product_id, skip=skip, limit=limit
|
|
)
|
|
elif transaction_type:
|
|
transactions = crud.inventory_transaction.get_by_type(
|
|
db, transaction_type=transaction_type, skip=skip, limit=limit
|
|
)
|
|
elif start_date and end_date:
|
|
transactions = crud.inventory_transaction.get_by_date_range(
|
|
db, start_date=start_date, end_date=end_date, skip=skip, limit=limit
|
|
)
|
|
else:
|
|
transactions = crud.inventory_transaction.get_multi(db, skip=skip, limit=limit)
|
|
|
|
return transactions
|
|
|
|
|
|
@router.get("/transactions/{transaction_id}", response_model=schemas.InventoryTransaction)
|
|
def read_inventory_transaction(
|
|
*,
|
|
db: Session = Depends(deps.get_db),
|
|
transaction_id: int,
|
|
current_user: models.User = Depends(deps.get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Get inventory transaction by ID.
|
|
"""
|
|
transaction = crud.inventory_transaction.get(db, id=transaction_id)
|
|
if not transaction:
|
|
raise HTTPException(status_code=404, detail="Transaction not found")
|
|
return transaction |