feat(seed): add multiple stores per merchant and team member seeding
Add 4 new stores (WizaGadgets, WizaHome, Fashion Outlet, BookWorld Digital) and 5 team member users with store assignments. Fix FK ordering in reset_all_data to delete Products before MarketplaceProducts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -144,6 +144,24 @@ DEMO_STORES = [
|
||||
"theme_preset": "modern",
|
||||
"custom_domain": "wizamart.shop",
|
||||
},
|
||||
{
|
||||
"merchant_index": 0, # WizaCorp
|
||||
"store_code": "WIZAGADGETS",
|
||||
"name": "WizaGadgets",
|
||||
"subdomain": "wizagadgets",
|
||||
"description": "Smart home devices and IoT accessories",
|
||||
"theme_preset": "modern",
|
||||
"custom_domain": None,
|
||||
},
|
||||
{
|
||||
"merchant_index": 0, # WizaCorp
|
||||
"store_code": "WIZAHOME",
|
||||
"name": "WizaHome",
|
||||
"subdomain": "wizahome",
|
||||
"description": "Home appliances and kitchen electronics",
|
||||
"theme_preset": "classic",
|
||||
"custom_domain": None,
|
||||
},
|
||||
{
|
||||
"merchant_index": 1, # Fashion Group
|
||||
"store_code": "FASHIONHUB",
|
||||
@@ -153,6 +171,15 @@ DEMO_STORES = [
|
||||
"theme_preset": "vibrant",
|
||||
"custom_domain": "fashionhub.store",
|
||||
},
|
||||
{
|
||||
"merchant_index": 1, # Fashion Group
|
||||
"store_code": "FASHIONOUTLET",
|
||||
"name": "Fashion Outlet",
|
||||
"subdomain": "fashionoutlet",
|
||||
"description": "Discounted designer fashion and seasonal clearance",
|
||||
"theme_preset": "vibrant",
|
||||
"custom_domain": None,
|
||||
},
|
||||
{
|
||||
"merchant_index": 2, # BookWorld
|
||||
"store_code": "BOOKSTORE",
|
||||
@@ -162,6 +189,67 @@ DEMO_STORES = [
|
||||
"theme_preset": "classic",
|
||||
"custom_domain": None,
|
||||
},
|
||||
{
|
||||
"merchant_index": 2, # BookWorld
|
||||
"store_code": "BOOKDIGITAL",
|
||||
"name": "BookWorld Digital",
|
||||
"subdomain": "bookdigital",
|
||||
"description": "E-books, audiobooks, and digital learning resources",
|
||||
"theme_preset": "modern",
|
||||
"custom_domain": None,
|
||||
},
|
||||
]
|
||||
|
||||
# Demo team members (linked to merchants by index, assigned to stores by store_code)
|
||||
DEMO_TEAM_MEMBERS = [
|
||||
# WizaCorp team
|
||||
{
|
||||
"merchant_index": 0,
|
||||
"email": "alice.manager@wizacorp.com",
|
||||
"password": "password123",
|
||||
"first_name": "Alice",
|
||||
"last_name": "Manager",
|
||||
"store_codes": ["WIZAMART", "WIZAGADGETS"], # manages two stores
|
||||
"user_type": "member",
|
||||
},
|
||||
{
|
||||
"merchant_index": 0,
|
||||
"email": "charlie.staff@wizacorp.com",
|
||||
"password": "password123",
|
||||
"first_name": "Charlie",
|
||||
"last_name": "Staff",
|
||||
"store_codes": ["WIZAHOME"],
|
||||
"user_type": "member",
|
||||
},
|
||||
# Fashion Group team
|
||||
{
|
||||
"merchant_index": 1,
|
||||
"email": "diana.stylist@fashiongroup.com",
|
||||
"password": "password123",
|
||||
"first_name": "Diana",
|
||||
"last_name": "Stylist",
|
||||
"store_codes": ["FASHIONHUB", "FASHIONOUTLET"],
|
||||
"user_type": "member",
|
||||
},
|
||||
{
|
||||
"merchant_index": 1,
|
||||
"email": "eric.sales@fashiongroup.com",
|
||||
"password": "password123",
|
||||
"first_name": "Eric",
|
||||
"last_name": "Sales",
|
||||
"store_codes": ["FASHIONOUTLET"],
|
||||
"user_type": "member",
|
||||
},
|
||||
# BookWorld team
|
||||
{
|
||||
"merchant_index": 2,
|
||||
"email": "fiona.editor@bookworld.com",
|
||||
"password": "password123",
|
||||
"first_name": "Fiona",
|
||||
"last_name": "Editor",
|
||||
"store_codes": ["BOOKSTORE", "BOOKDIGITAL"],
|
||||
"user_type": "member",
|
||||
},
|
||||
]
|
||||
|
||||
# Theme presets
|
||||
@@ -458,8 +546,9 @@ def reset_all_data(db: Session):
|
||||
CustomerAddress,
|
||||
Customer,
|
||||
MarketplaceImportJob,
|
||||
Product, # Product references MarketplaceProduct
|
||||
MarketplaceProductTranslation, # Translation references MarketplaceProduct
|
||||
MarketplaceProduct,
|
||||
Product,
|
||||
ContentPage, # Delete store content pages (keep platform defaults)
|
||||
StoreDomain,
|
||||
StoreTheme,
|
||||
@@ -662,6 +751,79 @@ def create_demo_stores(
|
||||
return stores
|
||||
|
||||
|
||||
def create_demo_team_members(
|
||||
db: Session, stores: list[Store], auth_manager: AuthManager
|
||||
) -> list[User]:
|
||||
"""Create demo team member users and assign them to stores."""
|
||||
|
||||
if SEED_MODE == "minimal":
|
||||
return []
|
||||
|
||||
team_users = []
|
||||
# Build a store_code → Store lookup from the created stores
|
||||
store_lookup = {s.store_code: s for s in stores}
|
||||
|
||||
for member_data in DEMO_TEAM_MEMBERS:
|
||||
# Check if user already exists
|
||||
user = db.execute(
|
||||
select(User).where(User.email == member_data["email"])
|
||||
).scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
user = User(
|
||||
username=member_data["email"].split("@")[0],
|
||||
email=member_data["email"],
|
||||
hashed_password=auth_manager.hash_password(member_data["password"]),
|
||||
role="store",
|
||||
first_name=member_data["first_name"],
|
||||
last_name=member_data["last_name"],
|
||||
is_active=True,
|
||||
is_email_verified=True,
|
||||
created_at=datetime.now(UTC),
|
||||
updated_at=datetime.now(UTC),
|
||||
)
|
||||
db.add(user)
|
||||
db.flush()
|
||||
print_success(
|
||||
f"Created team member: {user.email} (password: {member_data['password']})"
|
||||
)
|
||||
else:
|
||||
print_warning(f"Team member already exists: {user.email}")
|
||||
|
||||
team_users.append(user)
|
||||
|
||||
# Assign user to stores
|
||||
for store_code in member_data["store_codes"]:
|
||||
store = store_lookup.get(store_code)
|
||||
if not store:
|
||||
print_warning(f"Store {store_code} not found, skipping assignment")
|
||||
continue
|
||||
|
||||
# Check if StoreUser link already exists
|
||||
existing_link = db.execute(
|
||||
select(StoreUser).where(
|
||||
StoreUser.store_id == store.id,
|
||||
StoreUser.user_id == user.id,
|
||||
)
|
||||
).scalar_one_or_none()
|
||||
|
||||
if existing_link:
|
||||
continue
|
||||
|
||||
store_user = StoreUser(
|
||||
store_id=store.id,
|
||||
user_id=user.id,
|
||||
user_type=member_data["user_type"],
|
||||
is_active=True,
|
||||
created_at=datetime.now(UTC),
|
||||
)
|
||||
db.add(store_user)
|
||||
print_success(f" Assigned {user.first_name} to {store.name} as {member_data['user_type']}")
|
||||
|
||||
db.flush()
|
||||
return team_users
|
||||
|
||||
|
||||
def create_demo_customers(
|
||||
db: Session, store: Store, auth_manager: AuthManager, count: int
|
||||
) -> list[Customer]:
|
||||
@@ -905,20 +1067,24 @@ def seed_demo_data(db: Session, auth_manager: AuthManager):
|
||||
print_step(5, "Creating demo stores...")
|
||||
stores = create_demo_stores(db, merchants, auth_manager)
|
||||
|
||||
# Step 6: Create customers
|
||||
print_step(6, "Creating demo customers...")
|
||||
# Step 6: Create team members
|
||||
print_step(6, "Creating demo team members...")
|
||||
create_demo_team_members(db, stores, auth_manager)
|
||||
|
||||
# Step 7: Create customers
|
||||
print_step(7, "Creating demo customers...")
|
||||
for store in stores:
|
||||
create_demo_customers(
|
||||
db, store, auth_manager, count=settings.seed_customers_per_store
|
||||
)
|
||||
|
||||
# Step 7: Create products
|
||||
print_step(7, "Creating demo products...")
|
||||
# Step 8: Create products
|
||||
print_step(8, "Creating demo products...")
|
||||
for store in stores:
|
||||
create_demo_products(db, store, count=settings.seed_products_per_store)
|
||||
|
||||
# Step 8: Create store content pages
|
||||
print_step(8, "Creating store content page overrides...")
|
||||
# Step 9: Create store content pages
|
||||
print_step(9, "Creating store content page overrides...")
|
||||
create_demo_store_content_pages(db, stores)
|
||||
|
||||
# Commit all changes
|
||||
@@ -935,18 +1101,20 @@ def print_summary(db: Session):
|
||||
merchant_count = db.query(Merchant).count()
|
||||
store_count = db.query(Store).count()
|
||||
user_count = db.query(User).count()
|
||||
team_member_count = db.query(StoreUser).filter(StoreUser.user_type == "member").count()
|
||||
customer_count = db.query(Customer).count()
|
||||
product_count = db.query(Product).count()
|
||||
platform_pages = db.query(ContentPage).filter(ContentPage.store_id == None).count()
|
||||
store_pages = db.query(ContentPage).filter(ContentPage.store_id != None).count()
|
||||
|
||||
print("\n📊 Database Status:")
|
||||
print(f" Merchants: {merchant_count}")
|
||||
print(f" Stores: {store_count}")
|
||||
print(f" Users: {user_count}")
|
||||
print(f" Customers: {customer_count}")
|
||||
print(f" Products: {product_count}")
|
||||
print(f" Content Pages: {platform_pages} platform + {store_pages} store overrides")
|
||||
print(f" Merchants: {merchant_count}")
|
||||
print(f" Stores: {store_count}")
|
||||
print(f" Users: {user_count}")
|
||||
print(f" Team memberships: {team_member_count}")
|
||||
print(f" Customers: {customer_count}")
|
||||
print(f" Products: {product_count}")
|
||||
print(f" Content Pages: {platform_pages} platform + {store_pages} store overrides")
|
||||
|
||||
# Show merchant details
|
||||
merchants = db.query(Merchant).all()
|
||||
@@ -959,7 +1127,7 @@ def print_summary(db: Session):
|
||||
if merchant.is_verified:
|
||||
print(" Verified: ✓")
|
||||
|
||||
# Show store details
|
||||
# Show store details with team members
|
||||
stores = db.query(Store).all()
|
||||
print("\n🏪 Demo Stores:")
|
||||
for store in stores:
|
||||
@@ -985,6 +1153,18 @@ def print_summary(db: Session):
|
||||
|
||||
print(f" Status: {'✓ Active' if store.is_active else '✗ Inactive'}")
|
||||
|
||||
# Show team members for this store
|
||||
store_users = (
|
||||
db.query(StoreUser)
|
||||
.filter(StoreUser.store_id == store.id)
|
||||
.all()
|
||||
)
|
||||
if store_users:
|
||||
for su in store_users:
|
||||
user = db.query(User).filter(User.id == su.user_id).first()
|
||||
if user:
|
||||
print(f" Team: {user.email} ({su.user_type})")
|
||||
|
||||
print("\n🔐 Demo Merchant Owner Credentials:")
|
||||
print("─" * 70)
|
||||
for i, merchant_data in enumerate(DEMO_COMPANIES[:merchant_count], 1):
|
||||
@@ -1002,6 +1182,17 @@ def print_summary(db: Session):
|
||||
)
|
||||
print()
|
||||
|
||||
print("\n👥 Demo Team Member Credentials:")
|
||||
print("─" * 70)
|
||||
for member_data in DEMO_TEAM_MEMBERS:
|
||||
merchant_name = DEMO_COMPANIES[member_data["merchant_index"]]["name"]
|
||||
store_codes = ", ".join(member_data["store_codes"])
|
||||
print(f" {member_data['first_name']} {member_data['last_name']} ({merchant_name})")
|
||||
print(f" Email: {member_data['email']}")
|
||||
print(f" Password: {member_data['password']}")
|
||||
print(f" Stores: {store_codes}")
|
||||
print()
|
||||
|
||||
print("\n🛒 Demo Customer Credentials:")
|
||||
print("─" * 70)
|
||||
print(" All customers:")
|
||||
|
||||
Reference in New Issue
Block a user