Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
193 lines
5.1 KiB
Python
193 lines
5.1 KiB
Python
# app/modules/tenancy/models/store_platform.py
|
|
"""
|
|
StorePlatform junction table for many-to-many relationship between Store and Platform.
|
|
|
|
A store CAN belong to multiple platforms (e.g., both OMS and Loyalty Program).
|
|
Each membership can have:
|
|
- Platform-specific subscription tier
|
|
- Custom subdomain for that platform
|
|
- Platform-specific settings
|
|
- Active/inactive status
|
|
"""
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
from sqlalchemy import (
|
|
JSON,
|
|
Boolean,
|
|
Column,
|
|
DateTime,
|
|
ForeignKey,
|
|
Index,
|
|
Integer,
|
|
String,
|
|
UniqueConstraint,
|
|
)
|
|
from sqlalchemy.orm import relationship
|
|
|
|
from app.core.database import Base
|
|
from models.database.base import TimestampMixin
|
|
|
|
|
|
class StorePlatform(Base, TimestampMixin):
|
|
"""
|
|
Junction table linking stores to platforms.
|
|
|
|
Allows a store to:
|
|
- Subscribe to multiple platforms (OMS + Loyalty)
|
|
- Have different tiers per platform
|
|
- Have platform-specific subdomains
|
|
- Store platform-specific settings
|
|
|
|
Example:
|
|
- Store "Orion" is on OMS platform (Professional tier)
|
|
- Store "Orion" is also on Loyalty platform (Basic tier)
|
|
"""
|
|
|
|
__tablename__ = "store_platforms"
|
|
|
|
id = Column(Integer, primary_key=True, index=True)
|
|
|
|
# ========================================================================
|
|
# Foreign Keys
|
|
# ========================================================================
|
|
|
|
store_id = Column(
|
|
Integer,
|
|
ForeignKey("stores.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
comment="Reference to the store",
|
|
)
|
|
|
|
platform_id = Column(
|
|
Integer,
|
|
ForeignKey("platforms.id", ondelete="CASCADE"),
|
|
nullable=False,
|
|
index=True,
|
|
comment="Reference to the platform",
|
|
)
|
|
|
|
tier_id = Column(
|
|
Integer,
|
|
ForeignKey("subscription_tiers.id", ondelete="SET NULL"),
|
|
nullable=True,
|
|
index=True,
|
|
comment="Platform-specific subscription tier",
|
|
)
|
|
|
|
# ========================================================================
|
|
# Membership Status
|
|
# ========================================================================
|
|
|
|
is_active = Column(
|
|
Boolean,
|
|
default=True,
|
|
nullable=False,
|
|
comment="Whether the store is active on this platform",
|
|
)
|
|
|
|
is_primary = Column(
|
|
Boolean,
|
|
default=False,
|
|
nullable=False,
|
|
comment="Whether this is the store's primary platform",
|
|
)
|
|
|
|
# ========================================================================
|
|
# Platform-Specific Configuration
|
|
# ========================================================================
|
|
|
|
custom_subdomain = Column(
|
|
String(100),
|
|
nullable=True,
|
|
comment="Platform-specific subdomain (if different from main subdomain)",
|
|
)
|
|
|
|
settings = Column(
|
|
JSON,
|
|
nullable=True,
|
|
default=dict,
|
|
comment="Platform-specific store settings",
|
|
)
|
|
|
|
# ========================================================================
|
|
# Timestamps
|
|
# ========================================================================
|
|
|
|
joined_at = Column(
|
|
DateTime(timezone=True),
|
|
default=lambda: datetime.now(UTC),
|
|
nullable=False,
|
|
comment="When the store joined this platform",
|
|
)
|
|
|
|
# ========================================================================
|
|
# Relationships
|
|
# ========================================================================
|
|
|
|
store = relationship(
|
|
"Store",
|
|
back_populates="store_platforms",
|
|
)
|
|
|
|
platform = relationship(
|
|
"Platform",
|
|
back_populates="store_platforms",
|
|
)
|
|
|
|
tier = relationship(
|
|
"SubscriptionTier",
|
|
foreign_keys=[tier_id],
|
|
)
|
|
|
|
# ========================================================================
|
|
# Constraints & Indexes
|
|
# ========================================================================
|
|
|
|
__table_args__ = (
|
|
# Each store can only be on a platform once
|
|
UniqueConstraint(
|
|
"store_id",
|
|
"platform_id",
|
|
name="uq_store_platform",
|
|
),
|
|
# Performance indexes
|
|
Index(
|
|
"idx_store_platform_active",
|
|
"store_id",
|
|
"platform_id",
|
|
"is_active",
|
|
),
|
|
Index(
|
|
"idx_store_platform_primary",
|
|
"store_id",
|
|
"is_primary",
|
|
),
|
|
)
|
|
|
|
# ========================================================================
|
|
# Properties
|
|
# ========================================================================
|
|
|
|
@property
|
|
def tier_code(self) -> str | None:
|
|
"""Get the tier code for this platform membership."""
|
|
return self.tier.code if self.tier else None
|
|
|
|
@property
|
|
def tier_name(self) -> str | None:
|
|
"""Get the tier name for this platform membership."""
|
|
return self.tier.name if self.tier else None
|
|
|
|
def __repr__(self) -> str:
|
|
return (
|
|
f"<StorePlatform("
|
|
f"store_id={self.store_id}, "
|
|
f"platform_id={self.platform_id}, "
|
|
f"is_active={self.is_active})>"
|
|
)
|
|
|
|
|
|
__all__ = ["StorePlatform"]
|