""" 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 (commented out until other models are fully implemented) # 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") # Simple property to avoid relationship issues during testing @property def users(self): """Get users for this tenant.""" return [] @property def documents(self): """Get documents for this tenant.""" return [] @property def commitments(self): """Get commitments for this tenant.""" return [] @property def audit_logs(self): """Get audit logs for this tenant.""" return [] def __repr__(self): return f"" @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