feat(subscriptions): migrate subscription management to merchant level and seed tiers

Move subscription create/edit from store detail (broken endpoint) to merchant
detail page with proper modal UI. Seed 4 subscription tiers (Essential,
Professional, Business, Enterprise) in init_production.py. Also includes
cross-module dependency declarations, store domain platform_id migration,
platform context middleware, CMS route fixes, and migration backups.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 21:04:04 +01:00
parent 7feacd5af8
commit 68493dc6cb
97 changed files with 13286 additions and 77 deletions

View File

@@ -34,6 +34,25 @@ store_content_pages_router = APIRouter(prefix="/content-pages")
logger = logging.getLogger(__name__)
def _resolve_platform_id(db: Session, store_id: int) -> int | None:
"""Resolve platform_id from store's primary StorePlatform. Returns None if not found."""
from app.modules.tenancy.models import StorePlatform
primary_sp = (
db.query(StorePlatform)
.filter(StorePlatform.store_id == store_id, StorePlatform.is_primary.is_(True))
.first()
)
if primary_sp:
return primary_sp.platform_id
# Fallback: any active store_platform
any_sp = (
db.query(StorePlatform)
.filter(StorePlatform.store_id == store_id, StorePlatform.is_active.is_(True))
.first()
)
return any_sp.platform_id if any_sp else None
# ============================================================================
# STORE CONTENT PAGES
# ============================================================================
@@ -50,8 +69,9 @@ def list_store_pages(
Returns store-specific overrides + platform defaults (store overrides take precedence).
"""
platform_id = _resolve_platform_id(db, current_user.token_store_id)
pages = content_page_service.list_pages_for_store(
db, store_id=current_user.token_store_id, include_unpublished=include_unpublished
db, platform_id=platform_id, store_id=current_user.token_store_id, include_unpublished=include_unpublished
)
return [page.to_dict() for page in pages]
@@ -148,11 +168,10 @@ def get_platform_default(
Useful for stores to view the original before/after overriding.
"""
# Get store's platform
store = store_service.get_store_by_id_optional(db, current_user.token_store_id)
platform_id = 1 # Default to OMS
if store and store.platforms:
platform_id = store.platforms[0].id
platform_id = _resolve_platform_id(db, current_user.token_store_id)
if platform_id is None:
from fastapi import HTTPException
raise HTTPException(status_code=400, detail="Store is not subscribed to any platform")
# Get platform default (store_id=None)
page = content_page_service.get_store_default_page(
@@ -177,8 +196,10 @@ def get_page(
Returns store override if exists, otherwise platform default.
"""
platform_id = _resolve_platform_id(db, current_user.token_store_id)
page = content_page_service.get_page_for_store_or_raise(
db,
platform_id=platform_id,
slug=slug,
store_id=current_user.token_store_id,
include_unpublished=include_unpublished,

View File

@@ -36,11 +36,13 @@ def get_navigation_pages(request: Request, db: Session = Depends(get_db)):
Returns store overrides + platform defaults.
"""
store = getattr(request.state, "store", None)
platform = getattr(request.state, "platform", None)
store_id = store.id if store else None
platform_id = platform.id if platform else 1
# Get all published pages for this store
pages = content_page_service.list_pages_for_store(
db, store_id=store_id, include_unpublished=False
db, platform_id=platform_id, store_id=store_id, include_unpublished=False
)
return [
@@ -64,10 +66,13 @@ def get_content_page(slug: str, request: Request, db: Session = Depends(get_db))
Returns store override if exists, otherwise platform default.
"""
store = getattr(request.state, "store", None)
platform = getattr(request.state, "platform", None)
store_id = store.id if store else None
platform_id = platform.id if platform else 1
page = content_page_service.get_page_for_store_or_raise(
db,
platform_id=platform_id,
slug=slug,
store_id=store_id,
include_unpublished=False, # Only show published pages