New enterprise feature

This commit is contained in:
Urtzi Alfaro
2025-11-30 09:12:40 +01:00
parent f9d0eec6ec
commit 972db02f6d
176 changed files with 19741 additions and 1361 deletions

View File

@@ -0,0 +1,271 @@
#!/usr/bin/env python3
"""
One-time data migration script to populate demo_session_id for existing virtual tenants.
This script fixes existing demo sessions created before the demo_session_id fix was implemented.
It links tenants to their sessions using DemoSession.virtual_tenant_id and session_metadata.child_tenant_ids.
Usage:
python3 scripts/fix_existing_demo_sessions.py
Requirements:
- Both demo_session and tenant services must be accessible
- Database credentials must be available via environment variables
"""
import asyncio
import asyncpg
import json
import os
import sys
from datetime import datetime
from typing import List, Dict, Any
from uuid import UUID
# Database connection URLs
DEMO_SESSION_DB_URL = os.getenv(
"DEMO_SESSION_DATABASE_URL",
"postgresql://demo_session_user:demo_password@localhost:5432/demo_session_db"
)
TENANT_DB_URL = os.getenv(
"TENANT_DATABASE_URL",
"postgresql://tenant_user:T0uJnXs0r4TUmxSQeQ2DuQGP6HU0LEba@localhost:5432/tenant_db"
)
async def get_all_demo_sessions(demo_session_conn) -> List[Dict[str, Any]]:
"""Fetch all demo sessions from demo_session database"""
query = """
SELECT
id,
session_id,
virtual_tenant_id,
demo_account_type,
session_metadata,
status,
created_at
FROM demo_sessions
WHERE status IN ('ready', 'active', 'partial')
ORDER BY created_at DESC
"""
rows = await demo_session_conn.fetch(query)
sessions = []
for row in rows:
sessions.append({
"id": row["id"],
"session_id": row["session_id"],
"virtual_tenant_id": row["virtual_tenant_id"],
"demo_account_type": row["demo_account_type"],
"session_metadata": row["session_metadata"],
"status": row["status"],
"created_at": row["created_at"]
})
return sessions
async def check_tenant_exists(tenant_conn, tenant_id: UUID) -> bool:
"""Check if a tenant exists in the tenant database"""
query = """
SELECT id FROM tenants WHERE id = $1 AND is_demo = true
"""
result = await tenant_conn.fetchrow(query, tenant_id)
return result is not None
async def update_tenant_session_id(tenant_conn, tenant_id: UUID, session_id: str):
"""Update a tenant's demo_session_id"""
query = """
UPDATE tenants
SET demo_session_id = $2
WHERE id = $1 AND is_demo = true
"""
await tenant_conn.execute(query, tenant_id, session_id)
async def get_tenant_session_id(tenant_conn, tenant_id: UUID) -> str:
"""Get the current demo_session_id for a tenant"""
query = """
SELECT demo_session_id FROM tenants WHERE id = $1 AND is_demo = true
"""
result = await tenant_conn.fetchrow(query, tenant_id)
return result["demo_session_id"] if result else None
async def migrate_demo_sessions():
"""Main migration function"""
print("=" * 80)
print("Demo Session Migration Script")
print("=" * 80)
print(f"Started at: {datetime.now()}")
print()
# Connect to both databases
print("Connecting to databases...")
demo_session_conn = await asyncpg.connect(DEMO_SESSION_DB_URL)
tenant_conn = await asyncpg.connect(TENANT_DB_URL)
print("✓ Connected to both databases")
print()
try:
# Fetch all demo sessions
print("Fetching demo sessions...")
sessions = await get_all_demo_sessions(demo_session_conn)
print(f"✓ Found {len(sessions)} demo sessions")
print()
# Statistics
stats = {
"sessions_processed": 0,
"tenants_updated": 0,
"tenants_already_set": 0,
"tenants_not_found": 0,
"errors": 0
}
# Process each session
for session in sessions:
session_id = session["session_id"]
virtual_tenant_id = session["virtual_tenant_id"]
demo_account_type = session["demo_account_type"]
session_metadata = session["session_metadata"] or {}
print(f"Processing session: {session_id}")
print(f" Type: {demo_account_type}")
print(f" Main tenant: {virtual_tenant_id}")
tenant_ids_to_update = [virtual_tenant_id]
# For enterprise sessions, also get child tenant IDs
if demo_account_type in ["enterprise_chain", "enterprise_parent"]:
child_tenant_ids = session_metadata.get("child_tenant_ids", [])
if child_tenant_ids:
# Convert string UUIDs to UUID objects
child_uuids = [UUID(tid) if isinstance(tid, str) else tid for tid in child_tenant_ids]
tenant_ids_to_update.extend(child_uuids)
print(f" Child tenants: {len(child_uuids)}")
# Update each tenant
session_tenants_updated = 0
for tenant_id in tenant_ids_to_update:
try:
# Check if tenant exists
exists = await check_tenant_exists(tenant_conn, tenant_id)
if not exists:
print(f" ⚠ Tenant {tenant_id} not found - skipping")
stats["tenants_not_found"] += 1
continue
# Check current session_id
current_session_id = await get_tenant_session_id(tenant_conn, tenant_id)
if current_session_id == session_id:
print(f" ✓ Tenant {tenant_id} already has session_id set")
stats["tenants_already_set"] += 1
continue
# Update the tenant
await update_tenant_session_id(tenant_conn, tenant_id, session_id)
print(f" ✓ Updated tenant {tenant_id}")
stats["tenants_updated"] += 1
session_tenants_updated += 1
except Exception as e:
print(f" ✗ Error updating tenant {tenant_id}: {e}")
stats["errors"] += 1
stats["sessions_processed"] += 1
print(f" Session complete: {session_tenants_updated} tenant(s) updated")
print()
# Print summary
print("=" * 80)
print("Migration Complete!")
print("=" * 80)
print(f"Sessions processed: {stats['sessions_processed']}")
print(f"Tenants updated: {stats['tenants_updated']}")
print(f"Tenants already set: {stats['tenants_already_set']}")
print(f"Tenants not found: {stats['tenants_not_found']}")
print(f"Errors: {stats['errors']}")
print()
print(f"Finished at: {datetime.now()}")
print("=" * 80)
# Return success status
return stats["errors"] == 0
except Exception as e:
print(f"✗ Migration failed with error: {e}")
import traceback
traceback.print_exc()
return False
finally:
# Close connections
await demo_session_conn.close()
await tenant_conn.close()
print("Database connections closed")
async def verify_migration():
"""Verify that the migration was successful"""
print()
print("=" * 80)
print("Verification Check")
print("=" * 80)
tenant_conn = await asyncpg.connect(TENANT_DB_URL)
try:
# Count tenants without session_id
query = """
SELECT COUNT(*) as count
FROM tenants
WHERE is_demo = true AND demo_session_id IS NULL
"""
result = await tenant_conn.fetchrow(query)
null_count = result["count"]
if null_count == 0:
print("✓ All demo tenants have demo_session_id set")
else:
print(f"{null_count} demo tenant(s) still have NULL demo_session_id")
print(" These may be template tenants or orphaned records")
# Count tenants with session_id
query2 = """
SELECT COUNT(*) as count
FROM tenants
WHERE is_demo = true AND demo_session_id IS NOT NULL
"""
result2 = await tenant_conn.fetchrow(query2)
set_count = result2["count"]
print(f"{set_count} demo tenant(s) have demo_session_id set")
print("=" * 80)
print()
finally:
await tenant_conn.close()
if __name__ == "__main__":
# Run migration
success = asyncio.run(migrate_demo_sessions())
# Run verification
if success:
asyncio.run(verify_migration())
sys.exit(0)
else:
print("Migration failed - see errors above")
sys.exit(1)

