swiftops-backend / docs /features /users /bulk-invitations.md
kamau1's picture
refactor: remove reconciliation system and all related code, tasks, and docs
d12a170
# 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: <file>
project_id: <uuid> (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