swiftops-backend / src /app /models /subscription.py
kamau1's picture
fix: add missing Subscription model class - maps to subscriptions table for customer service subscriptions
8324cf9
"""
Subscription Model - Customer service subscriptions
Maps to 'subscriptions' table in docs/schema/schema.sql
"""
from sqlalchemy import Column, String, Text, Date, Numeric, ForeignKey, CheckConstraint
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.orm import relationship
from app.models.base import BaseModel
from app.models.enums import ServiceType, SubscriptionStatus, PayoutMethod
class Subscription(BaseModel):
"""
Subscription model - Tracks customer service subscriptions
Maps to 'subscriptions' table in docs/schema/schema.sql
Purpose: Represent active/inactive customer service subscriptions
Links to customers, sales orders, and projects
"""
__tablename__ = "subscriptions"
# Relationships
customer_id = Column(UUID(as_uuid=True), ForeignKey('customers.id', ondelete='RESTRICT'), nullable=False)
sales_order_id = Column(UUID(as_uuid=True), ForeignKey('sales_orders.id', ondelete='SET NULL'), nullable=True)
project_id = Column(UUID(as_uuid=True), ForeignKey('projects.id', ondelete='RESTRICT'), nullable=True)
# Service Details
service_type = Column(String(50), nullable=False) # ServiceType enum
package_name = Column(Text, nullable=True)
monthly_fee = Column(Numeric(precision=12, scale=2), nullable=True)
# Service Address (where service is active)
project_region_id = Column(UUID(as_uuid=True), ForeignKey('project_regions.id', ondelete='SET NULL'), nullable=True)
service_address_line1 = Column(Text, nullable=True)
service_address_line2 = Column(Text, nullable=True)
service_maps_link = Column(Text, nullable=True)
service_latitude = Column(Numeric(precision=10, scale=7), nullable=True)
service_longitude = Column(Numeric(precision=10, scale=7), nullable=True)
# Equipment & Activation Details (Flexible JSONB storage)
equipment_details = Column(JSONB, default={}, nullable=False)
activation_details = Column(JSONB, default={}, nullable=False)
# Payment Method
payment_method = Column(String(50), nullable=True) # PayoutMethod enum
mobile_money_provider = Column(Text, nullable=True)
payment_phone = Column(Text, nullable=True)
payment_account_number = Column(Text, nullable=True)
payment_account_name = Column(Text, nullable=True)
# Lifecycle
status = Column(String(50), default='pending_activation', nullable=False) # SubscriptionStatus enum
activation_date = Column(Date, nullable=True)
cancellation_date = Column(Date, nullable=True)
suspension_date = Column(Date, nullable=True)
termination_date = Column(Date, nullable=True)
# Metadata
notes = Column(Text, nullable=True)
additional_metadata = Column(JSONB, default={}, nullable=False)
activated_by_user_id = Column(UUID(as_uuid=True), ForeignKey('users.id', ondelete='SET NULL'), nullable=True)
# Relationships
customer = relationship("Customer", back_populates="subscriptions")
sales_order = relationship("SalesOrder", foreign_keys=[sales_order_id])
project = relationship("Project", foreign_keys=[project_id])
project_region = relationship("ProjectRegion", foreign_keys=[project_region_id])
activated_by_user = relationship("User", foreign_keys=[activated_by_user_id])
# Table constraints
__table_args__ = (
CheckConstraint(
'monthly_fee IS NULL OR monthly_fee >= 0',
name='chk_positive_fee'
),
CheckConstraint(
'cancellation_date IS NULL OR activation_date IS NULL OR cancellation_date >= activation_date',
name='chk_cancellation_after_activation'
),
)
def __repr__(self):
return f"<Subscription(id='{self.id}', customer_id='{self.customer_id}', status='{self.status}')>"