Automated Action 5935f302dc Create Small Business Inventory Management System with FastAPI and SQLite
- Set up project structure and FastAPI application
- Create database models with SQLAlchemy
- Implement authentication with JWT
- Add CRUD operations for products, inventory, categories
- Implement purchase order and sales functionality
- Create reporting endpoints
- Set up Alembic for database migrations
- Add comprehensive documentation in README.md
2025-05-16 08:53:15 +00:00

168 lines
4.9 KiB
Python

from typing import Any, List, Optional
from decimal import Decimal
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from app.api.deps import get_db, get_current_active_user, get_current_admin_user
from app.crud.crud_sale import sale
from app.crud.crud_product import product
from app.crud.crud_inventory import inventory
from app.models.user import User as UserModel
from app.schemas.sale import Sale, SaleCreate, SaleUpdate
router = APIRouter()
@router.get("/", response_model=List[Sale])
def read_sales(
db: Session = Depends(get_db),
skip: int = 0,
limit: int = 100,
status: Optional[str] = None,
current_user: UserModel = Depends(get_current_active_user),
) -> Any:
"""
Retrieve sales with their items.
Filter by status if provided.
"""
sales = sale.get_multi_with_items(db, skip=skip, limit=limit)
# Apply status filter
if status:
sales = [s for s in sales if s.status == status]
# Calculate total amount for each sale
for s in sales:
s.total_amount = sale.get_total_amount(db, id=s.id)
return sales
@router.post("/", response_model=Sale)
def create_sale(
*,
db: Session = Depends(get_db),
sale_in: SaleCreate,
current_user: UserModel = Depends(get_current_active_user),
) -> Any:
"""
Create new sale with items and update inventory.
"""
# Verify all products exist and have enough inventory
for item in sale_in.items:
# Check if product exists
prod = product.get(db, id=item.product_id)
if not prod:
raise HTTPException(
status_code=404,
detail=f"Product with id {item.product_id} not found"
)
# Check if enough inventory is available
available = inventory.get_total_product_quantity(db, product_id=item.product_id)
if available < item.quantity:
raise HTTPException(
status_code=400,
detail=f"Not enough inventory for product {prod.name}. Available: {available}, Requested: {item.quantity}"
)
# Create sale with items
new_sale = sale.create_with_items(
db, obj_in=sale_in, user_id=current_user.id
)
if not new_sale:
raise HTTPException(
status_code=400,
detail="Failed to create sale, likely due to insufficient inventory"
)
# Calculate total amount
new_sale.total_amount = sale.get_total_amount(db, id=new_sale.id)
return new_sale
@router.get("/{id}", response_model=Sale)
def read_sale(
*,
db: Session = Depends(get_db),
id: int,
current_user: UserModel = Depends(get_current_active_user),
) -> Any:
"""
Get sale by ID with its items.
"""
sale_item = sale.get_with_items(db, id=id)
if not sale_item:
raise HTTPException(status_code=404, detail="Sale not found")
# Calculate total amount
sale_item.total_amount = sale.get_total_amount(db, id=sale_item.id)
return sale_item
@router.put("/{id}", response_model=Sale)
def update_sale(
*,
db: Session = Depends(get_db),
id: int,
sale_in: SaleUpdate,
current_user: UserModel = Depends(get_current_active_user),
) -> Any:
"""
Update a sale (but not its items).
Can only update completed sales that haven't been cancelled or returned.
"""
sale_item = sale.get(db, id=id)
if not sale_item:
raise HTTPException(status_code=404, detail="Sale not found")
# Only allow updates to completed sales
if sale_item.status != "completed":
raise HTTPException(
status_code=400,
detail=f"Cannot update sale with status {sale_item.status}. Only completed sales can be updated."
)
updated_sale = sale.update(db, db_obj=sale_item, obj_in=sale_in)
# Get full sale with items for response
result = sale.get_with_items(db, id=updated_sale.id)
# Calculate total amount
result.total_amount = sale.get_total_amount(db, id=result.id)
return result
@router.post("/{id}/cancel", response_model=Sale)
def cancel_sale(
*,
db: Session = Depends(get_db),
id: int,
current_user: UserModel = Depends(get_current_active_user),
) -> Any:
"""
Cancel a sale and return items to inventory.
"""
sale_item = sale.get(db, id=id)
if not sale_item:
raise HTTPException(status_code=404, detail="Sale not found")
# Only allow cancelling completed sales
if sale_item.status != "completed":
raise HTTPException(
status_code=400,
detail=f"Cannot cancel sale with status {sale_item.status}. Only completed sales can be cancelled."
)
# Update status and return to inventory
cancelled_sale = sale.cancel_sale(db, id=id)
# Calculate total amount
cancelled_sale.total_amount = sale.get_total_amount(db, id=cancelled_sale.id)
return cancelled_sale