Automated Action 0474b30c3f Implement complete bookstore management API with FastAPI
- Added comprehensive book management with CRUD operations
- Implemented inventory tracking with stock management and reservations
- Created order management system with status tracking
- Integrated Stripe payment processing with payment intents
- Added SQLite database with SQLAlchemy ORM and Alembic migrations
- Implemented health check and API documentation endpoints
- Added comprehensive error handling and validation
- Configured CORS middleware for frontend integration
2025-06-25 10:34:27 +00:00

177 lines
6.8 KiB
Python

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from app.db.session import get_db
from app.models import Order, OrderItem, Book, Inventory, OrderStatus
from app.schemas import OrderCreate, OrderResponse, PaymentIntentCreate
from app.services.stripe_service import StripeService
router = APIRouter(prefix="/orders", tags=["orders"])
@router.post("/", response_model=OrderResponse)
def create_order(order: OrderCreate, db: Session = Depends(get_db)):
total_amount = 0
order_items_data = []
for item in order.items:
book = db.query(Book).filter(Book.id == item.book_id, Book.is_active).first()
if not book:
raise HTTPException(status_code=404, detail=f"Book with ID {item.book_id} not found")
inventory = db.query(Inventory).filter(Inventory.book_id == item.book_id).first()
if not inventory:
raise HTTPException(status_code=400, detail=f"No inventory found for book ID {item.book_id}")
available = inventory.quantity - inventory.reserved_quantity
if item.quantity > available:
raise HTTPException(status_code=400, detail=f"Not enough stock for book '{book.title}'. Available: {available}")
item_total = book.price * item.quantity
total_amount += item_total
order_items_data.append({
"book_id": item.book_id,
"quantity": item.quantity,
"price": book.price
})
db_order = Order(
customer_email=order.customer_email,
customer_name=order.customer_name,
customer_address=order.customer_address,
total_amount=total_amount
)
db.add(db_order)
db.commit()
db.refresh(db_order)
for item_data in order_items_data:
order_item = OrderItem(order_id=db_order.id, **item_data)
db.add(order_item)
inventory = db.query(Inventory).filter(Inventory.book_id == item_data["book_id"]).first()
inventory.reserved_quantity += item_data["quantity"]
db.commit()
db.refresh(db_order)
return db_order
@router.get("/", response_model=List[OrderResponse])
def get_orders(
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
status: Optional[OrderStatus] = None,
customer_email: Optional[str] = None,
db: Session = Depends(get_db)
):
query = db.query(Order)
if status:
query = query.filter(Order.status == status)
if customer_email:
query = query.filter(Order.customer_email.ilike(f"%{customer_email}%"))
return query.offset(skip).limit(limit).all()
@router.get("/{order_id}", response_model=OrderResponse)
def get_order(order_id: int, db: Session = Depends(get_db)):
order = db.query(Order).filter(Order.id == order_id).first()
if not order:
raise HTTPException(status_code=404, detail="Order not found")
return order
@router.put("/{order_id}/status")
def update_order_status(order_id: int, status: OrderStatus, db: Session = Depends(get_db)):
order = db.query(Order).filter(Order.id == order_id).first()
if not order:
raise HTTPException(status_code=404, detail="Order not found")
if order.status == OrderStatus.CANCELLED:
raise HTTPException(status_code=400, detail="Cannot update status of cancelled order")
if status == OrderStatus.CANCELLED:
for item in order.items:
inventory = db.query(Inventory).filter(Inventory.book_id == item.book_id).first()
if inventory:
inventory.reserved_quantity -= item.quantity
if status == OrderStatus.DELIVERED and order.status != OrderStatus.SHIPPED:
raise HTTPException(status_code=400, detail="Order must be shipped before it can be delivered")
if status == OrderStatus.CONFIRMED and order.status == OrderStatus.PENDING:
for item in order.items:
inventory = db.query(Inventory).filter(Inventory.book_id == item.book_id).first()
if inventory:
inventory.quantity -= item.quantity
inventory.reserved_quantity -= item.quantity
order.status = status
db.commit()
return {"message": f"Order status updated to {status.value}"}
@router.post("/payment-intent", response_model=dict)
def create_payment_intent(payment_data: PaymentIntentCreate, db: Session = Depends(get_db)):
order = db.query(Order).filter(Order.id == payment_data.order_id).first()
if not order:
raise HTTPException(status_code=404, detail="Order not found")
if order.stripe_payment_intent_id:
raise HTTPException(status_code=400, detail="Payment intent already exists for this order")
stripe_service = StripeService()
payment_intent = stripe_service.create_payment_intent(
amount=order.total_amount,
metadata={"order_id": str(order.id)}
)
order.stripe_payment_intent_id = payment_intent["payment_intent_id"]
db.commit()
return payment_intent
@router.post("/{order_id}/confirm-payment")
def confirm_payment(order_id: int, db: Session = Depends(get_db)):
order = db.query(Order).filter(Order.id == order_id).first()
if not order:
raise HTTPException(status_code=404, detail="Order not found")
if not order.stripe_payment_intent_id:
raise HTTPException(status_code=400, detail="No payment intent found for this order")
stripe_service = StripeService()
payment_status = stripe_service.confirm_payment_intent(order.stripe_payment_intent_id)
if payment_status["status"] == "succeeded":
order.status = OrderStatus.CONFIRMED
db.commit()
return {"message": "Payment confirmed and order updated"}
else:
return {"message": "Payment not yet completed", "status": payment_status["status"]}
@router.delete("/{order_id}")
def cancel_order(order_id: int, db: Session = Depends(get_db)):
order = db.query(Order).filter(Order.id == order_id).first()
if not order:
raise HTTPException(status_code=404, detail="Order not found")
if order.status in [OrderStatus.SHIPPED, OrderStatus.DELIVERED]:
raise HTTPException(status_code=400, detail="Cannot cancel shipped or delivered order")
for item in order.items:
inventory = db.query(Inventory).filter(Inventory.book_id == item.book_id).first()
if inventory:
inventory.reserved_quantity -= item.quantity
if order.stripe_payment_intent_id:
stripe_service = StripeService()
try:
stripe_service.cancel_payment_intent(order.stripe_payment_intent_id)
except HTTPException:
pass
order.status = OrderStatus.CANCELLED
db.commit()
return {"message": "Order cancelled successfully"}