# Bulk User Invitations via CSV ## Overview The bulk invitation feature allows admins to invite multiple users at once by uploading a CSV file. The system intelligently handles: - **Existing users in project** → Skip (already members) - **Existing users not in project** → Add to project team - **New users** → Send invitations ## CSV Format ### Organization-Level Invitations For inviting users to a client or contractor organization: ```csv email,first_name,last_name,phone,role,invitation_method john@example.com,John,Doe,+254712345678,field_agent,whatsapp jane@example.com,Jane,Smith,+254798765432,dispatcher,email ``` **Required columns:** - `email` - User email address - `role` - System role (field_agent, dispatcher, project_manager, etc.) **Optional columns:** - `first_name` - First name (for personalization) - `last_name` - Last name - `phone` - Phone with country code (+254...) - `invitation_method` - whatsapp, email, or both (default: whatsapp) ### Project-Level Invitations For inviting users directly to a project with role assignments: ```csv email,first_name,last_name,phone,role,invitation_method,project_role,region,subcontractor john@example.com,John,Doe,+254712345678,field_agent,whatsapp,Installer,Nairobi West, jane@example.com,Jane,Smith,+254798765432,dispatcher,email,Site Supervisor,, ``` **Additional columns for projects:** - `project_role` - Role name or ID on the project (e.g., "Installer", "Technician") - `region` - Region name or ID (optional, NULL = project-wide) - `subcontractor` - Subcontractor name or ID (optional, NULL = main contractor) ## API Workflow ### Step 1: Analyze CSV Upload CSV and get analysis of users: ```http POST /api/v1/invitations/bulk/analyze Content-Type: multipart/form-data csv_file: project_id: (OR client_id OR contractor_id) ``` **Response:** ```json { "total_rows": 50, "valid_rows": 48, "invalid_rows": 2, "analysis": { "existing_in_project": [ { "email": "john@example.com", "user_id": "uuid", "name": "John Doe", "current_role": "field_agent", "status": "already_member" } ], "existing_not_in_project": [ { "email": "jane@example.com", "user_id": "uuid", "name": "Jane Smith", "current_role": "dispatcher", "action": "add_to_project", "csv_data": {...} } ], "new_users_to_invite": [ { "email": "bob@example.com", "action": "send_invitation", "csv_data": {...} } ], "errors": [ { "row": 5, "email": "invalid-email", "errors": ["Invalid email format"] } ] } } ``` ### Step 2: Review & Execute Frontend displays analysis to user for review. User confirms, then: ```http POST /api/v1/invitations/bulk/execute Content-Type: application/json { "project_id": "uuid", "users_to_add": [ { "email": "jane@example.com", "user_id": "uuid", "csv_data": {...} } ], "users_to_invite": [ { "email": "bob@example.com", "csv_data": {...} } ], "bulk_operation_id": "BULK_20251211_123456" } ``` **Response:** ```json { "results": { "users_added": { "success": 10, "failed": 0, "details": [...] }, "invitations_sent": { "success": 35, "failed": 2, "details": [...] } }, "summary": { "total_processed": 47, "successful": 45, "failed": 2 } } ``` ## Download CSV Template Get a pre-formatted CSV template: ```http GET /api/v1/invitations/bulk/template/download?template_type=project ``` Template types: - `organization` - For org-level invites (simpler) - `project` - For project-level invites (includes project fields) ## Authorization All roles with `invite_users` permission can use bulk invitations: **Platform Admin:** - Can bulk invite to any context (organization or project) **Client Admin:** - Can bulk invite to their client organization only **Contractor Admin:** - Can bulk invite to their contractor organization only **Project Manager:** - Can bulk invite to their own projects - Must invite to their contractor's organization **Dispatcher:** - Can bulk invite to projects in their contractor - Must invite to their contractor's organization **Sales Manager:** - Can bulk invite to projects in their contractor - Must invite to their contractor's organization **Note:** Service-level authorization validates that users can only invite within their scope (own organization/projects) ## Tracking Bulk invitations are tracked in the `invitation_metadata` JSONB field: ```json { "bulk_import": true, "bulk_operation_id": "BULK_20251211_123456" } ``` This allows filtering/reporting on bulk vs individual invitations without needing a separate table. ## Validation Rules - Email format validation - Phone must start with + and country code - Role must be valid enum value - Project role/region/subcontractor must exist if specified - Max 1000 rows per CSV (configurable via `MAX_BULK_INVITATION_ROWS` env var) - Duplicate detection: won't create duplicate pending invitations ## Error Handling - Invalid CSV rows are reported but don't block valid rows - Each user operation (add/invite) is independent - Partial success is supported (some succeed, some fail) - Detailed error messages for each failure ## Frontend Flow 1. User uploads CSV 2. Call `/analyze` endpoint 3. Display categorized results: - "10 users will be added to project" - "35 invitations will be sent" - "5 users already in project (skipped)" - "2 errors found" 4. User reviews and optionally deselects users 5. User confirms 6. Call `/execute` endpoint with selected users 7. Display results with success/failure breakdown ## Implementation Notes ### No Database Table Needed - Analysis results are returned in-memory (not persisted) - Frontend manages the review/confirmation flow - Bulk operations tracked via `invitation_metadata.bulk_operation_id` in existing `user_invitations` table - Simpler architecture, no session cleanup needed ### Smart User Resolution - Project roles, regions, and subcontractors can be specified by name or UUID - Case-insensitive matching for names - Validates that referenced entities exist and belong to the project ### Files Created - `src/app/schemas/bulk_invitation.py` - Pydantic schemas - `src/app/services/bulk_invitation_service.py` - Business logic - `src/app/api/v1/bulk_invitations.py` - API endpoints - Registered in `src/app/api/v1/router.py` ### Reuses Existing Infrastructure - Uses existing `InvitationService` for sending invitations - Uses existing `UserInvitation` model with metadata tracking - Uses existing `ProjectTeam` model for adding users to projects - No new database migrations required