# Enterprise Demo Fixes Summary **Date:** 2025-12-17 **Issue:** Child tenants not visible in multi-tenant menu & Distribution data not displaying ## Problems Identified ### 1. Child Tenant Visibility Issue ❌ **Root Cause:** Child tenants were being created with the wrong `owner_id`. **Location:** `services/tenant/app/api/internal_demo.py:620` **Problem Details:** - Child tenants were hardcoded to use the professional demo owner ID: `c1a2b3c4-d5e6-47a8-b9c0-d1e2f3a4b5c6` - This is INCORRECT for enterprise demos - The enterprise parent tenant uses owner ID: `d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7` - Because of the mismatch, when the enterprise parent owner logged in, they could only see the parent tenant - The child tenants belonged to a different owner and were not visible in the tenant switcher **Impact:** - Parent tenant owner could NOT see child tenants in the multi-tenant menu - Child tenants existed in the database but were inaccessible - Enterprise demo was non-functional for testing multi-location features ### 2. Distribution Data File Not Found for Child Tenants ❌ **Root Cause:** Distribution service was trying to load non-existent distribution files for child tenants. **Location:** `services/distribution/app/api/internal_demo.py:148` **Problem Details:** - When cloning data for `enterprise_child` tenants, the code tried to load: `shared/demo/fixtures/enterprise/children/C0000000-0000-4000-a000-000000000001/12-distribution.json` - These files don't exist because **child outlets are delivery destinations, not distribution hubs** - Distribution is managed centrally by the parent tenant - This caused the demo session cloning to fail with FileNotFoundError **Error Message:** ``` FileNotFoundError: Seed data file not found: /app/shared/demo/fixtures/enterprise/children/C0000000-0000-4000-a000-000000000001/12-distribution.json ``` **Impact:** - Demo session cloning failed for enterprise demos - Child tenant creation was incomplete - Distribution page showed no data ### 3. Shipment Model Field Mismatch ❌ **Root Cause:** Distribution cloning code tried to create Shipment with fields that don't exist in the model. **Location:** `services/distribution/app/api/internal_demo.py:283` **Problem Details:** - Fixture contains `items` field (list of products being shipped) - Fixture contains `estimated_delivery_time` field - Shipment model doesn't have these fields - Model only has: `actual_delivery_time`, `delivery_notes`, etc. - This caused TypeError when creating Shipment objects **Error Message:** ``` TypeError: 'items' is an invalid keyword argument for Shipment ``` **Impact:** - Distribution data cloning failed completely - No routes or shipments were created - Distribution page was empty even after successful child tenant creation ## Fixes Applied ### Fix 1: Child Tenant Owner ID Correction ✅ **File Modified:** `services/tenant/app/api/internal_demo.py` **Changes Made:** 1. **Added parent tenant lookup** (Lines 599-614): ```python # Get parent tenant to retrieve the correct owner_id parent_result = await db.execute(select(Tenant).where(Tenant.id == parent_uuid)) parent_tenant = parent_result.scalars().first() if not parent_tenant: logger.error("Parent tenant not found", parent_tenant_id=parent_tenant_id) return {...} # Use the parent's owner_id for the child tenant (enterprise demo owner) parent_owner_id = parent_tenant.owner_id ``` 2. **Updated child tenant creation** (Line 637): ```python # Owner ID - MUST match the parent tenant owner (enterprise demo owner) # This ensures the parent owner can see and access child tenants owner_id=parent_owner_id ``` 3. **Updated TenantMember creation** (Line 711): ```python # Use the parent's owner_id (already retrieved above) # This ensures consistency between tenant.owner_id and TenantMember records child_owner_member = TenantMember( tenant_id=virtual_uuid, user_id=parent_owner_id, # Changed from hardcoded UUID role="owner", ... ) ``` 4. **Enhanced logging** (Line 764): ```python logger.info( "Child outlet created successfully", ... owner_id=str(parent_owner_id), # Added for debugging ... ) ``` ### Fix 2: Distribution Data Loading for Child Tenants ✅ **File Modified:** `services/distribution/app/api/internal_demo.py` **Changes Made:** 1. **Added early return for child tenants** (Lines 147-166): ```python elif demo_account_type == "enterprise_child": # Child outlets don't have their own distribution data # Distribution is managed centrally by the parent tenant # Child locations are delivery destinations, not distribution hubs logger.info( "Skipping distribution cloning for child outlet - distribution managed by parent", base_tenant_id=base_tenant_id, virtual_tenant_id=virtual_tenant_id, session_id=session_id ) duration_ms = int((datetime.now(timezone.utc) - start_time).total_seconds() * 1000) return { "service": "distribution", "status": "completed", "records_cloned": 0, "duration_ms": duration_ms, "details": { "note": "Child outlets don't manage distribution - handled by parent tenant" } } ``` **Rationale:** - In an enterprise bakery setup, the **central production facility (parent)** manages all distribution - **Retail outlets (children)** are **receiving locations**, not distribution hubs - The parent's distribution.json already includes routes and shipments that reference child tenant locations - Attempting to load child-specific distribution files was architecturally incorrect ### Fix 3: Shipment Field Compatibility ✅ **File Modified:** `services/distribution/app/api/internal_demo.py` **Changes Made:** 1. **Removed estimated_delivery_time field** (Lines 261-267): ```python # Note: The Shipment model doesn't have estimated_delivery_time # Only actual_delivery_time is stored actual_delivery_time = parse_date_field( shipment_data.get('actual_delivery_time'), session_time, "actual_delivery_time" ) ``` 2. **Stored items in delivery_notes** (Lines 273-287): ```python # Store items in delivery_notes as JSON for demo purposes # (In production, items would be in the linked purchase order) import json items_json = json.dumps(shipment_data.get('items', [])) if shipment_data.get('items') else None new_shipment = Shipment( ... total_weight_kg=shipment_data.get('total_weight_kg'), actual_delivery_time=actual_delivery_time, # Store items info in delivery_notes for demo display delivery_notes=f"{shipment_data.get('notes', '')}\nItems: {items_json}" if items_json else shipment_data.get('notes'), ... ) ``` **Rationale:** - Shipment model represents delivery tracking, not content inventory - In production systems, shipment items are stored in the linked purchase order - For demo purposes, we store items as JSON in the `delivery_notes` field - This allows the demo to show what's being shipped without requiring full PO integration ## How Data Flows in Enterprise Demo ### User & Ownership Structure ``` Enterprise Demo Owner ├── ID: d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7 ├── Email: director@panaderiaartesana.es ├── Role: owner │ ├── Parent Tenant (Central Production) │ ├── ID: 80000000-0000-4000-a000-000000000001 (template) │ ├── Name: "Panadería Artesana España - Central" │ ├── Type: parent │ └── Owner: d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7 │ └── Child Tenants (Retail Outlets) ├── Madrid - Salamanca │ ├── ID: A0000000-0000-4000-a000-000000000001 (template) │ ├── Type: child │ └── Owner: d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7 ✅ (NOW CORRECT) │ ├── Barcelona - Eixample │ ├── ID: B0000000-0000-4000-a000-000000000001 │ └── Owner: d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7 ✅ │ ├── Valencia - Ruzafa │ ├── ID: C0000000-0000-4000-a000-000000000001 │ └── Owner: d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7 ✅ │ ├── Seville - Triana │ ├── ID: D0000000-0000-4000-a000-000000000001 │ └── Owner: d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7 ✅ │ └── Bilbao - Casco Viejo ├── ID: E0000000-0000-4000-a000-000000000001 └── Owner: d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7 ✅ ``` ### Tenant Loading Flow 1. **User logs into enterprise demo** - Demo session created with `demo_account_type: "enterprise"` - Session ID stored in JWT token 2. **Frontend requests user tenants** - Calls: `GET /tenants/user/{user_id}/owned` - Backend: `services/tenant/app/api/tenant_operations.py:284` 3. **Backend retrieves virtual tenants** - Extracts `demo_session_id` from JWT - Calls: `tenant_service.get_virtual_tenants_for_session(demo_session_id, "enterprise")` - Query: `SELECT * FROM tenants WHERE demo_session_id = ? AND owner_id = ?` - Returns: Parent + All child tenants with matching owner_id ✅ 4. **Frontend displays in TenantSwitcher** - Component: `frontend/src/components/ui/TenantSwitcher.tsx` - Shows all tenants where user is owner - Now includes all 6 tenants (1 parent + 5 children) ✅ ### Distribution Data Flow 1. **Demo session cloning** - Orchestrator calls distribution service: `POST /internal/demo/clone` - Loads fixture: `shared/demo/fixtures/enterprise/parent/12-distribution.json` 2. **Distribution data includes** - Delivery routes with route_sequence (stops at multiple locations) - Shipments linked to child tenants - All dates use BASE_TS markers for session-relative times 3. **Frontend queries distribution** - Calls: `GET /tenants/{tenant_id}/distribution/routes?date={date}` - Calls: `GET /tenants/{tenant_id}/distribution/shipments?date={date}` - Service: `frontend/src/api/hooks/useEnterpriseDashboard.ts:307` ## Testing Instructions ### 1. Restart Services After applying the fixes, you need to restart the affected services: ```bash # Restart tenant service (Fix 1: child tenant owner_id) kubectl rollout restart deployment tenant-service -n bakery-ia # Restart distribution service (Fix 2: skip child distribution loading) kubectl rollout restart deployment distribution-service -n bakery-ia # Or restart all services at once ./kubernetes_restart.sh ``` ### 2. Create New Enterprise Demo Session **Important:** You must create a NEW demo session to test the fix. Existing sessions have already created child tenants with the wrong owner_id. ```bash # Navigate to frontend cd frontend # Start development server if not running npm run dev # Open browser to demo page # http://localhost:3000/demo ``` ### 3. Test Child Tenant Visibility 1. Click "Try Enterprise Demo" button 2. Wait for demo session to initialize 3. After redirect to dashboard, look for the tenant switcher in the top-left 4. Click on the tenant switcher dropdown 5. **Expected Result:** You should see 6 organizations: - Panadería Artesana España - Central (parent) - Madrid - Salamanca (child) - Barcelona - Eixample (child) - Valencia - Ruzafa (child) - Seville - Triana (child) - Bilbao - Casco Viejo (child) ### 4. Test Distribution Page 1. From the enterprise dashboard, navigate to "Distribution" 2. Check if routes and shipments are displayed 3. **Expected Result:** You should see: - Active routes count - Pending deliveries count - Distribution map with route visualization - List of routes in the "Rutas" tab ### 5. Verify Database (Optional) If you have database access: ```sql -- Check child tenant owner_ids SELECT id, name, tenant_type, owner_id, demo_session_id FROM tenants WHERE tenant_type = 'child' AND is_demo = true ORDER BY created_at DESC LIMIT 10; -- Should show owner_id = 'd2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7' for all child tenants ``` ## Troubleshooting ### Child Tenants Still Not Visible 1. **Verify you created a NEW demo session** after deploying the fix - Old sessions have child tenants with wrong owner_id - Solution: Create a new demo session 2. **Check logs for child tenant creation** ```bash kubectl logs -f deployment/tenant-service -n bakery-ia | grep "Child outlet created" ``` - Should show: `owner_id=d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7` 3. **Verify demo session ID in JWT** - Open browser DevTools > Application > Storage > Local Storage - Check if `demo_session_id` is present in token - Should match the session_id in database ### Distribution Data Not Showing 1. **Check date parameter** - Distribution page defaults to today's date - Demo data uses BASE_TS (session creation time) - Routes might be scheduled for BASE_TS + 2h, BASE_TS + 3h, etc. - Solution: Try querying without date filter or use session date 2. **Verify distribution data was cloned** ```bash kubectl logs -f deployment/demo-session-service -n bakery-ia | grep "distribution" ``` - Should show: "Distribution data cloning completed" - Should show: records_cloned > 0 3. **Check backend endpoint** ```bash # Get tenant ID from tenant switcher TENANT_ID="your-virtual-tenant-id" # Query routes directly curl -H "Authorization: Bearer YOUR_TOKEN" \ "http://localhost:8000/tenants/${TENANT_ID}/distribution/routes" ``` 4. **Check browser console for errors** - Open DevTools > Console - Look for API errors or failed requests - Check Network tab for distribution API calls ## Files Changed 1. **services/tenant/app/api/internal_demo.py** - Lines 599-614: Added parent tenant lookup - Line 637: Fixed child tenant owner_id - Line 711: Fixed TenantMember owner_id - Line 764: Enhanced logging 2. **services/distribution/app/api/internal_demo.py** - Lines 147-166: Skip distribution cloning for child tenants - Lines 261-267: Removed unsupported `estimated_delivery_time` field - Lines 273-292: Fixed `items` field issue (model doesn't support it) - Stored items data in `delivery_notes` field for demo display - Added clear logging explaining why child tenants don't get distribution data ## Verification Checklist - [x] Child tenant owner_id now matches parent tenant owner_id - [x] Child tenants include demo_session_id for session-based queries - [x] TenantMember records use consistent owner_id - [x] Distribution fixture exists with proper structure - [x] Distribution API endpoints are correctly implemented - [x] Frontend hooks properly call distribution API - [x] Distribution cloning skips child tenants (they don't manage distribution) - [x] FileNotFoundError for child distribution files is resolved - [x] Shipment model field compatibility issues resolved - [x] Items data stored in delivery_notes for demo display ## Next Steps 1. **Deploy Fixes** ```bash kubectl rollout restart deployment tenant-service -n bakery-ia kubectl rollout restart deployment distribution-service -n bakery-ia ``` 2. **Create New Demo Session** - Must be a new session, old sessions have wrong data 3. **Test Multi-Tenant Menu** - Verify all 6 tenants visible - Test switching between tenants 4. **Test Distribution Page** - Check if data displays - If not, investigate date filtering 5. **Monitor Logs** ```bash # Watch tenant service logs kubectl logs -f deployment/tenant-service -n bakery-ia # Watch distribution service logs kubectl logs -f deployment/distribution-service -n bakery-ia ``` ## Additional Notes ### Why This Fix Works The tenant visibility is controlled by the `owner_id` field. When a user logs in and requests their tenants: 1. Backend extracts user_id from JWT: `d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7` 2. Queries database: `SELECT * FROM tenants WHERE owner_id = ? AND demo_session_id = ?` 3. Previously: Parent had correct owner_id, children had wrong owner_id → Only parent returned 4. Now: Parent AND children have same owner_id → All tenants returned ✅ ### Distribution Data Structure The distribution fixture creates a realistic enterprise distribution scenario: - **Routes:** Delivery routes from central production to retail outlets - **Shipments:** Individual shipments assigned to routes - **Child References:** Shipments reference child_tenant_id for destination tracking - **Time Offsets:** Uses BASE_TS + offset for realistic scheduling Example: ```json { "route_number": "MAD-BCN-001", "route_date": "BASE_TS + 2h", // 2 hours after session creation "route_sequence": [ {"stop_number": 1, "location_id": "parent-id"}, {"stop_number": 2, "location_id": "child-A-id"}, {"stop_number": 3, "location_id": "child-B-id"} ] } ``` This creates a distribution network where: - Central production (parent) produces goods - Distribution routes deliver to retail outlets (children) - Shipments track individual deliveries - All entities are linked for network-wide visibility --- ## Summary of All Changes ### Services Modified 1. **tenant-service** - Fixed child tenant owner_id 2. **distribution-service** - Fixed child cloning + shipment fields ### Database Impact - Child tenants created in new sessions will have correct owner_id - Distribution routes and shipments will be created successfully - No migration needed (only affects new demo sessions) ### Deployment Commands ```bash # Restart affected services kubectl rollout restart deployment tenant-service -n bakery-ia kubectl rollout restart deployment distribution-service -n bakery-ia # Verify deployments kubectl rollout status deployment tenant-service -n bakery-ia kubectl rollout status deployment distribution-service -n bakery-ia ``` ### Testing Checklist - [ ] Create new enterprise demo session - [ ] Verify 6 tenants visible in tenant switcher - [ ] Switch between parent and child tenants - [ ] Navigate to Distribution page on parent tenant - [ ] Verify routes and shipments are displayed - [ ] Check demo session logs for errors --- **Fix Status:** ✅ ALL FIXES COMPLETED **Testing Status:** ⏳ PENDING USER VERIFICATION **Production Ready:** ✅ YES (after testing)