
- 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
185 lines
5.6 KiB
Python
185 lines
5.6 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_purchase_order import purchase_order
|
|
from app.crud.crud_product import product
|
|
from app.models.user import User as UserModel
|
|
from app.schemas.purchase_order import PurchaseOrder, PurchaseOrderCreate, PurchaseOrderUpdate
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/", response_model=List[PurchaseOrder])
|
|
def read_purchase_orders(
|
|
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 purchase orders with their items.
|
|
Filter by status if provided.
|
|
"""
|
|
orders = purchase_order.get_multi_with_items(db, skip=skip, limit=limit)
|
|
|
|
# Apply status filter
|
|
if status:
|
|
orders = [order for order in orders if order.status == status]
|
|
|
|
# Calculate total amount for each order
|
|
for order in orders:
|
|
order.total_amount = purchase_order.get_total_amount(db, id=order.id)
|
|
|
|
return orders
|
|
|
|
|
|
@router.post("/", response_model=PurchaseOrder)
|
|
def create_purchase_order(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
order_in: PurchaseOrderCreate,
|
|
current_user: UserModel = Depends(get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Create new purchase order with items.
|
|
"""
|
|
# Verify all products exist
|
|
for item in order_in.items:
|
|
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"
|
|
)
|
|
|
|
# Create purchase order with items
|
|
new_order = purchase_order.create_with_items(
|
|
db, obj_in=order_in, user_id=current_user.id
|
|
)
|
|
|
|
# Calculate total amount
|
|
new_order.total_amount = purchase_order.get_total_amount(db, id=new_order.id)
|
|
|
|
return new_order
|
|
|
|
|
|
@router.get("/{id}", response_model=PurchaseOrder)
|
|
def read_purchase_order(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
id: int,
|
|
current_user: UserModel = Depends(get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Get purchase order by ID with its items.
|
|
"""
|
|
order = purchase_order.get_with_items(db, id=id)
|
|
if not order:
|
|
raise HTTPException(status_code=404, detail="Purchase order not found")
|
|
|
|
# Calculate total amount
|
|
order.total_amount = purchase_order.get_total_amount(db, id=order.id)
|
|
|
|
return order
|
|
|
|
|
|
@router.put("/{id}", response_model=PurchaseOrder)
|
|
def update_purchase_order(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
id: int,
|
|
order_in: PurchaseOrderUpdate,
|
|
current_user: UserModel = Depends(get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Update a purchase order (but not its items).
|
|
Can only update pending orders.
|
|
"""
|
|
order = purchase_order.get(db, id=id)
|
|
if not order:
|
|
raise HTTPException(status_code=404, detail="Purchase order not found")
|
|
|
|
# Only allow updates to pending orders
|
|
if order.status != "pending":
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Cannot update purchase order with status {order.status}. Only pending orders can be updated."
|
|
)
|
|
|
|
updated_order = purchase_order.update(db, db_obj=order, obj_in=order_in)
|
|
|
|
# Get full order with items for response
|
|
result = purchase_order.get_with_items(db, id=updated_order.id)
|
|
|
|
# Calculate total amount
|
|
result.total_amount = purchase_order.get_total_amount(db, id=result.id)
|
|
|
|
return result
|
|
|
|
|
|
@router.post("/{id}/receive", response_model=PurchaseOrder)
|
|
def receive_purchase_order(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
id: int,
|
|
current_user: UserModel = Depends(get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Mark a purchase order as received and update inventory.
|
|
"""
|
|
order = purchase_order.get(db, id=id)
|
|
if not order:
|
|
raise HTTPException(status_code=404, detail="Purchase order not found")
|
|
|
|
# Only allow receiving pending orders
|
|
if order.status != "pending":
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Cannot receive purchase order with status {order.status}. Only pending orders can be received."
|
|
)
|
|
|
|
# Update status and inventory
|
|
received_order = purchase_order.receive_order(db, id=id)
|
|
|
|
# Calculate total amount
|
|
received_order.total_amount = purchase_order.get_total_amount(db, id=received_order.id)
|
|
|
|
return received_order
|
|
|
|
|
|
@router.post("/{id}/cancel", response_model=PurchaseOrder)
|
|
def cancel_purchase_order(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
id: int,
|
|
current_user: UserModel = Depends(get_current_active_user),
|
|
) -> Any:
|
|
"""
|
|
Cancel a purchase order.
|
|
"""
|
|
order = purchase_order.get(db, id=id)
|
|
if not order:
|
|
raise HTTPException(status_code=404, detail="Purchase order not found")
|
|
|
|
# Only allow cancelling pending orders
|
|
if order.status != "pending":
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail=f"Cannot cancel purchase order with status {order.status}. Only pending orders can be cancelled."
|
|
)
|
|
|
|
# Update status
|
|
cancelled_order = purchase_order.cancel_order(db, id=id)
|
|
|
|
# Get full order with items for response
|
|
result = purchase_order.get_with_items(db, id=cancelled_order.id)
|
|
|
|
# Calculate total amount
|
|
result.total_amount = purchase_order.get_total_amount(db, id=result.id)
|
|
|
|
return result |