rswag-online/backend/app/models/order.py

107 lines
4.3 KiB
Python

"""Order models."""
import uuid
from datetime import datetime
from enum import Enum
from sqlalchemy import String, Integer, Numeric, DateTime, ForeignKey, Text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.database import Base
class OrderStatus(str, Enum):
"""Order status enum."""
PENDING = "pending"
PAID = "paid"
PROCESSING = "processing"
PRINTING = "printing"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
REFUNDED = "refunded"
class Order(Base):
"""Order model."""
__tablename__ = "orders"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
customer_id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True), ForeignKey("customers.id"), nullable=True
)
# Payment provider info (provider-agnostic)
payment_provider: Mapped[str | None] = mapped_column(String(50), nullable=True)
payment_id: Mapped[str | None] = mapped_column(String(255), nullable=True)
payment_method: Mapped[str | None] = mapped_column(String(100), nullable=True)
status: Mapped[str] = mapped_column(String(50), default=OrderStatus.PENDING.value)
# Shipping info
shipping_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
shipping_email: Mapped[str | None] = mapped_column(String(255), nullable=True)
shipping_address_line1: Mapped[str | None] = mapped_column(String(255), nullable=True)
shipping_address_line2: Mapped[str | None] = mapped_column(String(255), nullable=True)
shipping_city: Mapped[str | None] = mapped_column(String(100), nullable=True)
shipping_state: Mapped[str | None] = mapped_column(String(100), nullable=True)
shipping_postal_code: Mapped[str | None] = mapped_column(String(20), nullable=True)
shipping_country: Mapped[str | None] = mapped_column(String(2), nullable=True)
# Financials
subtotal: Mapped[float | None] = mapped_column(Numeric(10, 2), nullable=True)
shipping_cost: Mapped[float | None] = mapped_column(Numeric(10, 2), nullable=True)
tax: Mapped[float | None] = mapped_column(Numeric(10, 2), nullable=True)
total: Mapped[float | None] = mapped_column(Numeric(10, 2), nullable=True)
currency: Mapped[str] = mapped_column(String(3), default="USD")
# Timestamps
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
updated_at: Mapped[datetime] = mapped_column(
DateTime, default=datetime.utcnow, onupdate=datetime.utcnow
)
paid_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
shipped_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
delivered_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
# Relationships
customer: Mapped["Customer | None"] = relationship("Customer", back_populates="orders")
items: Mapped[list["OrderItem"]] = relationship(
"OrderItem", back_populates="order", cascade="all, delete-orphan"
)
class OrderItem(Base):
"""Order item model."""
__tablename__ = "order_items"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
order_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("orders.id", ondelete="CASCADE"), nullable=False
)
product_slug: Mapped[str] = mapped_column(String(100), nullable=False)
product_name: Mapped[str] = mapped_column(String(255), nullable=False)
variant: Mapped[str | None] = mapped_column(String(50), nullable=True)
quantity: Mapped[int] = mapped_column(Integer, nullable=False)
unit_price: Mapped[float] = mapped_column(Numeric(10, 2), nullable=False)
# POD fulfillment
pod_provider: Mapped[str | None] = mapped_column(String(50), nullable=True)
pod_order_id: Mapped[str | None] = mapped_column(String(255), nullable=True)
pod_status: Mapped[str | None] = mapped_column(String(50), nullable=True)
pod_tracking_number: Mapped[str | None] = mapped_column(String(100), nullable=True)
pod_tracking_url: Mapped[str | None] = mapped_column(Text, nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)
# Relationships
order: Mapped["Order"] = relationship("Order", back_populates="items")