Allow cancellation of gifts with a status of either RESERVED or RECEIVED. This ensures consistency in handling gift statuses during reservation management across the application.
1051 lines
38 KiB
Python
1051 lines
38 KiB
Python
from typing import List, Optional, Dict, Any
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Path
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.api.dependencies.auth import get_current_active_user, get_current_user, get_optional_current_user
|
|
from app.core.database import get_db
|
|
from app.crud.event import event_crud
|
|
from app.crud.gift import gift_item_crud, gift_category_crud, gift_purchase_crud, event_gift_category_crud
|
|
from app.crud.guest import guest_crud
|
|
from app.models.gift import GiftStatus, EventGiftCategory, GiftItem as GiftItemModel
|
|
from app.models.user import User
|
|
from app.schemas.gifts import (
|
|
GiftItem, GiftItemCreate, GiftItemUpdate,
|
|
GiftCategory, GiftCategoryCreate, GiftCategoryUpdate,
|
|
GiftPurchase, GiftPurchaseCreate, EventGiftCategoryCreate
|
|
)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
# ===== GIFT CATEGORIES ===== #
|
|
|
|
@router.post("/categories/", response_model=GiftCategory, operation_id="create_gift_category")
|
|
def create_gift_category(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
category_in: GiftCategoryCreate,
|
|
event_id: UUID,
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Create new gift category and associate it with an event.
|
|
"""
|
|
# Check if user has permission to manage this event
|
|
event = event_crud.get(db, event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions (basic implementation)
|
|
# In a complete system, use the EventManager permissions
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# Create the category
|
|
category = gift_category_crud.create(db, obj_in=category_in)
|
|
|
|
# Create the association between the category and the event
|
|
association_data = EventGiftCategoryCreate(
|
|
event_id=event_id,
|
|
category_id=category.id,
|
|
display_order=0, # Default display order
|
|
is_visible=True # Default visibility
|
|
)
|
|
event_gift_category_crud.create(db, obj_in=association_data)
|
|
|
|
# Set display properties for the response
|
|
category.display_order = 0
|
|
category.is_visible = True
|
|
|
|
return category
|
|
|
|
|
|
@router.get("/categories/event/{event_id}", response_model=List[GiftCategory], operation_id="read_gift_categories")
|
|
def read_gift_categories(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
event_id: UUID = Path(...),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
include_hidden: bool = False,
|
|
include_gifts: bool = False,
|
|
current_user: Optional[User] = Depends(get_optional_current_user)
|
|
) -> Any:
|
|
"""
|
|
Retrieve gift categories for an event.
|
|
"""
|
|
# Check if event exists and is accessible
|
|
event = event_crud.get(db, event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# For public access, ensure event is public
|
|
if not current_user and not event.is_public:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# Get categories for this event using the association table
|
|
categories = event_gift_category_crud.get_categories_by_event(
|
|
db, event_id=event_id, skip=skip, limit=limit, include_hidden=include_hidden
|
|
)
|
|
|
|
# Create a list to hold the enhanced category responses
|
|
enhanced_categories = []
|
|
|
|
for category in categories:
|
|
# Get the association to access display_order and is_visible
|
|
association = event_gift_category_crud.get(
|
|
db, event_id=event_id, category_id=category.id
|
|
)
|
|
|
|
# Default values
|
|
display_order = None
|
|
is_visible = None
|
|
if association:
|
|
display_order = association.display_order
|
|
is_visible = association.is_visible
|
|
|
|
# Calculate statistics for this event
|
|
total_gifts = 0
|
|
available_gifts = 0
|
|
gifts_list = None
|
|
|
|
# If include_gifts is true, fetch gift items for the category
|
|
if include_gifts:
|
|
gifts = gift_item_crud.get_multi_by_event(
|
|
db, event_id=event_id, category_id=category.id, include_hidden=include_hidden
|
|
)
|
|
gifts_list = gifts
|
|
|
|
# Calculate statistics
|
|
for gift in gifts:
|
|
if gift.event_id == event_id:
|
|
total_gifts += 1
|
|
if gift.status == GiftStatus.AVAILABLE and gift.is_visible:
|
|
available_gifts += 1
|
|
else:
|
|
# Calculate statistics without fetching all gifts
|
|
if category.gifts:
|
|
for gift in category.gifts:
|
|
if gift.event_id == event_id:
|
|
total_gifts += 1
|
|
if gift.status == GiftStatus.AVAILABLE and gift.is_visible:
|
|
available_gifts += 1
|
|
|
|
# Create a new category response with the calculated values
|
|
category_data = {
|
|
**category.__dict__,
|
|
"display_order": display_order,
|
|
"is_visible": is_visible,
|
|
"total_gifts": total_gifts,
|
|
"available_gifts": available_gifts
|
|
}
|
|
|
|
if gifts_list is not None:
|
|
category_data["gifts"] = gifts_list
|
|
|
|
# Remove SQLAlchemy state attributes
|
|
if "_sa_instance_state" in category_data:
|
|
del category_data["_sa_instance_state"]
|
|
|
|
enhanced_category = GiftCategory(**category_data)
|
|
enhanced_categories.append(enhanced_category)
|
|
|
|
# Replace the original categories list with the enhanced one
|
|
categories = enhanced_categories
|
|
|
|
return categories
|
|
|
|
|
|
@router.get("/categories/{category_id}", response_model=GiftCategory, operation_id="read_gift_category")
|
|
def read_gift_category(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
category_id: UUID = Path(...),
|
|
event_id: Optional[UUID] = None,
|
|
include_gifts: bool = False,
|
|
current_user: Optional[User] = Depends(get_optional_current_user)
|
|
) -> Any:
|
|
"""
|
|
Get gift category by ID. If event_id is provided, includes event-specific display settings.
|
|
"""
|
|
category = gift_category_crud.get(db, id=category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Default values for event-specific properties
|
|
display_order = None
|
|
is_visible = None
|
|
total_gifts = None
|
|
available_gifts = None
|
|
gifts_list = None
|
|
|
|
# If event_id is provided, get event-specific information
|
|
if event_id:
|
|
# Check if event exists and is accessible
|
|
event = event_crud.get(db, event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# For public access, ensure event is public
|
|
if not event.is_public and not current_user:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# Check if this category is associated with the event
|
|
association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
|
if not association:
|
|
raise HTTPException(status_code=404, detail="Category not associated with this event")
|
|
|
|
# Get event-specific display properties
|
|
display_order = association.display_order
|
|
is_visible = association.is_visible
|
|
|
|
# Calculate statistics for this event
|
|
total_gifts = 0
|
|
available_gifts = 0
|
|
|
|
# If include_gifts is true, fetch gift items for the category in this event
|
|
if include_gifts:
|
|
gifts = gift_item_crud.get_multi_by_event(
|
|
db, event_id=event_id, category_id=category.id,
|
|
include_hidden=current_user is not None # Only include hidden for logged-in users
|
|
)
|
|
gifts_list = gifts
|
|
|
|
# Calculate statistics
|
|
for gift in gifts:
|
|
total_gifts += 1
|
|
if gift.status == GiftStatus.AVAILABLE and gift.is_visible:
|
|
available_gifts += 1
|
|
else:
|
|
# Calculate statistics without fetching all gifts
|
|
gifts_query = db.query(GiftItemModel).filter(
|
|
GiftItemModel.event_id == event_id,
|
|
GiftItemModel.category_id == category_id
|
|
)
|
|
total_gifts = gifts_query.count()
|
|
available_gifts = gifts_query.filter(
|
|
GiftItemModel.status == GiftStatus.AVAILABLE,
|
|
GiftItemModel.is_visible == True
|
|
).count()
|
|
elif include_gifts:
|
|
# If no event_id but include_gifts is true, just get all gifts for this category
|
|
# This is less useful without event context but included for completeness
|
|
gifts = db.query(GiftItemModel).filter(GiftItemModel.category_id == category_id).all()
|
|
gifts_list = gifts
|
|
|
|
# Create a new category response with the calculated values
|
|
category_data = {
|
|
**category.__dict__,
|
|
"display_order": display_order,
|
|
"is_visible": is_visible,
|
|
"total_gifts": total_gifts,
|
|
"available_gifts": available_gifts
|
|
}
|
|
|
|
if gifts_list is not None:
|
|
category_data["gifts"] = gifts_list
|
|
|
|
# Remove SQLAlchemy state attributes
|
|
if "_sa_instance_state" in category_data:
|
|
del category_data["_sa_instance_state"]
|
|
|
|
# Create a new category instance with the enhanced data
|
|
category = GiftCategory(**category_data)
|
|
|
|
return category
|
|
|
|
|
|
@router.put("/categories/{category_id}", response_model=GiftCategory, operation_id="update_gift_category")
|
|
def update_gift_category(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
category_id: UUID = Path(...),
|
|
category_in: GiftCategoryUpdate,
|
|
event_id: Optional[UUID] = None,
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Update a gift category. If event_id is provided, also updates the event-specific settings.
|
|
"""
|
|
category = gift_category_crud.get(db, id=category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Update the category itself
|
|
updated_category = gift_category_crud.update(db, db_obj=category, obj_in=category_in)
|
|
|
|
# Default values for event-specific properties
|
|
display_order = None
|
|
is_visible = None
|
|
total_gifts = None
|
|
available_gifts = None
|
|
|
|
# If event_id is provided, update the event-specific settings
|
|
if event_id:
|
|
# Check if event exists
|
|
event = event_crud.get(db, event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions (basic implementation)
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# Check if this category is associated with the event
|
|
association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
|
if not association:
|
|
# If not associated, create the association
|
|
association_data = EventGiftCategoryCreate(
|
|
event_id=event_id,
|
|
category_id=category_id,
|
|
display_order=0, # Default display order
|
|
is_visible=True # Default visibility
|
|
)
|
|
association = event_gift_category_crud.create(db, obj_in=association_data)
|
|
else:
|
|
# If display_order or is_visible are in the update data, update the association
|
|
association_update = {}
|
|
if hasattr(category_in, 'display_order') and category_in.display_order is not None:
|
|
association_update['display_order'] = category_in.display_order
|
|
if hasattr(category_in, 'is_visible') and category_in.is_visible is not None:
|
|
association_update['is_visible'] = category_in.is_visible
|
|
|
|
if association_update:
|
|
association = event_gift_category_crud.update(
|
|
db, db_obj=association, obj_in=association_update
|
|
)
|
|
|
|
# Get event-specific properties for the response
|
|
display_order = association.display_order
|
|
is_visible = association.is_visible
|
|
|
|
# Calculate statistics for this event
|
|
gifts_query = db.query(GiftItem).filter(
|
|
GiftItem.event_id == event_id,
|
|
GiftItem.category_id == category_id
|
|
)
|
|
total_gifts = gifts_query.count()
|
|
available_gifts = gifts_query.filter(
|
|
GiftItem.status == GiftStatus.AVAILABLE,
|
|
GiftItem.is_visible == True
|
|
).count()
|
|
|
|
# Create a new category response with the calculated values
|
|
category_data = {
|
|
**updated_category.__dict__,
|
|
"display_order": display_order,
|
|
"is_visible": is_visible,
|
|
"total_gifts": total_gifts,
|
|
"available_gifts": available_gifts
|
|
}
|
|
|
|
# Remove SQLAlchemy state attributes
|
|
if "_sa_instance_state" in category_data:
|
|
del category_data["_sa_instance_state"]
|
|
|
|
# Create a new category instance with the enhanced data
|
|
updated_category = GiftCategory(**category_data)
|
|
|
|
return updated_category
|
|
|
|
|
|
@router.delete("/categories/{category_id}", response_model=GiftCategory, operation_id="delete_gift_category")
|
|
def delete_gift_category(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
category_id: UUID = Path(...),
|
|
event_id: Optional[UUID] = None,
|
|
force: bool = False,
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Delete a gift category. If event_id is provided, only removes the association with that event.
|
|
If force=True and no event_id is provided, deletes the category completely.
|
|
"""
|
|
category = gift_category_crud.get(db, id=category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Default values for event-specific properties
|
|
display_order = None
|
|
is_visible = None
|
|
total_gifts = None
|
|
available_gifts = None
|
|
gifts_list = None
|
|
|
|
if event_id:
|
|
# Check if event exists
|
|
event = event_crud.get(db, event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions (basic implementation)
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# Check if this category is associated with the event
|
|
association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
|
if not association:
|
|
raise HTTPException(status_code=404, detail="Category not associated with this event")
|
|
|
|
# Remove the association
|
|
event_gift_category_crud.remove(db, event_id=event_id, category_id=category_id)
|
|
|
|
# Create a new category response with the calculated values
|
|
category_data = {
|
|
**category.__dict__,
|
|
"display_order": display_order,
|
|
"is_visible": is_visible,
|
|
"total_gifts": total_gifts,
|
|
"available_gifts": available_gifts
|
|
}
|
|
|
|
# Remove SQLAlchemy state attributes
|
|
if "_sa_instance_state" in category_data:
|
|
del category_data["_sa_instance_state"]
|
|
|
|
# Create a new category instance with the enhanced data
|
|
category_response = GiftCategory(**category_data)
|
|
|
|
return category_response
|
|
elif force:
|
|
# Check if the user has permission to delete the category
|
|
# This is a more restrictive operation, so we might want to add additional checks
|
|
|
|
# Check if the category is used by any events
|
|
# Get all associations for this category
|
|
associations = db.query(EventGiftCategory).filter(
|
|
EventGiftCategory.category_id == category_id
|
|
).all()
|
|
|
|
if associations and len(associations) > 0:
|
|
# If there are associations, we need to check if the user has permission to manage all events
|
|
for assoc in associations:
|
|
event = event_crud.get(db, assoc.event_id)
|
|
if not event:
|
|
continue
|
|
|
|
# If the user doesn't have permission for any of the events, deny the operation
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(
|
|
status_code=403,
|
|
detail="Not enough permissions. Category is used by events you don't manage."
|
|
)
|
|
|
|
# Remove all associations
|
|
for assoc in associations:
|
|
event_gift_category_crud.remove(db, event_id=assoc.event_id, category_id=category_id)
|
|
|
|
# Now delete the category itself
|
|
deleted_category = gift_category_crud.remove(db, id=category_id)
|
|
|
|
# Create a new category response with the calculated values
|
|
category_data = {
|
|
**deleted_category.__dict__,
|
|
"display_order": display_order,
|
|
"is_visible": is_visible,
|
|
"total_gifts": total_gifts,
|
|
"available_gifts": available_gifts
|
|
}
|
|
|
|
# Remove SQLAlchemy state attributes
|
|
if "_sa_instance_state" in category_data:
|
|
del category_data["_sa_instance_state"]
|
|
|
|
# Create a new category instance with the enhanced data
|
|
category_response = GiftCategory(**category_data)
|
|
|
|
return category_response
|
|
else:
|
|
# If no event_id and not force, raise an error
|
|
raise HTTPException(
|
|
status_code=400,
|
|
detail="Must provide event_id to remove association or set force=True to delete category completely"
|
|
)
|
|
|
|
|
|
# ===== EVENT-CATEGORY ASSOCIATIONS ===== #
|
|
|
|
@router.post("/events/{event_id}/categories/{category_id}", response_model=GiftCategory, operation_id="associate_category_with_event")
|
|
def associate_category_with_event(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
event_id: UUID = Path(...),
|
|
category_id: UUID = Path(...),
|
|
display_order: int = 0,
|
|
is_visible: bool = True,
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Associate an existing category with an event.
|
|
"""
|
|
# Check if event exists
|
|
event = event_crud.get(db, event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# Check if category exists
|
|
category = gift_category_crud.get(db, id=category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Check if association already exists
|
|
existing_association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
|
if existing_association:
|
|
raise HTTPException(status_code=400, detail="Category already associated with this event")
|
|
|
|
# Create the association
|
|
association_data = EventGiftCategoryCreate(
|
|
event_id=event_id,
|
|
category_id=category_id,
|
|
display_order=display_order,
|
|
is_visible=is_visible
|
|
)
|
|
event_gift_category_crud.create(db, obj_in=association_data)
|
|
|
|
# Set display properties for the response
|
|
category.display_order = display_order
|
|
category.is_visible = is_visible
|
|
|
|
# Calculate statistics for this event
|
|
gifts_query = db.query(GiftItem).filter(
|
|
GiftItem.event_id == event_id,
|
|
GiftItem.category_id == category_id
|
|
)
|
|
category.total_gifts = gifts_query.count()
|
|
category.available_gifts = gifts_query.filter(
|
|
GiftItem.status == GiftStatus.AVAILABLE,
|
|
GiftItem.is_visible == True
|
|
).count()
|
|
|
|
return category
|
|
|
|
|
|
@router.put("/events/{event_id}/categories/{category_id}", response_model=GiftCategory, operation_id="update_category_event_settings")
|
|
def update_category_event_settings(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
event_id: UUID = Path(...),
|
|
category_id: UUID = Path(...),
|
|
display_order: Optional[int] = None,
|
|
is_visible: Optional[bool] = None,
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Update the display settings for a category in an event.
|
|
"""
|
|
# Check if event exists
|
|
event = event_crud.get(db, event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# Check if category exists
|
|
category = gift_category_crud.get(db, id=category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Check if association exists
|
|
association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
|
if not association:
|
|
raise HTTPException(status_code=404, detail="Category not associated with this event")
|
|
|
|
# Update the association
|
|
update_data = {}
|
|
if display_order is not None:
|
|
update_data['display_order'] = display_order
|
|
if is_visible is not None:
|
|
update_data['is_visible'] = is_visible
|
|
|
|
if update_data:
|
|
association = event_gift_category_crud.update(db, db_obj=association, obj_in=update_data)
|
|
|
|
# Set display properties for the response
|
|
category.display_order = association.display_order
|
|
category.is_visible = association.is_visible
|
|
|
|
# Calculate statistics for this event
|
|
gifts_query = db.query(GiftItem).filter(
|
|
GiftItem.event_id == event_id,
|
|
GiftItem.category_id == category_id
|
|
)
|
|
category.total_gifts = gifts_query.count()
|
|
category.available_gifts = gifts_query.filter(
|
|
GiftItem.status == GiftStatus.AVAILABLE,
|
|
GiftItem.is_visible == True
|
|
).count()
|
|
|
|
return category
|
|
|
|
|
|
@router.get("/categories/{category_id}/events", response_model=List[Dict[str, Any]], operation_id="get_events_for_category")
|
|
def get_events_for_category(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
category_id: UUID = Path(...),
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Get all events that use a specific category.
|
|
"""
|
|
# Check if category exists
|
|
category = gift_category_crud.get(db, id=category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Get all events for this category
|
|
events = event_gift_category_crud.get_events_by_category(db, category_id=category_id)
|
|
|
|
# Filter events that the user has permission to see
|
|
result = []
|
|
for event in events:
|
|
# Check if user has permission to see this event
|
|
if event.created_by == current_user.id or event.is_public:
|
|
# Get the association to access display_order and is_visible
|
|
association = event_gift_category_crud.get(db, event_id=event.id, category_id=category_id)
|
|
if association:
|
|
# Create a response with event details and association settings
|
|
event_data = {
|
|
"event_id": event.id,
|
|
"event_title": event.title,
|
|
"event_date": event.event_date,
|
|
"display_order": association.display_order,
|
|
"is_visible": association.is_visible
|
|
}
|
|
result.append(event_data)
|
|
|
|
return result
|
|
|
|
|
|
@router.put("/categories/{category_id}/reorder", response_model=GiftCategory, operation_id="reorder_gifts_in_category")
|
|
def reorder_gifts_in_category(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
category_id: UUID = Path(...),
|
|
gift_orders: Dict[UUID, int],
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Reorder gifts within a category.
|
|
"""
|
|
category = gift_category_crud.get(db, id=category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Check if user has permission to manage this event
|
|
event = event_crud.get(db, category.event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions (basic implementation)
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
return gift_category_crud.reorder_gifts(db, category_id=category_id, gift_orders=gift_orders)
|
|
|
|
|
|
# ===== GIFT ITEMS ===== #
|
|
|
|
@router.post("/items/", response_model=GiftItem, operation_id="create_gift_item")
|
|
def create_gift_item(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
item_in: GiftItemCreate,
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Create new gift item.
|
|
"""
|
|
# Check if user has permission to manage this event
|
|
event = event_crud.get(db, item_in.event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions (basic implementation)
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# If category is specified, check if it exists
|
|
if item_in.category_id:
|
|
category = gift_category_crud.get(db, id=item_in.category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Check category belongs to the same event by checking the association
|
|
association = event_gift_category_crud.get(db, event_id=item_in.event_id, category_id=item_in.category_id)
|
|
if not association:
|
|
raise HTTPException(status_code=400, detail="Category does not belong to this event")
|
|
|
|
return gift_item_crud.create(db, obj_in=item_in)
|
|
|
|
|
|
@router.get("/items/event/{event_id}", response_model=List[GiftItem], operation_id="read_gift_items")
|
|
def read_gift_items(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
event_id: UUID = Path(...),
|
|
skip: int = 0,
|
|
limit: int = 100,
|
|
include_hidden: bool = False,
|
|
category_id: Optional[UUID] = None,
|
|
current_user: Optional[User] = Depends(get_optional_current_user)
|
|
) -> Any:
|
|
"""
|
|
Retrieve gift items for an event.
|
|
"""
|
|
# Check if event exists and is accessible
|
|
event = event_crud.get(db, event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# For public access, ensure event is public
|
|
if not current_user and not event.is_public:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# If category is specified, check if it exists
|
|
if category_id:
|
|
category = gift_category_crud.get(db, id=category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Check category belongs to the requested event by checking the association
|
|
association = event_gift_category_crud.get(db, event_id=event_id, category_id=category_id)
|
|
if not association:
|
|
raise HTTPException(status_code=400, detail="Category does not belong to this event")
|
|
|
|
return gift_item_crud.get_multi_by_event(
|
|
db, event_id=event_id, skip=skip, limit=limit,
|
|
include_hidden=include_hidden, category_id=category_id
|
|
)
|
|
|
|
|
|
@router.get("/items/{item_id}", response_model=GiftItem, operation_id="read_gift_item")
|
|
def read_gift_item(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
item_id: UUID = Path(...),
|
|
current_user: Optional[User] = Depends(get_optional_current_user)
|
|
) -> Any:
|
|
"""
|
|
Get gift item by ID.
|
|
"""
|
|
gift = gift_item_crud.get(db, id=item_id)
|
|
if not gift:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
# Check if event is public or user is authorized
|
|
event = event_crud.get(db, gift.event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
if not event.is_public and not current_user:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# Check if gift is visible for public users
|
|
if not current_user and not gift.is_visible:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
return gift
|
|
|
|
|
|
@router.put("/items/{item_id}", response_model=GiftItem, operation_id="update_gift_item")
|
|
def update_gift_item(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
item_id: UUID = Path(...),
|
|
item_in: GiftItemUpdate,
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Update a gift item.
|
|
"""
|
|
gift = gift_item_crud.get(db, id=item_id)
|
|
if not gift:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
# Check if user has permission to manage this event
|
|
event = event_crud.get(db, gift.event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions (basic implementation)
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
# If changing category, check if new category exists and belongs to same event
|
|
if item_in.category_id and item_in.category_id != gift.category_id:
|
|
category = gift_category_crud.get(db, id=item_in.category_id)
|
|
if not category:
|
|
raise HTTPException(status_code=404, detail="Gift category not found")
|
|
|
|
# Check category belongs to the same event by checking the association
|
|
association = event_gift_category_crud.get(db, event_id=gift.event_id, category_id=item_in.category_id)
|
|
if not association:
|
|
raise HTTPException(status_code=400, detail="Category does not belong to this event")
|
|
|
|
return gift_item_crud.update(db, db_obj=gift, obj_in=item_in)
|
|
|
|
|
|
@router.delete("/items/{item_id}", response_model=GiftItem, operation_id="delete_gift_item")
|
|
def delete_gift_item(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
item_id: UUID = Path(...),
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Delete a gift item.
|
|
"""
|
|
gift = gift_item_crud.get(db, id=item_id)
|
|
if not gift:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
# Check if user has permission to manage this event
|
|
event = event_crud.get(db, gift.event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions (basic implementation)
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
return gift_item_crud.remove(db, id=item_id)
|
|
|
|
|
|
@router.put("/items/{item_id}/status", response_model=GiftItem, operation_id="update_gift_item_status")
|
|
def update_gift_item_status(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
item_id: UUID = Path(...),
|
|
status: GiftStatus,
|
|
current_user: User = Depends(get_current_active_user)
|
|
) -> Any:
|
|
"""
|
|
Update a gift item's status.
|
|
"""
|
|
gift = gift_item_crud.get(db, id=item_id)
|
|
if not gift:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
# Check if user has permission to manage this event
|
|
event = event_crud.get(db, gift.event_id)
|
|
if not event:
|
|
raise HTTPException(status_code=404, detail="Event not found")
|
|
|
|
# Check permissions (basic implementation)
|
|
if event.created_by != current_user.id:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
return gift_item_crud.update_status(db, gift_id=item_id, new_status=status)
|
|
|
|
|
|
@router.post("/items/{item_id}/reserve", response_model=GiftItem, operation_id="reserve_gift_item")
|
|
def reserve_gift_item(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
item_id: UUID = Path(...),
|
|
guest_id: UUID,
|
|
quantity: Optional[int] = 1,
|
|
notes: Optional[str] = None,
|
|
current_user: Optional[User] = Depends(get_optional_current_user)
|
|
) -> Any:
|
|
"""
|
|
Reserve a gift item for a guest with specified quantity.
|
|
Default quantity is 1 if not provided.
|
|
"""
|
|
gift = gift_item_crud.get(db, id=item_id)
|
|
if not gift:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
# Check if gift is available
|
|
if gift.status == GiftStatus.RECEIVED or gift.status == GiftStatus.REMOVED:
|
|
raise HTTPException(status_code=400, detail="Gift is not available for reservation")
|
|
|
|
# Validate quantity
|
|
if quantity <= 0:
|
|
raise HTTPException(status_code=400, detail="Quantity must be a positive number")
|
|
|
|
# Check if requested quantity is valid
|
|
if quantity > gift.remaining_quantity:
|
|
raise HTTPException(status_code=400,
|
|
detail=f"Requested quantity ({quantity}) exceeds available quantity ({gift.remaining_quantity})")
|
|
|
|
# Check if guest exists
|
|
guest = guest_crud.get(db, id=guest_id)
|
|
if not guest:
|
|
raise HTTPException(status_code=404, detail="Guest not found")
|
|
|
|
# Check if guest belongs to the same event
|
|
if guest.event_id != gift.event_id:
|
|
raise HTTPException(status_code=400, detail="Guest does not belong to this event")
|
|
|
|
# For admin users, allow direct reservation
|
|
if current_user:
|
|
return gift_item_crud.reserve_gift(db, gift_id=item_id, guest_id=guest_id, quantity=quantity, notes=notes)
|
|
|
|
# For public users, additional validation
|
|
# Check if event is public
|
|
event = event_crud.get(db, gift.event_id)
|
|
if not event or not event.is_public:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
return gift_item_crud.reserve_gift(db, gift_id=item_id, guest_id=guest_id, quantity=quantity, notes=notes)
|
|
|
|
@router.post("/items/{item_id}/cancel-reservation", response_model=GiftItem, operation_id="cancel_gift_reservation")
|
|
def cancel_gift_reservation(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
item_id: UUID = Path(...),
|
|
guest_id: UUID,
|
|
current_user: Optional[User] = Depends(get_optional_current_user)
|
|
) -> Any:
|
|
"""
|
|
Cancel a gift reservation.
|
|
"""
|
|
gift = gift_item_crud.get(db, id=item_id)
|
|
if not gift:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
# Check if gift is reserved
|
|
if gift.status not in [GiftStatus.RESERVED, GiftStatus.RECEIVED]:
|
|
raise HTTPException(status_code=400, detail="Gift is not currently reserved")
|
|
|
|
# Check if guest exists
|
|
guest = guest_crud.get(db, id=guest_id)
|
|
if not guest:
|
|
raise HTTPException(status_code=404, detail="Guest not found")
|
|
|
|
# For admin users, allow direct cancellation
|
|
if current_user:
|
|
try:
|
|
return gift_item_crud.cancel_reservation(db, gift_id=item_id, guest_id=guest_id)
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
|
|
|
|
return gift_item_crud.cancel_reservation(db, gift_id=item_id, guest_id=guest_id)
|
|
|
|
# ===== GIFT PURCHASES ===== #
|
|
|
|
@router.post("/purchases/", response_model=GiftPurchase, operation_id="create_gift_purchase")
|
|
def create_gift_purchase(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
purchase_in: GiftPurchaseCreate,
|
|
current_user: Optional[User] = Depends(get_optional_current_user)
|
|
) -> Any:
|
|
"""
|
|
Create a gift purchase record.
|
|
"""
|
|
# Check if gift exists
|
|
gift = gift_item_crud.get(db, id=purchase_in.gift_id)
|
|
if not gift:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
# Check if guest exists
|
|
guest = guest_crud.get(db, id=purchase_in.guest_id)
|
|
if not guest:
|
|
raise HTTPException(status_code=404, detail="Guest not found")
|
|
|
|
# Check if guest belongs to the same event as the gift
|
|
if guest.event_id != gift.event_id:
|
|
raise HTTPException(status_code=400, detail="Guest does not belong to this event")
|
|
|
|
# For public users, additional validation
|
|
if not current_user:
|
|
# Check if event is public
|
|
event = event_crud.get(db, gift.event_id)
|
|
if not event or not event.is_public:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
return gift_purchase_crud.create(db, obj_in=purchase_in)
|
|
|
|
|
|
@router.get("/purchases/{purchase_id}", response_model=GiftPurchase, operation_id="read_gift_purchase")
|
|
def read_gift_purchase(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
purchase_id: UUID = Path(...),
|
|
current_user: Optional[User] = Depends(get_optional_current_user)
|
|
) -> Any:
|
|
"""
|
|
Get a gift purchase by ID.
|
|
"""
|
|
purchase = gift_purchase_crud.get(db, id=purchase_id)
|
|
if not purchase:
|
|
raise HTTPException(status_code=404, detail="Gift purchase not found")
|
|
|
|
# If user is authenticated, allow access
|
|
if current_user:
|
|
return purchase
|
|
|
|
# For public users, check if the event is public
|
|
gift = gift_item_crud.get(db, id=purchase.gift_id)
|
|
if not gift:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
event = event_crud.get(db, gift.event_id)
|
|
if not event or not event.is_public:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
return purchase
|
|
|
|
|
|
@router.get("/purchases/gift/{gift_id}", response_model=List[GiftPurchase], operation_id="read_gift_purchases_by_gift")
|
|
def read_gift_purchases_by_gift(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
gift_id: UUID = Path(...),
|
|
current_user: Optional[User] = Depends(get_optional_current_user)
|
|
) -> Any:
|
|
"""
|
|
Get all purchases for a specific gift.
|
|
"""
|
|
# Check if gift exists
|
|
gift = gift_item_crud.get(db, id=gift_id)
|
|
if not gift:
|
|
raise HTTPException(status_code=404, detail="Gift item not found")
|
|
|
|
# If user is authenticated, allow access
|
|
if current_user:
|
|
return gift_purchase_crud.get_by_gift(db, gift_id=gift_id)
|
|
|
|
# For public users, check if the event is public
|
|
event = event_crud.get(db, gift.event_id)
|
|
if not event or not event.is_public:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
return gift_purchase_crud.get_by_gift(db, gift_id=gift_id)
|
|
|
|
|
|
@router.get("/purchases/guest/{guest_id}", response_model=List[GiftPurchase], operation_id="read_gift_purchases_by_guest")
|
|
def read_gift_purchases_by_guest(
|
|
*,
|
|
db: Session = Depends(get_db),
|
|
guest_id: UUID = Path(...),
|
|
) -> Any:
|
|
"""
|
|
Get all purchases made by a specific guest.
|
|
"""
|
|
# Check if guest exists
|
|
guest = guest_crud.get(db, id=guest_id)
|
|
if not guest:
|
|
raise HTTPException(status_code=404, detail="Guest not found")
|
|
|
|
|
|
# For public users, check if the event is public
|
|
event = event_crud.get(db, guest.event_id)
|
|
if not event or not event.is_public:
|
|
raise HTTPException(status_code=403, detail="Not enough permissions")
|
|
|
|
return gift_purchase_crud.get_gift_reservations_by_guest(db, guest_id=guest_id)
|