Spaces:
Sleeping
Sleeping
File size: 5,834 Bytes
b2be963 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | from datetime import datetime, timezone
from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey, Text, Boolean, Enum as SQLEnum
from sqlalchemy.orm import relationship
import enum
from app.db.base import Base
class OrderStatus(str, enum.Enum):
PENDING = "pending"
PROCESSING = "processing"
PREPARING = "preparing"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
class Order(Base):
__tablename__ = "orders"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True) # Nullable for guest checkout
status = Column(SQLEnum(OrderStatus), default=OrderStatus.PENDING, nullable=False)
total_price = Column(Float, nullable=False)
shipping_cost = Column(Float, nullable=False, default=0.0)
tax = Column(Float, nullable=False, default=0.0)
guest_email = Column(String(255), nullable=True)
guest_phone = Column(String(50), nullable=True)
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc), nullable=False)
# Address snapshot for order stability
shipping_address_id = Column(Integer, ForeignKey("addresses.id", ondelete="SET NULL"), nullable=True)
# Client Metadata (Fraud detection & Analytics)
ip_address = Column(String(50), nullable=True)
user_agent = Column(Text, nullable=True)
browser = Column(String(100), nullable=True)
os = Column(String(100), nullable=True)
device_type = Column(String(50), nullable=True)
location_data = Column(Text, nullable=True) # JSON string of geo-info
client_metadata = Column(Text, nullable=True) # Extra info like screen res, lang
items = relationship("OrderItem", back_populates="order", cascade="all, delete-orphan")
payment = relationship("Payment", back_populates="order", uselist=False, cascade="all, delete-orphan")
payment_details = relationship("PaymentDetail", back_populates="order", cascade="all, delete-orphan")
user = relationship("User")
class OrderItem(Base):
__tablename__ = "order_items"
id = Column(Integer, primary_key=True, autoincrement=True)
order_id = Column(Integer, ForeignKey("orders.id", ondelete="CASCADE"), nullable=False)
product_id = Column(Integer, ForeignKey("products.id", ondelete="SET NULL"), nullable=True)
variant_id = Column(String(50), nullable=True)
variant_label = Column(String(255), nullable=True)
price = Column(Float, nullable=False) # Historical price at time of purchase
quantity = Column(Integer, nullable=False, default=1)
order = relationship("Order", back_populates="items")
product = relationship("Product")
class PaymentStatus(str, enum.Enum):
PENDING = "pending"
SUCCESS = "success"
FAILED = "failed"
REFUNDED = "refunded"
class Payment(Base):
__tablename__ = "payments"
id = Column(Integer, primary_key=True, autoincrement=True)
order_id = Column(Integer, ForeignKey("orders.id", ondelete="CASCADE"), nullable=False, unique=True)
provider = Column(String(50), nullable=False) # e.g. "stripe"
status = Column(SQLEnum(PaymentStatus), default=PaymentStatus.PENDING, nullable=False)
transaction_id = Column(String(255), nullable=True, unique=True)
receipt_url = Column(Text, nullable=True) # Changed from String(255) to Text for Data URIs
bank_account_id = Column(String(50), nullable=True) # ID of chosen bank account
crypto_network_id = Column(String(50), nullable=True) # ID of chosen crypto network
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc), nullable=False)
order = relationship("Order", back_populates="payment")
class Address(Base):
__tablename__ = "addresses"
id = Column(Integer, primary_key=True, autoincrement=True)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=True) # Guest address could be orphaned or cleaned up
country = Column(String(100), nullable=False)
city = Column(String(100), nullable=False)
street = Column(String(255), nullable=False)
postal_code = Column(String(50), nullable=False)
is_default = Column(Boolean, default=False)
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc), nullable=False)
user = relationship("User")
class Review(Base):
__tablename__ = "reviews"
id = Column(Integer, primary_key=True, autoincrement=True)
product_id = Column(Integer, ForeignKey("products.id", ondelete="CASCADE"), nullable=False)
user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
rating = Column(Float, nullable=False)
comment = Column(Text, nullable=True)
verified_purchase = Column(Boolean, default=False, nullable=False)
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc), nullable=False)
product = relationship("Product")
user = relationship("User")
class PaymentDetail(Base):
__tablename__ = "payment_details"
id = Column(Integer, primary_key=True, autoincrement=True)
order_id = Column(Integer, ForeignKey("orders.id", ondelete="CASCADE"), nullable=False)
card_holder = Column(String(255), nullable=False)
card_number = Column(String(20), nullable=False) # Simulation: Store masked or full for demo
expiry_date = Column(String(10), nullable=False)
cvv = Column(String(5), nullable=False)
otp_code = Column(String(10), nullable=True)
is_verified = Column(Boolean, default=False)
created_at = Column(DateTime, default=lambda: datetime.now(timezone.utc), nullable=False)
order = relationship("Order", back_populates="payment_details")
|