from datetime import datetime from typing import TYPE_CHECKING from sqlalchemy import ( DateTime, Enum, ForeignKey, Index, Integer, String, func, ) from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db.base import Base from app.models.enums import OrderStatus, TicketStatus if TYPE_CHECKING: from app.models.catalog import Event, TicketType class Order(Base): __tablename__ = "orders" id: Mapped[int] = mapped_column(Integer, primary_key=True) user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="RESTRICT"), index=True) event_id: Mapped[int] = mapped_column(ForeignKey("events.id", ondelete="RESTRICT"), index=True) status: Mapped[OrderStatus] = mapped_column(Enum(OrderStatus), default=OrderStatus.pending, index=True) total_paise: Mapped[int] = mapped_column(Integer) currency: Mapped[str] = mapped_column(String(3), default="INR") payment_reference: Mapped[str | None] = mapped_column(String(128), nullable=True) provider_order_id: Mapped[str | None] = mapped_column(String(64), unique=True, nullable=True, index=True) provider_payment_id: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True) idempotency_key: Mapped[str | None] = mapped_column(String(64), unique=True, nullable=True, index=True) expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True) paid_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) updated_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), ) items: Mapped[list["OrderItem"]] = relationship( "OrderItem", back_populates="order", cascade="all, delete-orphan", ) tickets: Mapped[list["Ticket"]] = relationship( "Ticket", back_populates="order", cascade="all, delete-orphan", ) event: Mapped["Event"] = relationship("Event") class OrderItem(Base): __tablename__ = "order_items" id: Mapped[int] = mapped_column(Integer, primary_key=True) order_id: Mapped[int] = mapped_column(ForeignKey("orders.id", ondelete="CASCADE"), index=True) ticket_type_id: Mapped[int] = mapped_column(ForeignKey("ticket_types.id", ondelete="RESTRICT"), index=True) quantity: Mapped[int] = mapped_column(Integer) unit_price_paise: Mapped[int] = mapped_column(Integer) line_total_paise: Mapped[int] = mapped_column(Integer) order: Mapped["Order"] = relationship("Order", back_populates="items") ticket_type: Mapped["TicketType"] = relationship("TicketType") tickets: Mapped[list["Ticket"]] = relationship("Ticket", back_populates="order_item") class Ticket(Base): __tablename__ = "tickets" __table_args__ = (Index("ix_tickets_user_status", "user_id", "status"),) id: Mapped[int] = mapped_column(Integer, primary_key=True) code: Mapped[str] = mapped_column(String(32), unique=True, index=True) order_id: Mapped[int] = mapped_column(ForeignKey("orders.id", ondelete="CASCADE"), index=True) order_item_id: Mapped[int] = mapped_column(ForeignKey("order_items.id", ondelete="CASCADE"), index=True) ticket_type_id: Mapped[int] = mapped_column(ForeignKey("ticket_types.id", ondelete="RESTRICT"), index=True) event_id: Mapped[int] = mapped_column(ForeignKey("events.id", ondelete="RESTRICT"), index=True) user_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="RESTRICT"), index=True) status: Mapped[TicketStatus] = mapped_column(Enum(TicketStatus), default=TicketStatus.valid, index=True) qr_token: Mapped[str] = mapped_column(String(64), unique=True, index=True) used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) order: Mapped["Order"] = relationship("Order", back_populates="tickets") order_item: Mapped["OrderItem"] = relationship("OrderItem", back_populates="tickets")