65 lines
2.7 KiB
Python
65 lines
2.7 KiB
Python
"""
|
|
SQLAlchemy models for coupon system
|
|
"""
|
|
from datetime import datetime
|
|
from sqlalchemy import Column, String, Integer, Boolean, DateTime, ForeignKey, JSON, Index
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.dialects.postgresql import UUID
|
|
import uuid
|
|
|
|
from shared.database import Base
|
|
|
|
|
|
class CouponModel(Base):
|
|
"""Coupon configuration table"""
|
|
__tablename__ = "coupons"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
code = Column(String(50), unique=True, nullable=False, index=True)
|
|
discount_type = Column(String(20), nullable=False) # trial_extension, percentage, fixed_amount
|
|
discount_value = Column(Integer, nullable=False) # Days/percentage/cents depending on type
|
|
max_redemptions = Column(Integer, nullable=True) # None = unlimited
|
|
current_redemptions = Column(Integer, nullable=False, default=0)
|
|
valid_from = Column(DateTime(timezone=True), nullable=False)
|
|
valid_until = Column(DateTime(timezone=True), nullable=True) # None = no expiry
|
|
active = Column(Boolean, nullable=False, default=True)
|
|
created_at = Column(DateTime(timezone=True), nullable=False, default=datetime.utcnow)
|
|
extra_data = Column(JSON, nullable=True) # Renamed from metadata to avoid SQLAlchemy reserved name
|
|
|
|
# Relationships
|
|
redemptions = relationship("CouponRedemptionModel", back_populates="coupon")
|
|
|
|
# Indexes for performance
|
|
__table_args__ = (
|
|
Index('idx_coupon_code_active', 'code', 'active'),
|
|
Index('idx_coupon_valid_dates', 'valid_from', 'valid_until'),
|
|
)
|
|
|
|
def __repr__(self):
|
|
return f"<Coupon(code='{self.code}', type='{self.discount_type}', value={self.discount_value})>"
|
|
|
|
|
|
class CouponRedemptionModel(Base):
|
|
"""Coupon redemption history table"""
|
|
__tablename__ = "coupon_redemptions"
|
|
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
tenant_id = Column(String(255), nullable=False, index=True)
|
|
coupon_code = Column(String(50), ForeignKey('coupons.code'), nullable=False)
|
|
redeemed_at = Column(DateTime(timezone=True), nullable=False, default=datetime.utcnow)
|
|
discount_applied = Column(JSON, nullable=False) # Details of discount applied
|
|
extra_data = Column(JSON, nullable=True) # Renamed from metadata to avoid SQLAlchemy reserved name
|
|
|
|
# Relationships
|
|
coupon = relationship("CouponModel", back_populates="redemptions")
|
|
|
|
# Constraints
|
|
__table_args__ = (
|
|
Index('idx_redemption_tenant', 'tenant_id'),
|
|
Index('idx_redemption_coupon', 'coupon_code'),
|
|
Index('idx_redemption_tenant_coupon', 'tenant_id', 'coupon_code'), # Prevent duplicate redemptions
|
|
)
|
|
|
|
def __repr__(self):
|
|
return f"<CouponRedemption(tenant_id='{self.tenant_id}', code='{self.coupon_code}')>"
|