2025-06-04 22:37:35 +00:00

250 lines
7.1 KiB
Python

from typing import Any, List
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.api.deps import get_current_user
from app.db.session import get_db
from app.models.cart import CartItem
from app.models.order import Order, OrderItem, OrderStatus
from app.models.product import Product
from app.models.user import User
from app.schemas.order import Order as OrderSchema
from app.schemas.order import OrderCreate, OrderUpdate
router = APIRouter()
@router.get("/", response_model=List[OrderSchema])
def read_orders(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
skip: int = 0,
limit: int = 100,
) -> Any:
"""
Retrieve orders.
"""
# If user is admin, return all orders
if current_user.is_admin:
orders = db.query(Order).offset(skip).limit(limit).all()
else:
# If user is not admin, return only their orders
orders = db.query(Order).filter(Order.user_id == current_user.id).offset(skip).limit(limit).all()
return orders
@router.post("/", response_model=OrderSchema)
def create_order(
*,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
order_in: OrderCreate = None,
) -> Any:
"""
Create new order from cart.
"""
# Get cart items
cart_items = db.query(CartItem).filter(CartItem.user_id == current_user.id).all()
if not cart_items:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Cart is empty",
)
# Calculate total amount
total_amount = 0.0
order_items_data = []
for cart_item in cart_items:
product = db.query(Product).filter(Product.id == cart_item.product_id).first()
if not product or not product.is_active:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Product {cart_item.product_id} not found or not active",
)
# Check if product has enough stock
if product.stock_quantity < cart_item.quantity:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Not enough stock for product {product.name}",
)
# Add to total
item_total = product.price * cart_item.quantity
total_amount += item_total
# Prepare order item data
order_items_data.append({
"product_id": product.id,
"quantity": cart_item.quantity,
"price": product.price,
})
# Update product stock
product.stock_quantity -= cart_item.quantity
db.add(product)
# Create order
order_data = {
"user_id": current_user.id,
"total_amount": total_amount,
"status": OrderStatus.PENDING,
}
if order_in:
order_data.update({
"shipping_address": order_in.shipping_address,
"tracking_number": order_in.tracking_number,
"payment_id": order_in.payment_id,
"status": order_in.status,
})
order = Order(**order_data)
db.add(order)
db.commit()
db.refresh(order)
# Create order items
for item_data in order_items_data:
order_item = OrderItem(
order_id=order.id,
**item_data,
)
db.add(order_item)
db.commit()
# Clear cart
db.query(CartItem).filter(CartItem.user_id == current_user.id).delete()
db.commit()
# Refresh order to get items
db.refresh(order)
return order
@router.get("/{order_id}", response_model=OrderSchema)
def read_order(
*,
db: Session = Depends(get_db),
order_id: str,
current_user: User = Depends(get_current_user),
) -> Any:
"""
Get order by ID.
"""
order = db.query(Order).filter(Order.id == order_id).first()
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Order not found",
)
# Check permissions
if not current_user.is_admin and order.user_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions",
)
return order
@router.put("/{order_id}", response_model=OrderSchema)
def update_order(
*,
db: Session = Depends(get_db),
order_id: str,
order_in: OrderUpdate,
current_user: User = Depends(get_current_user),
) -> Any:
"""
Update an order. Only admin users can update any order. Regular users can only update their own orders.
"""
order = db.query(Order).filter(Order.id == order_id).first()
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Order not found",
)
# Check permissions
if not current_user.is_admin and order.user_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions",
)
# Regular users can only update shipping address
if not current_user.is_admin:
if hasattr(order_in, "status") and order_in.status != order.status:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Cannot update order status",
)
update_data = order_in.dict(exclude_unset=True)
for field, value in update_data.items():
setattr(order, field, value)
db.add(order)
db.commit()
db.refresh(order)
return order
@router.delete("/{order_id}", status_code=status.HTTP_204_NO_CONTENT, response_model=None)
def cancel_order(
*,
db: Session = Depends(get_db),
order_id: str,
current_user: User = Depends(get_current_user),
) -> Any:
"""
Cancel an order. Only admin users can cancel any order. Regular users can only cancel their own orders.
"""
order = db.query(Order).filter(Order.id == order_id).first()
if not order:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Order not found",
)
# Check permissions
if not current_user.is_admin and order.user_id != current_user.id:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Not enough permissions",
)
# Check if order can be cancelled
if order.status not in [OrderStatus.PENDING, OrderStatus.PAID]:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Cannot cancel order with status {order.status}",
)
# Restore product stock
order_items = db.query(OrderItem).filter(OrderItem.order_id == order.id).all()
for item in order_items:
product = db.query(Product).filter(Product.id == item.product_id).first()
if product:
product.stock_quantity += item.quantity
db.add(product)
# Update order status
order.status = OrderStatus.CANCELLED
db.add(order)
db.commit()
return None