View File

@@ -1,179 +0,0 @@
#!/bin/bash
#
# Master Demo Data Seeding Script
# Seeds all demo data for base template tenants
#
# This script executes all individual seed scripts in the correct order
# to populate the base demo template tenants with complete, realistic data
#
# Usage:
# ./scripts/seed_all_demo_data.sh [--skip-existing]
#
# Options:
# --skip-existing Skip seeding if data already exists (idempotent)
#
# Environment Variables Required:
# - DATABASE_URL or service-specific database URLs
# - DEMO_MODE=production (recommended for consistent seeding)
# - LOG_LEVEL=INFO (default)
#
set -e # Exit on error
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
SKIP_EXISTING=false
if [[ "$1" == "--skip-existing" ]]; then
SKIP_EXISTING=true
fi
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Demo Data Seeding - Bakery IA${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
echo -e "${YELLOW}⚠️ This script will seed demo data for:${NC}"
echo -e " - Panadería San Pablo (Individual Bakery)"
echo -e " - Panadería La Espiga (Central Workshop)"
echo ""
echo -e "${YELLOW}Execution Order:${NC}"
echo -e " 1. Auth: Users (enhanced with staff roles)"
echo -e " 2. Tenant: Tenant members (link staff to tenants)"
echo -e " 3. Inventory: Stock batches with expiration dates"
echo -e " 4. Orders: Customers"
echo -e " 5. Orders: Customer orders"
echo -e " 6. Suppliers: Supplier data"
echo -e " 7. Procurement: Procurement plans"
echo -e " 8. Procurement: Purchase orders"
echo -e " 9. Production: Equipment"
echo -e " 10. Production: Production schedules"
echo -e " 11. Production: Quality check templates"
echo -e " 12. Forecasting: Demand forecasts"
echo ""
# Prompt for confirmation
read -p "Continue? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo -e "${RED}Aborted.${NC}"
exit 1
fi
echo ""
echo -e "${GREEN}Starting demo data seeding...${NC}"
echo ""
# Function to run a seed script
run_seed() {
local service=$1
local script=$2
local description=$3
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${GREEN}${description}${NC}"
echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━${NC}"
local script_path="$PROJECT_ROOT/services/$service/scripts/demo/$script"
if [[ ! -f "$script_path" ]]; then
echo -e "${YELLOW}⚠ Script not found: $script_path${NC}"
echo -e "${YELLOW} Skipping...${NC}"
echo ""
return 0
fi
# Export PYTHONPATH
export PYTHONPATH="$PROJECT_ROOT:$PROJECT_ROOT/services/$service:$PYTHONPATH"
# Run the script
if python3 "$script_path"; then
echo -e "${GREEN}${description} - Completed${NC}"
else
echo -e "${RED}${description} - Failed${NC}"
echo -e "${RED} Check logs above for errors${NC}"
exit 1
fi
echo ""
}
# ============================================================================
# Phase 1: Users (Enhanced with Staff Roles)
# ============================================================================
run_seed "auth" "seed_demo_users.py" "Seeding demo users with staff roles"
# ============================================================================
# Phase 2: Tenant Members (Link Staff to Tenants)
# ============================================================================
run_seed "tenant" "seed_demo_tenant_members.py" "Linking staff users to tenants"
# ============================================================================
# Phase 3: Inventory Stock
# ============================================================================
run_seed "inventory" "seed_demo_stock.py" "Seeding inventory stock batches"
# ============================================================================
# Phase 4: Customers & Orders
# ============================================================================
run_seed "orders" "seed_demo_customers.py" "Seeding customer data"
run_seed "orders" "seed_demo_orders.py" "Seeding customer orders"
# ============================================================================
# Phase 5: Procurement (New Architecture)
# ============================================================================
run_seed "procurement" "seed_demo_suppliers.py" "Seeding supplier data"
run_seed "procurement" "seed_demo_procurement_plans.py" "Seeding procurement plans"
run_seed "procurement" "seed_demo_purchase_orders.py" "Seeding purchase orders"
# ============================================================================
# Phase 6: Production Equipment & Schedules
# ============================================================================
run_seed "production" "seed_demo_equipment.py" "Seeding production equipment"
run_seed "production" "seed_demo_production_schedules.py" "Seeding production schedules"
# ============================================================================
# Phase 7: Quality Templates
# ============================================================================
run_seed "production" "seed_demo_quality_templates.py" "Seeding quality check templates"
# ============================================================================
# Phase 8: Forecasting
# ============================================================================
run_seed "forecasting" "seed_demo_forecasts.py" "Seeding demand forecasts"
# ============================================================================
# Phase 9: Orchestration Runs
# ============================================================================
run_seed "orchestrator" "seed_demo_orchestration_runs.py" "Seeding orchestration runs with reasoning"
# ============================================================================
# Summary
# ============================================================================
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}✅ Demo Data Seeding Completed${NC}"
echo -e "${GREEN}========================================${NC}"
echo ""
echo -e "${YELLOW}Next Steps:${NC}"
echo " 1. Verify data in base template tenants:"
echo " - San Pablo: a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6"
echo " - La Espiga: b2c3d4e5-f6a7-48b9-c0d1-e2f3a4b5c6d7"
echo ""
echo " 2. Test demo session creation:"
echo " curl -X POST http://localhost:8000/demo/sessions \\"
echo " -H 'Content-Type: application/json' \\"
echo " -d '{\"account_type\": \"individual_bakery\"}'"
echo ""
echo " 3. Verify alert generation works"
echo " 4. Check date offset calculations"
echo ""
echo -e "${GREEN}🎉 Demo environment ready for cloning!${NC}"
echo ""

