cuatrolabs-pos-ms / scripts /seed_data.py
MukeshKapoor25's picture
docs: Update terminology from "salon" to "retail" for consistency
17b18e9
"""
Seed data script for POS Microservice.
Populates MongoDB with sample catalogue services, staff, and customers.
Usage:
python scripts/seed_data.py
Or with custom merchant_id:
python scripts/seed_data.py --merchant-id <uuid>
"""
import asyncio
import argparse
import sys
from pathlib import Path
from uuid import uuid4
from datetime import datetime
# Add parent directory to path to import app modules
sys.path.insert(0, str(Path(__file__).parent.parent))
from app.nosql import connect_to_mongo, close_mongo_connection, get_database
from app.core.config import settings
# Use standard UUID for consistency across services
MERCHANT_ID = "01234567-89ab-cdef-0123-456789abcdef" # Cuatro Beauty Ltd UUID
# Sample Services
SAMPLE_SERVICES = [
{
"name": "Hair Cut – Women",
"code": "HC-WOMEN-45",
"category_id": "hair",
"category_name": "Hair",
"description": "Includes wash, cut and blow-dry",
"duration_mins": 45,
"price": 700,
"gst_rate": 18,
},
{
"name": "Hair Cut – Men",
"code": "HC-MEN-30",
"category_id": "hair",
"category_name": "Hair",
"description": "Standard men's haircut",
"duration_mins": 30,
"price": 400,
"gst_rate": 18,
},
{
"name": "Hair Color – Full",
"code": "COL-FULL-120",
"category_id": "hair",
"category_name": "Hair",
"description": "Complete hair coloring with premium products",
"duration_mins": 120,
"price": 3500,
"gst_rate": 18,
},
{
"name": "Hair Spa Treatment",
"code": "SPA-HAIR-60",
"category_id": "spa",
"category_name": "Spa",
"description": "Deep conditioning and scalp treatment",
"duration_mins": 60,
"price": 1500,
"gst_rate": 18,
},
{
"name": "Facial – Classic",
"code": "FACE-CLS-45",
"category_id": "facial",
"category_name": "Facial",
"description": "Deep cleansing facial with mask",
"duration_mins": 45,
"price": 1200,
"gst_rate": 18,
},
{
"name": "Manicure",
"code": "MANI-30",
"category_id": "nails",
"category_name": "Nails",
"description": "Classic manicure with nail polish",
"duration_mins": 30,
"price": 500,
"gst_rate": 18,
},
{
"name": "Pedicure",
"code": "PEDI-45",
"category_id": "nails",
"category_name": "Nails",
"description": "Classic pedicure with foot massage",
"duration_mins": 45,
"price": 700,
"gst_rate": 18,
},
{
"name": "Blow Dry & Styling",
"code": "STYLE-30",
"category_id": "hair",
"category_name": "Hair",
"description": "Professional blow dry and styling",
"duration_mins": 30,
"price": 600,
"gst_rate": 18,
},
{
"name": "Makeup – Party",
"code": "MKP-PARTY-60",
"category_id": "makeup",
"category_name": "Makeup",
"description": "Party makeup with false lashes",
"duration_mins": 60,
"price": 2500,
"gst_rate": 18,
},
{
"name": "Waxing – Full Arms",
"code": "WAX-ARMS-20",
"category_id": "waxing",
"category_name": "Waxing",
"description": "Full arms waxing",
"duration_mins": 20,
"price": 400,
"gst_rate": 18,
},
]
# Sample Staff
SAMPLE_STAFF = [
{
"name": "Priya Sharma",
"role": "Senior Stylist",
"staff_code": "STF001",
"phone": "+91-9876543210",
"email": "priya.sharma@retail.com",
"skills": ["Haircut", "Color", "Styling", "Hair Spa"],
"working_hours": [
{"day": "Mon", "from": "10:00", "to": "19:00"},
{"day": "Tue", "from": "10:00", "to": "19:00"},
{"day": "Wed", "from": "10:00", "to": "19:00"},
{"day": "Thu", "from": "10:00", "to": "19:00"},
{"day": "Fri", "from": "10:00", "to": "19:00"},
{"day": "Sat", "from": "11:00", "to": "18:00"},
],
},
{
"name": "Rajesh Kumar",
"role": "Stylist",
"staff_code": "STF002",
"phone": "+91-9876543211",
"email": "rajesh.kumar@retail.com",
"skills": ["Haircut", "Styling", "Beard Trim"],
"working_hours": [
{"day": "Mon", "from": "10:00", "to": "19:00"},
{"day": "Tue", "from": "10:00", "to": "19:00"},
{"day": "Wed", "from": "10:00", "to": "19:00"},
{"day": "Thu", "from": "10:00", "to": "19:00"},
{"day": "Fri", "from": "10:00", "to": "19:00"},
],
},
{
"name": "Anjali Verma",
"role": "Makeup Artist",
"staff_code": "STF003",
"phone": "+91-9876543212",
"email": "anjali.verma@retail.com",
"skills": ["Makeup", "Facial", "Bridal Makeup"],
"working_hours": [
{"day": "Tue", "from": "11:00", "to": "20:00"},
{"day": "Wed", "from": "11:00", "to": "20:00"},
{"day": "Thu", "from": "11:00", "to": "20:00"},
{"day": "Fri", "from": "11:00", "to": "20:00"},
{"day": "Sat", "from": "10:00", "to": "19:00"},
{"day": "Sun", "from": "10:00", "to": "17:00"},
],
},
{
"name": "Meera Patel",
"role": "Nail Technician",
"staff_code": "STF004",
"phone": "+91-9876543213",
"email": "meera.patel@retail.com",
"skills": ["Manicure", "Pedicure", "Nail Art"],
"working_hours": [
{"day": "Mon", "from": "10:00", "to": "19:00"},
{"day": "Tue", "from": "10:00", "to": "19:00"},
{"day": "Wed", "from": "10:00", "to": "19:00"},
{"day": "Thu", "from": "10:00", "to": "19:00"},
{"day": "Fri", "from": "10:00", "to": "19:00"},
{"day": "Sat", "from": "11:00", "to": "18:00"},
],
},
]
# Sample Customers
SAMPLE_CUSTOMERS = [
{
"name": "Neha Kapoor",
"phone": "+91-9988776655",
"email": "neha.kapoor@example.com",
"notes": "Regular customer, prefers morning appointments",
},
{
"name": "Arjun Reddy",
"phone": "+91-9988776656",
"email": "arjun.reddy@example.com",
"notes": "Walk-in customer",
},
{
"name": "Kavya Nair",
"phone": "+91-9988776657",
"email": "kavya.nair@example.com",
"notes": "Prefers specific stylist - Priya",
},
]
async def seed_services(merchant_id: str):
"""Seed catalogue services."""
db = get_database()
if db is None:
print("❌ MongoDB not connected. Cannot seed data.")
return False
collection = db["catalogue_services"]
print("\nπŸ“‹ Seeding Catalogue Services...")
created_count = 0
skipped_count = 0
for service in SAMPLE_SERVICES:
# Check if service code already exists
existing = await collection.find_one({
"merchant_id": merchant_id,
"code": service["code"]
})
if existing:
print(f" ⏭️ Skipped: {service['name']} (code: {service['code']}) - already exists")
skipped_count += 1
continue
service_id = str(uuid4())
now = datetime.utcnow()
doc = {
"_id": service_id,
"merchant_id": merchant_id,
"name": service["name"],
"code": service["code"],
"category": {
"id": service["category_id"],
"name": service["category_name"]
},
"description": service["description"],
"duration_mins": service["duration_mins"],
"pricing": {
"price": service["price"],
"currency": "INR",
"gst_rate": service["gst_rate"]
},
"status": "active",
"sort_order": created_count,
"created_at": now,
"updated_at": now
}
await collection.insert_one(doc)
print(f" βœ… Created: {service['name']} (β‚Ή{service['price']}, {service['duration_mins']} mins)")
created_count += 1
print(f"\n πŸ“Š Services: {created_count} created, {skipped_count} skipped")
return True
async def seed_staff(merchant_id: str):
"""Seed staff members."""
db = get_database()
if db is None:
print("❌ MongoDB not connected. Cannot seed data.")
return False
collection = db["pos_staff"]
print("\nπŸ‘₯ Seeding Staff...")
created_count = 0
skipped_count = 0
for staff in SAMPLE_STAFF:
# Check if staff code already exists
existing = await collection.find_one({
"merchant_id": merchant_id,
"staff_code": staff["staff_code"]
})
if existing:
print(f" ⏭️ Skipped: {staff['name']} ({staff['staff_code']}) - already exists")
skipped_count += 1
continue
staff_id = f"staff_{uuid4().hex[:16]}"
now = datetime.utcnow()
doc = {
"staff_id": staff_id,
"merchant_id": merchant_id,
"name": staff["name"],
"role": staff["role"],
"staff_code": staff["staff_code"],
"contact": {
"phone": staff["phone"],
"email": staff["email"]
},
"skills": staff["skills"],
"status": "active",
"working_hours": staff["working_hours"],
"created_at": now,
"updated_at": now
}
await collection.insert_one(doc)
print(f" βœ… Created: {staff['name']} - {staff['role']}")
created_count += 1
print(f"\n πŸ“Š Staff: {created_count} created, {skipped_count} skipped")
return True
async def seed_customers(merchant_id: str):
"""Seed sample customers."""
db = get_database()
if db is None:
print("❌ MongoDB not connected. Cannot seed data.")
return False
collection = db["pos_customers"]
print("\nπŸ‘€ Seeding Customers...")
created_count = 0
skipped_count = 0
for customer in SAMPLE_CUSTOMERS:
# Check if customer phone already exists
existing = await collection.find_one({
"merchant_id": merchant_id,
"phone": customer["phone"]
})
if existing:
print(f" ⏭️ Skipped: {customer['name']} ({customer['phone']}) - already exists")
skipped_count += 1
continue
customer_id = str(uuid4())
now = datetime.utcnow()
doc = {
"customer_id": customer_id,
"merchant_id": merchant_id,
"name": customer["name"],
"phone": customer["phone"],
"email": customer["email"],
"notes": customer["notes"],
"created_at": now,
"updated_at": now
}
await collection.insert_one(doc)
print(f" βœ… Created: {customer['name']} ({customer['phone']})")
created_count += 1
print(f"\n πŸ“Š Customers: {created_count} created, {skipped_count} skipped")
return True
async def main(merchant_id: str):
"""Main seed function."""
print("="*60)
print("🌱 POS Microservice - Seed Data Script")
print("="*60)
print(f"\nπŸ“ Merchant ID: {merchant_id}")
print(f"πŸ“ MongoDB URI: {settings.MONGODB_URI}")
print(f"πŸ“ Database: {settings.MONGODB_DB_NAME}")
# Connect to MongoDB
try:
await connect_to_mongo()
except Exception as e:
print(f"\n❌ Failed to connect to MongoDB: {e}")
print("\nπŸ’‘ Make sure MongoDB is running:")
print(" docker run -d --name mongo -p 27017:27017 mongo:6")
return
# Seed data
try:
success = True
success = await seed_services(merchant_id) and success
success = await seed_staff(merchant_id) and success
success = await seed_customers(merchant_id) and success
if success:
print("\n" + "="*60)
print("βœ… Seed data completed successfully!")
print("="*60)
print("\nπŸ’‘ You can now:")
print(" 1. Start the API: uvicorn app.main:app --reload --port 8282")
print(f" 2. List services: GET /api/v1/pos/catalogue/services?merchant_id={merchant_id}")
print(f" 3. Create appointments with the seeded services and staff")
else:
print("\n⚠️ Seed data completed with some issues")
except Exception as e:
print(f"\n❌ Error during seeding: {e}")
import traceback
traceback.print_exc()
finally:
await close_mongo_connection()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Seed POS microservice data")
parser.add_argument(
"--merchant-id",
type=str,
default=MERCHANT_ID,
help=f"Merchant UUID (default: {MERCHANT_ID})"
)
args = parser.parse_args()
asyncio.run(main(args.merchant_id))