Updated guest schema and database models to increase the default value for `max_additional_guests` from 0 to 10 and enable `can_bring_guests` by default. This ensures new guests can bring additional attendees without manual configuration.
93 lines
3.5 KiB
Python
93 lines
3.5 KiB
Python
from datetime import datetime, timezone
|
|
from enum import Enum
|
|
|
|
from sqlalchemy import Column, String, Boolean, ForeignKey, UniqueConstraint, Integer, Enum as SQLEnum, DateTime, Table
|
|
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
|
from sqlalchemy.orm import relationship
|
|
|
|
from .base import Base, TimestampMixin, UUIDMixin
|
|
|
|
|
|
class GuestStatus(str, Enum):
|
|
INVITED = "invited" # Initial state when guest is added
|
|
PENDING = "pending" # Invitation sent, waiting for response
|
|
CONFIRMED = "confirmed" # RSVP confirmed attendance
|
|
DECLINED = "declined" # RSVP declined attendance
|
|
WAITLISTED = "waitlisted" # On waitlist if event is at capacity
|
|
CANCELLED = "cancelled" # Guest cancelled after initial confirmation
|
|
|
|
|
|
class Guest(Base, UUIDMixin, TimestampMixin):
|
|
__tablename__ = 'guests'
|
|
|
|
# Foreign Keys
|
|
event_id = Column(UUID(as_uuid=True), ForeignKey('events.id'), nullable=False)
|
|
invited_by = Column(UUID(as_uuid=True), ForeignKey('users.id'), nullable=False)
|
|
user_id = Column(UUID(as_uuid=True), ForeignKey('users.id')) # Optional, if guest is a registered user
|
|
|
|
# Guest Information
|
|
full_name = Column(String, nullable=False)
|
|
email = Column(String)
|
|
phone = Column(String)
|
|
invitation_code = Column(String, unique=True, nullable=False)
|
|
|
|
# Guest Status
|
|
status = Column(SQLEnum(GuestStatus), nullable=False, default=GuestStatus.INVITED)
|
|
max_additional_guests = Column(Integer, default=10)
|
|
actual_additional_guests = Column(Integer, default=0)
|
|
|
|
# Tracking
|
|
invitation_sent_at = Column(DateTime(timezone=True))
|
|
last_reminded_at = Column(DateTime(timezone=True))
|
|
response_date = Column(DateTime(timezone=True))
|
|
|
|
# Additional Information
|
|
dietary_restrictions = Column(String)
|
|
notes = Column(String)
|
|
custom_fields = Column(JSONB)
|
|
|
|
# Access Management
|
|
is_blocked = Column(Boolean, default=False)
|
|
can_bring_guests = Column(Boolean, default=True)
|
|
|
|
# Relationships
|
|
event = relationship("Event", back_populates="guests")
|
|
inviter = relationship("User", foreign_keys=[invited_by])
|
|
user = relationship("User", foreign_keys=[user_id])
|
|
rsvp = relationship("RSVP", back_populates="guest", uselist=False)
|
|
gifts = relationship("GiftItem", secondary="guest_gifts", back_populates="reserved_by")
|
|
|
|
__table_args__ = (
|
|
UniqueConstraint('event_id', 'email', name='uq_event_guest_email'),
|
|
)
|
|
|
|
def __repr__(self):
|
|
return f"<Guest {self.full_name} ({self.status.value})>"
|
|
|
|
@property
|
|
def total_guests(self):
|
|
"""Total number of guests including additional guests"""
|
|
return 1 + (self.actual_additional_guests or 0)
|
|
|
|
@property
|
|
def has_responded(self):
|
|
"""Check if guest has responded to invitation"""
|
|
return self.status in (GuestStatus.CONFIRMED, GuestStatus.DECLINED)
|
|
|
|
def update_status(self, new_status: GuestStatus):
|
|
"""Update guest status and record response date"""
|
|
self.status = new_status
|
|
if new_status in (GuestStatus.CONFIRMED, GuestStatus.DECLINED):
|
|
self.response_date = datetime.now(timezone.utc)
|
|
|
|
|
|
# Association table for guest gifts (many-to-many relationship)
|
|
guest_gifts = Table(
|
|
'guest_gifts',
|
|
Base.metadata,
|
|
Column('guest_id', UUID(as_uuid=True), ForeignKey('guests.id'), primary_key=True),
|
|
Column('gift_id', UUID(as_uuid=True), ForeignKey('gift_items.id'), primary_key=True),
|
|
Column('reserved_at', DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)),
|
|
Column('notes', String),
|
|
)
|