Improve public pages

This commit is contained in:
Urtzi Alfaro
2025-10-17 18:14:28 +02:00
parent d4060962e4
commit 7e089b80cf
46 changed files with 5734 additions and 1084 deletions

View File

@@ -13,6 +13,7 @@ AuditLog = create_audit_log_model(Base)
# Import all models to register them with the Base metadata
from .tenants import Tenant, TenantMember, Subscription
from .coupon import CouponModel, CouponRedemptionModel
# List all models for easier access
__all__ = [
@@ -20,4 +21,6 @@ __all__ = [
"TenantMember",
"Subscription",
"AuditLog",
"CouponModel",
"CouponRedemptionModel",
]

View File

@@ -0,0 +1,64 @@
"""
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}')>"