130 lines
4.9 KiB
Python
130 lines
4.9 KiB
Python
"""
|
|
Tenant models for multi-company support in the Virtual Board Member AI System.
|
|
"""
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
from sqlalchemy import Column, String, DateTime, Boolean, Text, Integer, ForeignKey
|
|
from sqlalchemy.dialects.postgresql import UUID, JSONB
|
|
from sqlalchemy.orm import relationship
|
|
import uuid
|
|
import enum
|
|
from app.core.database import Base
|
|
|
|
|
|
class TenantStatus(str, enum.Enum):
|
|
"""Tenant status enumeration."""
|
|
ACTIVE = "active"
|
|
SUSPENDED = "suspended"
|
|
PENDING = "pending"
|
|
INACTIVE = "inactive"
|
|
|
|
|
|
class TenantTier(str, enum.Enum):
|
|
"""Tenant subscription tier."""
|
|
BASIC = "basic"
|
|
PROFESSIONAL = "professional"
|
|
ENTERPRISE = "enterprise"
|
|
CUSTOM = "custom"
|
|
|
|
|
|
class Tenant(Base):
|
|
"""Tenant model for multi-company support."""
|
|
__tablename__ = "tenants"
|
|
|
|
# Primary key
|
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
|
|
# Tenant identification
|
|
name = Column(String(255), nullable=False, unique=True)
|
|
slug = Column(String(100), nullable=False, unique=True) # URL-friendly identifier
|
|
domain = Column(String(255), nullable=True, unique=True) # Custom domain
|
|
|
|
# Company information
|
|
company_name = Column(String(255), nullable=False)
|
|
company_description = Column(Text, nullable=True)
|
|
industry = Column(String(100), nullable=True)
|
|
company_size = Column(String(50), nullable=True) # small, medium, large, enterprise
|
|
|
|
# Contact information
|
|
primary_contact_name = Column(String(255), nullable=False)
|
|
primary_contact_email = Column(String(255), nullable=False)
|
|
primary_contact_phone = Column(String(50), nullable=True)
|
|
|
|
# Subscription and billing
|
|
tier = Column(String(50), default=TenantTier.BASIC, nullable=False)
|
|
status = Column(String(50), default=TenantStatus.PENDING, nullable=False)
|
|
subscription_start_date = Column(DateTime, nullable=True)
|
|
subscription_end_date = Column(DateTime, nullable=True)
|
|
|
|
# Configuration
|
|
settings = Column(JSONB, nullable=True) # Tenant-specific settings
|
|
features_enabled = Column(JSONB, nullable=True) # Feature flags
|
|
storage_quota_gb = Column(Integer, default=10, nullable=False)
|
|
user_limit = Column(Integer, default=10, nullable=False)
|
|
|
|
# Security and compliance
|
|
data_retention_days = Column(Integer, default=2555, nullable=False) # 7 years default
|
|
encryption_level = Column(String(50), default="standard", nullable=False)
|
|
compliance_frameworks = Column(JSONB, nullable=True) # SOX, GDPR, etc.
|
|
|
|
# Timestamps
|
|
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
|
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
|
activated_at = Column(DateTime, nullable=True)
|
|
|
|
# Relationships
|
|
users = relationship("User", back_populates="tenant", cascade="all, delete-orphan")
|
|
documents = relationship("Document", back_populates="tenant", cascade="all, delete-orphan")
|
|
commitments = relationship("Commitment", back_populates="tenant", cascade="all, delete-orphan")
|
|
audit_logs = relationship("AuditLog", back_populates="tenant", cascade="all, delete-orphan")
|
|
|
|
def __repr__(self):
|
|
return f"<Tenant(id={self.id}, name='{self.name}', company='{self.company_name}')>"
|
|
|
|
@property
|
|
def is_active(self) -> bool:
|
|
"""Check if tenant is active."""
|
|
return self.status == TenantStatus.ACTIVE
|
|
|
|
@property
|
|
def is_suspended(self) -> bool:
|
|
"""Check if tenant is suspended."""
|
|
return self.status == TenantStatus.SUSPENDED
|
|
|
|
@property
|
|
def has_expired_subscription(self) -> bool:
|
|
"""Check if subscription has expired."""
|
|
if not self.subscription_end_date:
|
|
return False
|
|
return datetime.utcnow() > self.subscription_end_date
|
|
|
|
def get_setting(self, key: str, default=None):
|
|
"""Get a tenant-specific setting."""
|
|
if not self.settings:
|
|
return default
|
|
return self.settings.get(key, default)
|
|
|
|
def set_setting(self, key: str, value):
|
|
"""Set a tenant-specific setting."""
|
|
if not self.settings:
|
|
self.settings = {}
|
|
self.settings[key] = value
|
|
|
|
def is_feature_enabled(self, feature: str) -> bool:
|
|
"""Check if a feature is enabled for this tenant."""
|
|
if not self.features_enabled:
|
|
return False
|
|
return self.features_enabled.get(feature, False)
|
|
|
|
def enable_feature(self, feature: str):
|
|
"""Enable a feature for this tenant."""
|
|
if not self.features_enabled:
|
|
self.features_enabled = {}
|
|
self.features_enabled[feature] = True
|
|
|
|
def disable_feature(self, feature: str):
|
|
"""Disable a feature for this tenant."""
|
|
if not self.features_enabled:
|
|
self.features_enabled = {}
|
|
self.features_enabled[feature] = False
|