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