18 KiB
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_childtenants, 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
itemsfield (list of products being shipped) - Fixture contains
estimated_delivery_timefield - 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:
-
Added parent tenant lookup (Lines 599-614):
# 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 -
Updated child tenant creation (Line 637):
# 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 -
Updated TenantMember creation (Line 711):
# 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", ... ) -
Enhanced logging (Line 764):
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:
- Added early return for child tenants (Lines 147-166):
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:
-
Removed estimated_delivery_time field (Lines 261-267):
# 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" ) -
Stored items in delivery_notes (Lines 273-287):
# 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_notesfield - 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
-
User logs into enterprise demo
- Demo session created with
demo_account_type: "enterprise" - Session ID stored in JWT token
- Demo session created with
-
Frontend requests user tenants
- Calls:
GET /tenants/user/{user_id}/owned - Backend:
services/tenant/app/api/tenant_operations.py:284
- Calls:
-
Backend retrieves virtual tenants
- Extracts
demo_session_idfrom 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 ✅
- Extracts
-
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) ✅
- Component:
Distribution Data Flow
-
Demo session cloning
- Orchestrator calls distribution service:
POST /internal/demo/clone - Loads fixture:
shared/demo/fixtures/enterprise/parent/12-distribution.json
- Orchestrator calls distribution service:
-
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
-
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
- Calls:
Testing Instructions
1. Restart Services
After applying the fixes, you need to restart the affected services:
# 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.
# 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
- Click "Try Enterprise Demo" button
- Wait for demo session to initialize
- After redirect to dashboard, look for the tenant switcher in the top-left
- Click on the tenant switcher dropdown
- 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
- From the enterprise dashboard, navigate to "Distribution"
- Check if routes and shipments are displayed
- 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:
-- 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
-
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
-
Check logs for child tenant creation
kubectl logs -f deployment/tenant-service -n bakery-ia | grep "Child outlet created"- Should show:
owner_id=d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7
- Should show:
-
Verify demo session ID in JWT
- Open browser DevTools > Application > Storage > Local Storage
- Check if
demo_session_idis present in token - Should match the session_id in database
Distribution Data Not Showing
-
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
-
Verify distribution data was cloned
kubectl logs -f deployment/demo-session-service -n bakery-ia | grep "distribution"- Should show: "Distribution data cloning completed"
- Should show: records_cloned > 0
-
Check backend endpoint
# 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" -
Check browser console for errors
- Open DevTools > Console
- Look for API errors or failed requests
- Check Network tab for distribution API calls
Files Changed
-
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
-
services/distribution/app/api/internal_demo.py
- Lines 147-166: Skip distribution cloning for child tenants
- Lines 261-267: Removed unsupported
estimated_delivery_timefield - Lines 273-292: Fixed
itemsfield issue (model doesn't support it) - Stored items data in
delivery_notesfield for demo display - Added clear logging explaining why child tenants don't get distribution data
Verification Checklist
- Child tenant owner_id now matches parent tenant owner_id
- Child tenants include demo_session_id for session-based queries
- TenantMember records use consistent owner_id
- Distribution fixture exists with proper structure
- Distribution API endpoints are correctly implemented
- Frontend hooks properly call distribution API
- Distribution cloning skips child tenants (they don't manage distribution)
- FileNotFoundError for child distribution files is resolved
- Shipment model field compatibility issues resolved
- Items data stored in delivery_notes for demo display
Next Steps
-
Deploy Fixes
kubectl rollout restart deployment tenant-service -n bakery-ia kubectl rollout restart deployment distribution-service -n bakery-ia -
Create New Demo Session
- Must be a new session, old sessions have wrong data
-
Test Multi-Tenant Menu
- Verify all 6 tenants visible
- Test switching between tenants
-
Test Distribution Page
- Check if data displays
- If not, investigate date filtering
-
Monitor Logs
# 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:
- Backend extracts user_id from JWT:
d2e3f4a5-b6c7-48d9-e0f1-a2b3c4d5e6f7 - Queries database:
SELECT * FROM tenants WHERE owner_id = ? AND demo_session_id = ? - Previously: Parent had correct owner_id, children had wrong owner_id → Only parent returned
- 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:
{
"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
- tenant-service - Fixed child tenant owner_id
- 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
# 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)