feat: add Phase 2 migration, make urls command, fix seed script

- Create loyalty_003 migration: company-based architecture (adds
  company_id to all loyalty tables, creates company_loyalty_settings,
  renames vendor_id to enrolled_at_vendor_id on cards)
- Move platform migration back to alembic/versions (not loyalty-specific)
- Add version_locations to alembic.ini for module migration discovery
- Add make urls/urls-dev/urls-prod commands (scripts/show_urls.py)
- Fix seed_demo.py: import all module models to resolve SQLAlchemy
  string relationships, fix multiple admin query, set platform_id
  on vendor content pages
- Fix loyalty test fixtures to match Phase 2 model columns

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 20:56:07 +01:00
parent 994c6419f0
commit 922616c9e3
7 changed files with 861 additions and 29 deletions

View File

@@ -51,21 +51,37 @@ from app.core.config import settings
from app.core.database import SessionLocal
from app.core.environment import get_environment, is_production
from middleware.auth import AuthManager
from app.modules.cms.models import ContentPage
from app.modules.tenancy.models import PlatformAlert
from app.modules.tenancy.models import Company
# =============================================================================
# MODEL IMPORTS
# =============================================================================
# ALL models must be imported before any ORM query so SQLAlchemy can resolve
# cross-module string relationships (e.g. Vendor→VendorEmailTemplate,
# Platform→SubscriptionTier, Product→Inventory).
# Core modules
from app.modules.tenancy.models import Company, PlatformAlert, User, Role, Vendor, VendorUser, VendorDomain
from app.modules.cms.models import ContentPage, VendorTheme
from app.modules.catalog.models import Product
from app.modules.customers.models.customer import Customer, CustomerAddress
from app.modules.orders.models import Order, OrderItem
from app.modules.marketplace.models import (
MarketplaceImportJob,
MarketplaceProduct,
MarketplaceProductTranslation,
)
from app.modules.orders.models import Order, OrderItem
from app.modules.catalog.models import Product
from app.modules.tenancy.models import User
from app.modules.tenancy.models import Role, Vendor, VendorUser
from app.modules.tenancy.models import VendorDomain
from app.modules.cms.models import VendorTheme
# Optional modules — import to register models with SQLAlchemy
for _mod in [
"app.modules.inventory.models",
"app.modules.cart.models",
"app.modules.billing.models",
"app.modules.messaging.models",
"app.modules.loyalty.models",
]:
try:
__import__(_mod)
except ImportError:
pass
SEED_MODE = os.getenv("SEED_MODE", "normal") # normal, minimal, reset
FORCE_RESET = os.getenv("FORCE_RESET", "false").lower() in ("true", "1", "yes")
@@ -394,7 +410,7 @@ def check_environment():
def check_admin_exists(db: Session) -> bool:
"""Check if admin user exists."""
admin = db.execute(select(User).where(User.role == "admin")).scalar_one_or_none()
admin = db.execute(select(User).where(User.role == "admin").limit(1)).scalar_one_or_none()
if not admin:
print_error("No admin user found!")
@@ -799,6 +815,14 @@ def create_demo_vendor_content_pages(db: Session, vendors: list[Vendor]) -> int:
"""
created_count = 0
# Get the OMS platform ID (vendors are registered on OMS)
from app.modules.tenancy.models import Platform
oms_platform = db.execute(
select(Platform).where(Platform.code == "oms")
).scalar_one_or_none()
default_platform_id = oms_platform.id if oms_platform else 1
for vendor in vendors:
vendor_pages = VENDOR_CONTENT_PAGES.get(vendor.vendor_code, [])
@@ -819,6 +843,7 @@ def create_demo_vendor_content_pages(db: Session, vendors: list[Vendor]) -> int:
# Create vendor content page override
page = ContentPage(
platform_id=default_platform_id,
vendor_id=vendor.id,
slug=page_data["slug"],
title=page_data["title"],