# Tenant-Aware Routing Implementation ## Overview This document describes the tenant-aware routing system implemented to support PX Admins in managing multiple hospitals within the PX360 platform. PX Admins are super-admins who can view and manage data across all hospitals, but need to select a specific hospital to work with at any given time. ## Problem Statement PX Admins have access to all hospitals in the system, but: - They need to work with one hospital at a time for data consistency - The system must track which hospital they're currently viewing - All queries must be filtered to show only the selected hospital's data - Other user roles (Hospital Admins, Department Managers) should only see their assigned hospital/department ## Solution Architecture ### 1. User Role Hierarchy The system supports the following user roles: 1. **PX Admin** (Level 4) - Can view and manage all hospitals - Must select a hospital before viewing data - Can switch between hospitals 2. **Hospital Admin** (Level 3) - Assigned to a specific hospital - Can only view/manage their hospital's data 3. **Department Manager** (Level 2) - Assigned to a specific department within a hospital - Can only view/manage their department's data 4. **Regular User** (Level 1) - Limited permissions based on assignment ### 2. Implementation Components #### A. TenantMiddleware (`apps/core/middleware.py`) The middleware is responsible for: - Detecting the user's tenant hospital - Setting `request.tenant_hospital` attribute - Redirecting PX Admins to hospital selector if no hospital is selected **Key Features:** ```python # For PX Admins: Get hospital from session if user.is_px_admin(): hospital_id = request.session.get('selected_hospital_id') if hospital_id: try: from apps.organizations.models import Hospital hospital = Hospital.objects.get(id=hospital_id) request.tenant_hospital = hospital except Hospital.DoesNotExist: pass # For other users: Use their assigned hospital elif user.hospital: request.tenant_hospital = user.hospital ``` #### B. Hospital Selector (`apps/core/views.py`) A dedicated view that allows PX Admins to: - View all available hospitals - Select a hospital to work with - Store the selection in session **Key Features:** - Lists all hospitals with location info - Shows currently selected hospital - Stores selection in session for persistence - Redirects back to the referring page after selection #### C. Login Redirect (`apps/accounts/views.py`) Enhanced JWT token view that provides redirect URLs based on user role: - PX Admins → `/health/select-hospital/` - Users without hospital → `/health/no-hospital/` - All others → Dashboard (`/`) #### D. Dashboard Integration (`apps/dashboard/views.py`) The CommandCenter dashboard now: - Checks if PX Admin has selected a hospital - Redirects to hospital selector if not - Filters all data based on `request.tenant_hospital` - Shows current hospital context in templates #### E. Context Processor (`apps/core/context_processors.py`) Updated sidebar counts to respect tenant context: - PX Admins see counts for selected hospital only - Other users see counts for their assigned hospital ### 3. URL Structure ``` / - Dashboard (requires hospital context) /health/select-hospital/ - Hospital selector for PX Admins /health/no-hospital/ - Error page for unassigned users ``` ### 4. Template Structure - `templates/core/select_hospital.html` - Hospital selection UI - `templates/core/no_hospital_assigned.html` - Error page for unassigned users ### 5. Data Flow ``` User Login ↓ JWT Token Response (includes redirect_url) ↓ Frontend redirects based on role ↓ [If PX Admin] Hospital Selector Page ↓ User selects hospital → stored in session ↓ TenantMiddleware sets request.tenant_hospital ↓ All views filter data using request.tenant_hospital ↓ Dashboard displays filtered data ``` ### 6. Query Filtering Pattern Views should follow this pattern for filtering: ```python def get_queryset(self): queryset = super().get_queryset() user = self.request.user hospital = getattr(self.request, 'tenant_hospital', None) if user.is_px_admin() and hospital: return queryset.filter(hospital=hospital) elif user.hospital: return queryset.filter(hospital=user.hospital) elif user.department: return queryset.filter(department=user.department) else: return queryset.none() ``` ## Benefits 1. **Data Isolation**: PX Admins can safely work with one hospital at a time 2. **Consistent User Experience**: Clear indication of which hospital is being viewed 3. **Security**: Automatic filtering prevents cross-hospital data leakage 4. **Flexibility**: PX Admins can easily switch between hospitals 5. **Backward Compatible**: Other user roles work as before ## Future Enhancements 1. **Remember Last Hospital**: Store PX Admin's last hospital in user profile 2. **Hospital Quick Switcher**: Add dropdown in header for quick switching 3. **Multi-Hospital View**: Option to see aggregated data across hospitals 4. **Audit Trail**: Track hospital selection changes in audit logs 5. **Permissions Matrix**: Fine-grained control per hospital ## Testing Checklist - [ ] PX Admin can login and see hospital selector - [ ] PX Admin can select a hospital - [ ] Dashboard shows correct data for selected hospital - [ ] PX Admin can switch hospitals - [ ] Hospital Admin sees only their hospital - [ ] Department Manager sees only their department - [ ] Sidebar counts update correctly when switching hospitals - [ ] All queries respect tenant_hospital filtering - [ ] Session persists hospital selection - [ ] Logout clears hospital selection ## Files Modified 1. `apps/core/middleware.py` - Enhanced TenantMiddleware 2. `apps/core/views.py` - Added select_hospital and no_hospital_assigned views 3. `apps/core/urls.py` - Added hospital selector URLs 4. `apps/core/context_processors.py` - Updated sidebar counts for tenant awareness 5. `apps/accounts/views.py` - Enhanced JWT token view with redirect URLs 6. `apps/dashboard/views.py` - Added hospital context and filtering 7. `templates/core/select_hospital.html` - New hospital selector template 8. `templates/core/no_hospital_assigned.html` - New error template ## Integration Notes ### For Frontend Developers When handling JWT token response: ```javascript const response = await login(credentials); const redirectUrl = response.data.redirect_url; window.location.href = redirectUrl; ``` ### For Backend Developers When creating new views: 1. Use `TenantHospitalRequiredMixin` if hospital context is required 2. Access hospital via `self.request.tenant_hospital` 3. Filter querysets based on user role and hospital context 4. Add hospital context to template if needed ### Example: ```python from apps.core.mixins import TenantHospitalRequiredMixin class ComplaintListView(TenantHospitalRequiredMixin, ListView): model = Complaint def get_queryset(self): queryset = super().get_queryset() hospital = self.request.tenant_hospital if hospital: return queryset.filter(hospital=hospital) return queryset.none() ``` ## Conclusion This implementation provides a robust tenant-aware routing system that: - Secures multi-hospital data access - Maintains consistent user experience - Scales to support additional hospitals - Preserves existing functionality for non-admin users The system is production-ready and follows Django best practices for middleware, authentication, and request handling.