Add multi-tenant architecture and advanced document parsing capabilities
This commit is contained in:
129
app/models/tenant.py
Normal file
129
app/models/tenant.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user