297
scripts/validate_demo_seeding.sh Executable file
View File

@@ -0,0 +1,297 @@
#!/bin/bash
# validate_demo_seeding.sh
# Comprehensive smoke test for demo seeding validation
# Tests both Professional and Enterprise demo templates
set -e # Exit on error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Counters
TESTS_PASSED=0
TESTS_FAILED=0
TESTS_TOTAL=0
# Fixed Demo Tenant IDs
DEMO_TENANT_PROFESSIONAL="a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6"
DEMO_TENANT_ENTERPRISE_PARENT="c3d4e5f6-a7b8-49c0-d1e2-f3a4b5c6d7e8"
DEMO_TENANT_CHILD_1="d4e5f6a7-b8c9-40d1-e2f3-a4b5c6d7e8f9"
DEMO_TENANT_CHILD_2="e5f6a7b8-c9d0-41e2-f3a4-b5c6d7e8f9a0"
DEMO_TENANT_CHILD_3="f6a7b8c9-d0e1-42f3-a4b5-c6d7e8f9a0b1"
# Database connection strings (from Kubernetes secrets)
get_db_url() {
local service=$1
kubectl get secret database-secrets -n bakery-ia -o jsonpath="{.data.${service}_DATABASE_URL}" | base64 -d
}
# Test helper functions
test_start() {
TESTS_TOTAL=$((TESTS_TOTAL + 1))
echo -e "${BLUE}[TEST $TESTS_TOTAL]${NC} $1"
}
test_pass() {
TESTS_PASSED=$((TESTS_PASSED + 1))
echo -e " ${GREEN}✓ PASS${NC}: $1"
}
test_fail() {
TESTS_FAILED=$((TESTS_FAILED + 1))
echo -e " ${RED}✗ FAIL${NC}: $1"
}
test_warn() {
echo -e " ${YELLOW}⚠ WARN${NC}: $1"
}
# SQL query helper
query_db() {
local db_url=$1
local query=$2
kubectl run psql-temp-$RANDOM --rm -i --restart=Never --image=postgres:17-alpine -- \
psql "$db_url" -t -c "$query" 2>/dev/null | xargs
}
echo "========================================"
echo "🧪 Demo Seeding Validation Test Suite"
echo "========================================"
echo ""
echo "Testing Professional and Enterprise demo templates..."
echo ""
# =============================================================================
# PHASE 1: PROFESSIONAL TIER VALIDATION
# =============================================================================
echo "========================================"
echo "📦 Phase 1: Professional Tier (Single Bakery)"
echo "========================================"
echo ""
# Test 1: Tenant Service - Professional tenant exists
test_start "Professional tenant exists in tenant service"
TENANT_DB=$(get_db_url "TENANT")
TENANT_COUNT=$(query_db "$TENANT_DB" "SELECT COUNT(*) FROM tenants WHERE id='$DEMO_TENANT_PROFESSIONAL' AND business_model='individual_bakery'")
if [ "$TENANT_COUNT" -eq 1 ]; then
test_pass "Professional tenant found (Panadería Artesana Madrid)"
else
test_fail "Professional tenant not found or incorrect count: $TENANT_COUNT"
fi
# Test 2: Inventory - Professional has raw ingredients
test_start "Professional tenant has raw ingredients"
INVENTORY_DB=$(get_db_url "INVENTORY")
INGREDIENT_COUNT=$(query_db "$INVENTORY_DB" "SELECT COUNT(*) FROM ingredients WHERE tenant_id='$DEMO_TENANT_PROFESSIONAL' AND product_type='INGREDIENT'")
if [ "$INGREDIENT_COUNT" -ge 20 ]; then
test_pass "Found $INGREDIENT_COUNT raw ingredients (expected ~24)"
else
test_fail "Insufficient raw ingredients: $INGREDIENT_COUNT (expected >=20)"
fi
# Test 3: Inventory - Professional has finished products
test_start "Professional tenant has finished products"
PRODUCT_COUNT=$(query_db "$INVENTORY_DB" "SELECT COUNT(*) FROM ingredients WHERE tenant_id='$DEMO_TENANT_PROFESSIONAL' AND product_type='FINISHED_PRODUCT'")
if [ "$PRODUCT_COUNT" -ge 4 ]; then
test_pass "Found $PRODUCT_COUNT finished products (expected ~4)"
else
test_fail "Insufficient finished products: $PRODUCT_COUNT (expected >=4)"
fi
# Test 4: Recipes - Professional has recipes
test_start "Professional tenant has recipes"
RECIPES_DB=$(get_db_url "RECIPES")
RECIPE_COUNT=$(query_db "$RECIPES_DB" "SELECT COUNT(*) FROM recipes WHERE tenant_id='$DEMO_TENANT_PROFESSIONAL'")
if [ "$RECIPE_COUNT" -ge 4 ]; then
test_pass "Found $RECIPE_COUNT recipes (expected ~4-20)"
else
test_fail "Insufficient recipes: $RECIPE_COUNT (expected >=4)"
fi
# Test 5: Sales - Professional has sales history
test_start "Professional tenant has sales history"
SALES_DB=$(get_db_url "SALES")
SALES_COUNT=$(query_db "$SALES_DB" "SELECT COUNT(*) FROM sales_data WHERE tenant_id='$DEMO_TENANT_PROFESSIONAL'")
if [ "$SALES_COUNT" -ge 100 ]; then
test_pass "Found $SALES_COUNT sales records (expected ~360 for 90 days)"
else
test_warn "Lower than expected sales records: $SALES_COUNT (expected >=100)"
fi
# =============================================================================
# PHASE 2: ENTERPRISE PARENT VALIDATION
# =============================================================================
echo ""
echo "========================================"
echo "🏭 Phase 2: Enterprise Parent (Obrador)"
echo "========================================"
echo ""
# Test 6: Tenant Service - Enterprise parent exists
test_start "Enterprise parent tenant exists"
PARENT_COUNT=$(query_db "$TENANT_DB" "SELECT COUNT(*) FROM tenants WHERE id='$DEMO_TENANT_ENTERPRISE_PARENT' AND business_model='enterprise_chain'")
if [ "$PARENT_COUNT" -eq 1 ]; then
test_pass "Enterprise parent found (Obrador Madrid)"
else
test_fail "Enterprise parent not found or incorrect count: $PARENT_COUNT"
fi
# Test 7: Inventory - Parent has raw ingredients (scaled 10x)
test_start "Enterprise parent has raw ingredients"
PARENT_INGREDIENT_COUNT=$(query_db "$INVENTORY_DB" "SELECT COUNT(*) FROM ingredients WHERE tenant_id='$DEMO_TENANT_ENTERPRISE_PARENT' AND product_type='INGREDIENT'")
if [ "$PARENT_INGREDIENT_COUNT" -ge 20 ]; then
test_pass "Found $PARENT_INGREDIENT_COUNT raw ingredients (expected ~24)"
else
test_fail "Insufficient parent raw ingredients: $PARENT_INGREDIENT_COUNT (expected >=20)"
fi
# Test 8: Recipes - Parent has recipes
test_start "Enterprise parent has recipes"
PARENT_RECIPE_COUNT=$(query_db "$RECIPES_DB" "SELECT COUNT(*) FROM recipes WHERE tenant_id='$DEMO_TENANT_ENTERPRISE_PARENT'")
if [ "$PARENT_RECIPE_COUNT" -ge 4 ]; then
test_pass "Found $PARENT_RECIPE_COUNT recipes (expected ~4-20)"
else
test_fail "Insufficient parent recipes: $PARENT_RECIPE_COUNT (expected >=4)"
fi
# Test 9: Production - Parent has production batches
test_start "Enterprise parent has production batches"
PRODUCTION_DB=$(get_db_url "PRODUCTION")
BATCH_COUNT=$(query_db "$PRODUCTION_DB" "SELECT COUNT(*) FROM production_batches WHERE tenant_id='$DEMO_TENANT_ENTERPRISE_PARENT'")
if [ "$BATCH_COUNT" -ge 50 ]; then
test_pass "Found $BATCH_COUNT production batches (expected ~120)"
elif [ "$BATCH_COUNT" -ge 20 ]; then
test_warn "Lower production batches: $BATCH_COUNT (expected ~120)"
else
test_fail "Insufficient production batches: $BATCH_COUNT (expected >=50)"
fi
# =============================================================================
# PHASE 3: CHILD RETAIL OUTLETS VALIDATION
# =============================================================================
echo ""
echo "========================================"
echo "🏪 Phase 3: Child Retail Outlets"
echo "========================================"
echo ""
# Test each child tenant
for CHILD_ID in "$DEMO_TENANT_CHILD_1" "$DEMO_TENANT_CHILD_2" "$DEMO_TENANT_CHILD_3"; do
case "$CHILD_ID" in
"$DEMO_TENANT_CHILD_1") CHILD_NAME="Madrid Centro" ;;
"$DEMO_TENANT_CHILD_2") CHILD_NAME="Barcelona Gràcia" ;;
"$DEMO_TENANT_CHILD_3") CHILD_NAME="Valencia Ruzafa" ;;
esac
echo ""
echo "Testing: $CHILD_NAME"
echo "----------------------------------------"
# Test 10a: Child has finished products ONLY (no raw ingredients)
test_start "[$CHILD_NAME] Has finished products ONLY"
CHILD_PRODUCTS=$(query_db "$INVENTORY_DB" "SELECT COUNT(*) FROM ingredients WHERE tenant_id='$CHILD_ID' AND product_type='FINISHED_PRODUCT'")
CHILD_RAW=$(query_db "$INVENTORY_DB" "SELECT COUNT(*) FROM ingredients WHERE tenant_id='$CHILD_ID' AND product_type='INGREDIENT'")
if [ "$CHILD_PRODUCTS" -eq 4 ] && [ "$CHILD_RAW" -eq 0 ]; then
test_pass "Found $CHILD_PRODUCTS finished products, 0 raw ingredients (correct retail model)"
elif [ "$CHILD_RAW" -gt 0 ]; then
test_fail "Child has raw ingredients ($CHILD_RAW) - should only have finished products"
else
test_warn "Product count mismatch: $CHILD_PRODUCTS (expected 4)"
fi
# Test 10b: Child has stock batches
test_start "[$CHILD_NAME] Has stock batches"
CHILD_STOCK=$(query_db "$INVENTORY_DB" "SELECT COUNT(*) FROM stock WHERE tenant_id='$CHILD_ID'")
if [ "$CHILD_STOCK" -ge 10 ]; then
test_pass "Found $CHILD_STOCK stock batches (expected ~16)"
else
test_warn "Lower stock batches: $CHILD_STOCK (expected ~16)"
fi
# Test 10c: Child has sales history
test_start "[$CHILD_NAME] Has sales history"
CHILD_SALES=$(query_db "$SALES_DB" "SELECT COUNT(*) FROM sales_data WHERE tenant_id='$CHILD_ID'")
if [ "$CHILD_SALES" -ge 80 ]; then
test_pass "Found $CHILD_SALES sales records (expected ~120 for 30 days)"
else
test_warn "Lower sales records: $CHILD_SALES (expected ~120)"
fi
# Test 10d: Child has customers
test_start "[$CHILD_NAME] Has walk-in customers"
ORDERS_DB=$(get_db_url "ORDERS")
CHILD_CUSTOMERS=$(query_db "$ORDERS_DB" "SELECT COUNT(*) FROM customers WHERE tenant_id='$CHILD_ID'")
if [ "$CHILD_CUSTOMERS" -ge 40 ]; then
test_pass "Found $CHILD_CUSTOMERS customers (expected 60-100)"
else
test_warn "Lower customer count: $CHILD_CUSTOMERS (expected 60-100)"
fi
done
# =============================================================================
# PHASE 4: DISTRIBUTION VALIDATION
# =============================================================================
echo ""
echo "========================================"
echo "🚚 Phase 4: Distribution & Logistics"
echo "========================================"
echo ""
# Test 11: Distribution routes exist
test_start "Distribution routes created (Mon/Wed/Fri pattern)"
DISTRIBUTION_DB=$(get_db_url "DISTRIBUTION")
ROUTE_COUNT=$(query_db "$DISTRIBUTION_DB" "SELECT COUNT(*) FROM delivery_routes WHERE tenant_id='$DEMO_TENANT_ENTERPRISE_PARENT'")
if [ "$ROUTE_COUNT" -ge 10 ]; then
test_pass "Found $ROUTE_COUNT delivery routes (expected ~13 for 30 days, Mon/Wed/Fri)"
else
test_warn "Lower route count: $ROUTE_COUNT (expected ~13)"
fi
# Test 12: Shipments exist for all children
test_start "Shipments created for all retail outlets"
SHIPMENT_COUNT=$(query_db "$DISTRIBUTION_DB" "SELECT COUNT(*) FROM shipments WHERE parent_tenant_id='$DEMO_TENANT_ENTERPRISE_PARENT'")
if [ "$SHIPMENT_COUNT" -ge 30 ]; then
test_pass "Found $SHIPMENT_COUNT shipments (expected ~39: 13 routes × 3 children)"
else
test_warn "Lower shipment count: $SHIPMENT_COUNT (expected ~39)"
fi
# =============================================================================
# SUMMARY
# =============================================================================
echo ""
echo "========================================"
echo "📊 Test Summary"
echo "========================================"
echo ""
echo "Total Tests: $TESTS_TOTAL"
echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
echo -e "${RED}Failed: $TESTS_FAILED${NC}"
echo ""
if [ $TESTS_FAILED -eq 0 ]; then
echo -e "${GREEN}✅ ALL TESTS PASSED!${NC}"
echo ""
echo "Demo templates are ready for cloning:"
echo " ✓ Professional tier (single bakery): ~3,500 records"
echo " ✓ Enterprise parent (Obrador): ~3,000 records"
echo " ✓ 3 Child retail outlets: ~700 records"
echo " ✓ Distribution history: ~52 records"
echo " ✓ Total template data: ~4,200-4,800 records"
echo ""
exit 0
else
echo -e "${RED}❌ SOME TESTS FAILED${NC}"
echo ""
echo "Please review the failed tests above and:"
echo " 1. Check that all seed jobs completed successfully"
echo " 2. Verify database connections"
echo " 3. Check seed script logs for errors"
echo ""
exit 1
fi