Spaces:
Runtime error
Runtime error
| """ | |
| Subscription model - Tracks therapist billing and student limits | |
| """ | |
| from sqlalchemy import Column, Integer, String, Float, DateTime, ForeignKey, Boolean, Enum | |
| from sqlalchemy.orm import relationship | |
| from sqlalchemy.sql import func | |
| from config.database import Base | |
| import enum | |
| class PlanType(enum.Enum): | |
| FREE = "free" | |
| BASIC = "basic" # e.g., $29/month - up to 5 students | |
| PROFESSIONAL = "professional" # e.g., $79/month - up to 20 students | |
| ENTERPRISE = "enterprise" # Custom pricing - unlimited students | |
| class BillingPeriod(enum.Enum): | |
| MONTHLY = "monthly" | |
| YEARLY = "yearly" | |
| class Subscription(Base): | |
| __tablename__ = "subscriptions" | |
| id = Column(Integer, primary_key=True, index=True) | |
| therapist_id = Column(Integer, ForeignKey("therapists.id"), unique=True, nullable=False) | |
| # Plan details | |
| plan_type = Column(Enum(PlanType), nullable=False, default=PlanType.FREE) | |
| billing_period = Column(Enum(BillingPeriod), nullable=True) | |
| # Pricing | |
| base_price = Column(Float, default=0.0) # Base monthly price | |
| per_student_price = Column(Float, default=0.0) # Additional cost per student over limit | |
| # Limits | |
| included_students = Column(Integer, default=1) # Students included in base price | |
| max_students = Column(Integer, nullable=True) # Hard limit (null = unlimited) | |
| current_student_count = Column(Integer, default=0) # Active students | |
| # Billing | |
| stripe_customer_id = Column(String, nullable=True) | |
| stripe_subscription_id = Column(String, nullable=True) | |
| # Status | |
| is_active = Column(Boolean, default=True) | |
| trial_ends_at = Column(DateTime(timezone=True), nullable=True) | |
| current_period_start = Column(DateTime(timezone=True), nullable=True) | |
| current_period_end = Column(DateTime(timezone=True), nullable=True) | |
| # Timestamps | |
| created_at = Column(DateTime(timezone=True), server_default=func.now()) | |
| updated_at = Column(DateTime(timezone=True), onupdate=func.now()) | |
| canceled_at = Column(DateTime(timezone=True), nullable=True) | |
| # Relationships | |
| therapist = relationship("Therapist", backref="subscription") | |
| def calculate_monthly_cost(self): | |
| """Calculate total monthly cost based on current student count""" | |
| base = self.base_price | |
| # If yearly billing, calculate monthly equivalent | |
| if self.billing_period == BillingPeriod.YEARLY: | |
| base = base / 12 | |
| # Calculate overage if exceeding included students | |
| if self.current_student_count > self.included_students: | |
| overage = self.current_student_count - self.included_students | |
| additional = overage * self.per_student_price | |
| return base + additional | |
| return base | |
| def can_add_student(self): | |
| """Check if therapist can add another student""" | |
| if self.max_students is None: | |
| return True | |
| return self.current_student_count < self.max_students | |
| def __repr__(self): | |
| return f"<Subscription {self.plan_type.value} for therapist {self.therapist_id}>" |