# app/modules/tenancy/models/admin_platform.py """ AdminPlatform junction table for many-to-many relationship between Admin Users and Platforms. This enables platform-scoped admin access: - Super Admins: Have role='super_admin' on User model, bypass this table - Platform Admins: Have role='platform_admin', assigned to specific platforms via this junction table A platform admin CAN be assigned to multiple platforms (e.g., both OMS and Loyalty). """ from datetime import UTC, datetime from sqlalchemy import ( Boolean, Column, DateTime, ForeignKey, Index, Integer, UniqueConstraint, ) from sqlalchemy.orm import relationship from app.core.database import Base from models.database.base import TimestampMixin class AdminPlatform(Base, TimestampMixin): """ Junction table linking admin users to platforms they can manage. Allows a platform admin to: - Manage specific platforms only (not all) - Be assigned to multiple platforms - Have assignment tracked for audit purposes Example: - User "john@example.com" (admin) can manage OMS platform only - User "jane@example.com" (admin) can manage both OMS and Loyalty platforms """ __tablename__ = "admin_platforms" id = Column(Integer, primary_key=True, index=True) # ======================================================================== # Foreign Keys # ======================================================================== user_id = Column( Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True, comment="Reference to the admin user", ) platform_id = Column( Integer, ForeignKey("platforms.id", ondelete="CASCADE"), nullable=False, index=True, comment="Reference to the platform", ) # ======================================================================== # Assignment Status # ======================================================================== is_active = Column( Boolean, default=True, nullable=False, comment="Whether the admin assignment is active", ) # ======================================================================== # Audit Fields # ======================================================================== assigned_at = Column( DateTime(timezone=True), default=lambda: datetime.now(UTC), nullable=False, comment="When the admin was assigned to this platform", ) assigned_by_user_id = Column( Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, comment="Super admin who made this assignment", ) # ======================================================================== # Relationships # ======================================================================== user = relationship( "User", foreign_keys=[user_id], back_populates="admin_platforms", ) platform = relationship( "Platform", back_populates="admin_platforms", ) assigned_by = relationship( "User", foreign_keys=[assigned_by_user_id], ) # ======================================================================== # Constraints & Indexes # ======================================================================== __table_args__ = ( # Each admin can only be assigned to a platform once UniqueConstraint( "user_id", "platform_id", name="uq_admin_platform", ), # Performance indexes Index( "idx_admin_platform_active", "user_id", "platform_id", "is_active", ), Index( "idx_admin_platform_user_active", "user_id", "is_active", ), ) # ======================================================================== # Properties # ======================================================================== @property def platform_code(self) -> str | None: """Get the platform code for this assignment.""" return self.platform.code if self.platform else None @property def platform_name(self) -> str | None: """Get the platform name for this assignment.""" return self.platform.name if self.platform else None def __repr__(self) -> str: return ( f"" ) __all__ = ["AdminPlatform"]