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 != GiftStatus.RESERVED: 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)) # Missing the return statement here, which is also a problem 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)