294 lines
9.2 KiB
Python

import json
import logging
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from app.core.database import get_db
from app.dependencies.auth import get_current_active_user
from app.models.cart import CartItem
from app.models.product import Product, ProductStatus
from app.models.user import User
from app.schemas.cart import (
CartItem as CartItemSchema,
)
from app.schemas.cart import (
CartItemCreate,
CartItemUpdate,
CartSummary,
)
router = APIRouter()
logger = logging.getLogger(__name__)
@router.get("/", response_model=CartSummary)
async def get_cart(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Get the current user's shopping cart.
"""
cart_items = db.query(CartItem).filter(CartItem.user_id == current_user.id).all()
# Enhance cart items with product information
items = []
total_items = 0
subtotal = 0
total_weight = 0
for item in cart_items:
# Skip items with deleted products
if not item.product:
continue
# Skip items with unavailable products
if item.product.status != ProductStatus.PUBLISHED and item.product.status != ProductStatus.OUT_OF_STOCK:
continue
# Get primary image if available
product_image = None
primary_image = next((img for img in item.product.images if img.is_primary), None)
if primary_image:
product_image = primary_image.image_url
elif item.product.images:
product_image = item.product.images[0].image_url
# Calculate current price and subtotal
current_price = item.product.current_price
item_subtotal = current_price * item.quantity
# Parse custom properties
custom_properties = None
if item.custom_properties:
try:
custom_properties = json.loads(item.custom_properties)
except:
custom_properties = None
# Create enhanced cart item
cart_item = {
"id": item.id,
"user_id": item.user_id,
"product_id": item.product_id,
"quantity": item.quantity,
"price_at_addition": item.price_at_addition,
"custom_properties": custom_properties,
"created_at": item.created_at,
"updated_at": item.updated_at,
"product_name": item.product.name,
"product_image": product_image,
"current_price": current_price,
"subtotal": item_subtotal
}
items.append(cart_item)
total_items += item.quantity
subtotal += item_subtotal
# Add weight if available
if item.product.weight:
total_weight += item.product.weight * item.quantity
return {
"items": items,
"total_items": total_items,
"subtotal": subtotal,
"total_weight": total_weight if total_weight > 0 else None
}
@router.post("/items", response_model=CartItemSchema)
async def add_cart_item(
item_in: CartItemCreate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Add an item to the shopping cart.
"""
# Check if product exists and is available
product = db.query(Product).filter(Product.id == item_in.product_id).first()
if not product:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Product not found"
)
if product.status != ProductStatus.PUBLISHED:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Product is not available for purchase"
)
if product.stock_quantity < item_in.quantity:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Not enough stock available. Only {product.stock_quantity} items left."
)
# Check if the item is already in the cart
existing_item = db.query(CartItem).filter(
CartItem.user_id == current_user.id,
CartItem.product_id == item_in.product_id
).first()
if existing_item:
# Update quantity if item already exists
new_quantity = existing_item.quantity + item_in.quantity
# Check stock for the new quantity
if product.stock_quantity < new_quantity:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Not enough stock available. Only {product.stock_quantity} items left."
)
existing_item.quantity = new_quantity
# Update custom properties if provided
if item_in.custom_properties:
existing_item.custom_properties = json.dumps(item_in.custom_properties)
db.commit()
db.refresh(existing_item)
cart_item = existing_item
else:
# Serialize custom properties if provided
custom_properties_json = None
if item_in.custom_properties:
custom_properties_json = json.dumps(item_in.custom_properties)
# Create new cart item
cart_item = CartItem(
user_id=current_user.id,
product_id=item_in.product_id,
quantity=item_in.quantity,
price_at_addition=product.current_price,
custom_properties=custom_properties_json
)
db.add(cart_item)
db.commit()
db.refresh(cart_item)
# Enhance cart item with product information for response
product_image = None
primary_image = next((img for img in product.images if img.is_primary), None)
if primary_image:
product_image = primary_image.image_url
elif product.images:
product_image = product.images[0].image_url
cart_item.product_name = product.name
cart_item.product_image = product_image
cart_item.current_price = product.current_price
cart_item.subtotal = product.current_price * cart_item.quantity
logger.info(f"Item added to cart: Product {product.name} (ID: {product.id}) for user {current_user.email}")
return cart_item
@router.put("/items/{item_id}", response_model=CartItemSchema)
async def update_cart_item(
item_id: str,
item_in: CartItemUpdate,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Update a cart item.
"""
# Find the cart item
cart_item = db.query(CartItem).filter(
CartItem.id == item_id,
CartItem.user_id == current_user.id
).first()
if not cart_item:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Cart item not found"
)
# Check if product exists
product = db.query(Product).filter(Product.id == cart_item.product_id).first()
if not product:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Product not found"
)
# Update quantity if provided
if item_in.quantity is not None:
# Check stock
if product.stock_quantity < item_in.quantity:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Not enough stock available. Only {product.stock_quantity} items left."
)
cart_item.quantity = item_in.quantity
# Update custom properties if provided
if item_in.custom_properties is not None:
cart_item.custom_properties = json.dumps(item_in.custom_properties)
db.commit()
db.refresh(cart_item)
# Enhance cart item with product information for response
product_image = None
primary_image = next((img for img in product.images if img.is_primary), None)
if primary_image:
product_image = primary_image.image_url
elif product.images:
product_image = product.images[0].image_url
cart_item.product_name = product.name
cart_item.product_image = product_image
cart_item.current_price = product.current_price
cart_item.subtotal = product.current_price * cart_item.quantity
logger.info(f"Cart item updated: ID {item_id} for user {current_user.email}")
return cart_item
@router.delete("/items/{item_id}", response_model=dict)
async def remove_cart_item(
item_id: str,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Remove an item from the shopping cart.
"""
cart_item = db.query(CartItem).filter(
CartItem.id == item_id,
CartItem.user_id == current_user.id
).first()
if not cart_item:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Cart item not found"
)
# Delete the cart item
db.delete(cart_item)
db.commit()
logger.info(f"Cart item removed: ID {item_id} for user {current_user.email}")
return {"message": "Item removed from cart successfully"}
@router.delete("/", response_model=dict)
async def clear_cart(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_active_user)
):
"""
Clear the current user's shopping cart.
"""
db.query(CartItem).filter(CartItem.user_id == current_user.id).delete()
db.commit()
logger.info(f"Cart cleared for user {current_user.email}")
return {"message": "Cart cleared successfully"}