HH/docs/STAFF_USER_ACCOUNT_FEATURE_COMPLETE.md

811 lines
22 KiB
Markdown

# Staff User Account Feature - Complete Implementation
## Overview
The Staff User Account feature enables administrators to create optional user accounts for staff members, allowing them to log into the PX360 system. This implementation provides a complete CRUD interface with user account management capabilities.
## Architecture
### Model Relationship
The `Staff` model has an optional one-to-one relationship with the `User` model:
```python
# apps/organizations/models.py
class Staff(models.Model):
# ... other fields ...
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='staff_profile'
)
```
**Key Features:**
- Optional: Staff can exist without a user account
- SET_NULL: If user is deleted, staff record is preserved
- Related name: `user.staff_profile` allows reverse lookup
## Core Components
### 1. StaffService (`apps/organizations/services.py`)
The `StaffService` class provides all business logic for user account management:
#### Methods
##### `create_user_for_staff(staff, role='staff', request=None)`
Creates a User account for a Staff member.
**Process:**
1. Validates staff doesn't already have a user account
2. Requires staff to have an email address
3. Generates unique username (format: `firstname.lastname` or `firstname.lastnameN`)
4. Generates secure 12-character password
5. Creates User with staff information:
- Email as primary identifier
- Username (optional, for backward compatibility)
- First/last name
- Employee ID
- Hospital and Department
- Active status
6. Assigns role via group membership
7. Links user to staff
8. Logs audit trail
**Returns:** Created User instance
**Raises:** ValueError if staff already has user or no email
##### `link_user_to_staff(staff, user_id, request=None)`
Links an existing User account to a Staff member.
**Process:**
1. Validates staff doesn't have a user account
2. Retrieves user by ID
3. Links user to staff
4. Updates user's organization data if missing
5. Logs audit trail
**Returns:** Updated Staff instance
**Raises:** ValueError if staff has user or user not found
##### `unlink_user_from_staff(staff, request=None)`
Removes User account association from a Staff member.
**Process:**
1. Validates staff has a user account
2. Sets staff.user to None
3. Logs audit trail
**Returns:** Updated Staff instance
**Raises:** ValueError if staff has no user account
##### `send_credentials_email(staff, password, request)`
Sends login credentials email to staff member.
**Process:**
1. Validates staff has email and user account
2. Builds absolute login URL
3. Renders HTML email template
4. Sends email via Django's send_mail
5. Logs audit trail
**Raises:** ValueError if no email or user account
##### `generate_username(staff)`
Generates a unique username from staff name.
**Format:** `firstname.lastname` (lowercase)
**Duplicate Handling:** Appends incremental number (e.g., `john.smith2`)
**Returns:** Unique username string
##### `generate_password(length=12)`
Generates a secure random password.
**Characters:** ASCII letters + digits + punctuation
**Length:** 12 characters (configurable)
**Returns:** Secure random password string
##### `get_staff_type_role(staff_type)`
Maps staff_type to role name.
**Current Mapping:** All staff types map to 'staff' role
**Extensible:** Can be enhanced for role-based permissions
**Returns:** Role name string
### 2. API ViewSet (`apps/organizations/views.py`)
The `StaffViewSet` provides REST API endpoints:
#### Standard CRUD Operations
- `GET /api/organizations/staff/` - List staff (filtered by user role)
- `POST /api/organizations/staff/` - Create new staff
- `GET /api/organizations/staff/{id}/` - Retrieve staff details
- `PUT /api/organizations/staff/{id}/` - Update staff
- `PATCH /api/organizations/staff/{id}/` - Partial update staff
- `DELETE /api/organizations/staff/{id}/` - Delete staff
#### Custom Actions
##### `POST /api/organizations/staff/{id}/create_user_account/`
Creates a user account for staff member.
**Permissions:** PX Admin or Hospital Admin
**Body:** Optional `role` parameter (defaults to staff type role)
**Process:**
1. Validates staff doesn't have user account
2. Checks user permissions
3. Creates user via StaffService
4. Generates password
5. Sends credentials email
6. Returns success message with staff data
**Response:**
```json
{
"message": "User account created and credentials emailed successfully",
"staff": { ...staff data... },
"email": "staff@example.com"
}
```
##### `POST /api/organizations/staff/{id}/link_user/`
Links existing user account to staff member.
**Permissions:** PX Admin or Hospital Admin
**Body:** Required `user_id` parameter
**Process:**
1. Validates staff doesn't have user account
2. Checks user permissions
3. Links user via StaffService
4. Returns success message
**Response:**
```json
{
"message": "User account linked successfully",
"staff": { ...staff data... }
}
```
##### `POST /api/organizations/staff/{id}/unlink_user/`
Removes user account association from staff member.
**Permissions:** PX Admin or Hospital Admin
**Process:**
1. Validates staff has user account
2. Checks user permissions
3. Unlinks user via StaffService
4. Returns success message
**Response:**
```json
{
"message": "User account unlinked successfully",
"staff": { ...staff data... }
}
```
##### `POST /api/organizations/staff/{id}/send_invitation/`
Sends credentials email to staff member.
**Permissions:** PX Admin or Hospital Admin
**Process:**
1. Validates staff has user account
2. Checks user permissions
3. Generates new password
4. Updates user password
5. Sends email via StaffService
6. Returns success message
**Response:**
```json
{
"message": "Invitation email sent successfully",
"staff": { ...staff data... }
}
```
### 3. Admin Interface (`apps/organizations/admin.py`)
The Django admin interface is enhanced with user account management:
#### StaffAdmin Features
**List Display:**
- Staff name, type, job title, employee ID
- Hospital, department
- User account status (✓ Yes / ✗ No)
- Status
**Custom Column:**
- `has_user_account`: Displays user account status with color coding
**Bulk Actions:**
1. **"Create user accounts for selected staff"**
- Creates user accounts for multiple staff members
- Sends credentials emails
- Reports success/failure count
2. **"Send credential emails to selected staff"**
- Resends credentials to staff with existing accounts
- Generates new passwords
- Reports success/failure count
**Filtering & Search:**
- Filter by status, hospital, staff type, specialization
- Search by name, Arabic names, employee ID, license, job title
**Autocomplete Fields:**
- Hospital, Department, User (for linking)
### 4. UI Templates
#### Staff List (`templates/organizations/staff_list.html`)
**Features:**
- Filters: Hospital, status, staff type, search
- Table displays user account status with badge
- Quick actions for user account management:
- Create user account (for staff without accounts)
- Send invitation email (for staff with accounts)
- Unlink user account (for staff with accounts)
- Modals for confirmation dialogs
- Pagination support
**Actions Column:**
- View details (always available)
- Create user account (if no user and has email)
- Send invitation (if has user)
- Unlink user (if has user)
- Actions restricted to PX Admin and Hospital Admin
#### Staff Detail (`templates/organizations/staff_detail.html`)
**User Account Card:**
- Displays user account status (alert box)
- Shows user details (username, email, active status, created date)
- Action buttons:
- "Create User Account" (if no account)
- "Resend Invitation Email" (if account exists)
- "Unlink User Account" (if account exists)
- Confirmation modals for each action
**Other Cards:**
- Personal Information
- Organization Information
- Contact Information
- Status Information
**JavaScript Functions:**
- `createUserAccount()` - Shows modal and triggers API call
- `confirmCreateUser()` - Executes create user account
- `sendInvitation()` - Shows modal and triggers API call
- `confirmSendInvitation()` - Executes send invitation
- `unlinkUserAccount()` - Shows modal and triggers API call
- `confirmUnlinkUser()` - Executes unlink user
### 5. Email Template (`templates/organizations/emails/staff_credentials.html`)
**Design:**
- Professional HTML email with gradient header
- Clean, readable layout
- Responsive design
**Content:**
- Welcome message
- Credentials box with:
- Username
- Password
- Email
- Security notice (change password after first login)
- Login button (links to system)
- Footer with copyright
**Styling:**
- Purple gradient theme (matches PX360 branding)
- Color-coded credentials box
- Warning banner for security notice
- Mobile-friendly
### 6. Forms (`apps/organizations/forms.py`)
**StaffForm:**
- All standard staff fields
- Hospital filtering based on user role
- Department filtering based on selected hospital
- Email validation (lowercase, trimmed)
- Employee ID uniqueness validation
**No User Management in Form:**
- User account creation handled via separate actions
- Keeps form focused on staff data
- User management in detail view for better UX
## Permission Model
### Access Control
**PX Admin:**
- Can create user accounts for any staff
- Can link/unlink users for any staff
- Can send invitations to any staff
- Full access to all staff management features
**Hospital Admin:**
- Can create user accounts for staff in their hospital
- Can link/unlink users for staff in their hospital
- Can send invitations to staff in their hospital
- Cannot manage staff from other hospitals
**Department Manager:**
- Can view staff in their department
- Cannot create/link/unlink user accounts
- Cannot manage user accounts
**Regular Staff:**
- Can view staff in their hospital
- Cannot create/link/unlink user accounts
- Cannot manage user accounts
### Implementation
Permissions enforced in:
- API ViewSet actions
- Admin actions
- UI templates (buttons hidden for unauthorized users)
## Workflow Examples
### Example 1: Creating Staff and User Account
**Step 1: Create Staff Profile**
```
POST /api/organizations/staff/
{
"first_name": "Ahmed",
"last_name": "Al-Saud",
"email": "ahmed.alsaud@hospital.com",
"employee_id": "EMP001",
"hospital": "...",
"department": "...",
"staff_type": "physician"
}
```
**Step 2: Create User Account**
```
POST /api/organizations/staff/{id}/create_user_account/
{
"role": "staff"
}
```
**Result:**
- User created with username: `ahmed.alsaud`
- Password generated: `Xk9#mP2$vL5!`
- Email sent to ahmed.alsaud@hospital.com
- Staff.user linked to new user
### Example 2: Linking Existing User
**Step 1: Create Staff Profile** (same as above)
**Step 2: Link Existing User**
```
POST /api/organizations/staff/{id}/link_user/
{
"user_id": "123e4567-e89b-12d3-a456-426614174000"
}
```
**Result:**
- Staff.user linked to existing user
- User's organization data updated if missing
### Example 3: Resending Credentials
**Scenario:** Staff member forgot their password
**Action:**
```
POST /api/organizations/staff/{id}/send_invitation/
```
**Result:**
- New password generated: `Qw7$rT3!nK9#`
- User password updated in database
- Email sent with new credentials
### Example 4: Removing Login Access
**Scenario:** Staff member leaves organization
**Action:**
```
POST /api/organizations/staff/{id}/unlink_user/
```
**Result:**
- Staff.user set to None
- User account still exists but no longer linked
- Staff member cannot log in
### Example 5: Bulk User Account Creation
**Scenario:** Onboarding 10 new staff members
**Action:**
1. Go to Django Admin > Staff
2. Select 10 staff members (all have emails)
3. Choose "Create user accounts for selected staff"
4. Click "Go"
**Result:**
- 10 user accounts created
- 10 emails sent with credentials
- Admin message: "Created 10 user accounts. Failed: 0"
## Technical Details
### Username Generation Algorithm
```python
base_username = f"{first_name.lower()}.{last_name.lower()}"
username = base_username
counter = 1
while User.objects.filter(username=username).exists():
username = f"{base_username}{counter}"
counter += 1
```
**Examples:**
- John Smith → `john.smith`
- Duplicate John Smith → `john.smith2`
- Another duplicate → `john.smith3`
### Password Generation
```python
alphabet = string.ascii_letters + string.digits + string.punctuation
password = ''.join(secrets.choice(alphabet) for _ in range(12))
```
**Character Set:**
- Uppercase: A-Z
- Lowercase: a-z
- Digits: 0-9
- Punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
**Strength:** Cryptographically secure (uses `secrets` module)
### Email Sending
**Configuration Required (settings.py):**
```python
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.example.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'noreply@px360.com'
EMAIL_HOST_PASSWORD = 'password'
DEFAULT_FROM_EMAIL = 'PX360 <noreply@px360.com>'
```
**Alternative:** Use Email Backend services (SendGrid, Mailgun, etc.)
### Audit Logging
All user account operations are logged via `AuditService.log_from_request()`:
**Logged Events:**
- `user_creation` - When user account is created
- `other` - For link/unlink/send invitation actions
**Metadata Includes:**
- Staff ID and name
- User ID (for link/unlink)
- Role (for creation)
- Timestamp
- User who performed action
## Security Considerations
### 1. Password Security
- Passwords are hashed using Django's PBKDF2 algorithm
- Generated passwords are only sent via email (never stored in plain text)
- Staff should be instructed to change password after first login
### 2. Email Security
- Credentials are sent via SMTP with TLS
- Email templates include security warnings
- Passwords are not included in any logs
### 3. Access Control
- Role-based permissions enforced at all levels
- Hospital admins can only manage their hospital's staff
- Actions require proper CSRF tokens
### 4. Data Integrity
- Foreign key constraints prevent orphaned records
- SET_NULL on delete preserves staff if user is deleted
- Validation prevents duplicate user accounts
### 5. Audit Trail
- All user account operations are logged
- Logs include who, when, and what
- Metadata stored for analysis
## Database Schema
### Staff Model Fields
```python
class Staff(models.Model):
# Personal Information
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
first_name_ar = models.CharField(max_length=100, blank=True)
last_name_ar = models.CharField(max_length=100, blank=True)
# Role Information
staff_type = models.CharField(max_length=20, choices=STAFF_TYPE_CHOICES)
job_title = models.CharField(max_length=100)
license_number = models.CharField(max_length=50, blank=True)
specialization = models.CharField(max_length=100, blank=True)
# Employee Information
employee_id = models.CharField(max_length=50, unique=True)
email = models.EmailField(blank=True, null=True)
# Organization
hospital = models.ForeignKey(Hospital, on_delete=models.PROTECT)
department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True, blank=True)
# User Account (Optional)
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='staff_profile'
)
# Status
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
```
### Indexes
- `employee_id`: Unique index
- `user`: Unique index (OneToOne)
- `hospital`: Foreign key index
- `department`: Foreign key index
- `email`: Index for lookups
## Testing Recommendations
### Unit Tests
```python
# Test StaffService methods
def test_create_user_for_staff():
staff = create_test_staff(email="test@example.com")
user = StaffService.create_user_for_staff(staff)
assert staff.user == user
assert user.email == "test@example.com"
def test_generate_username():
staff = create_test_staff(first_name="John", last_name="Smith")
username = StaffService.generate_username(staff)
assert username == "john.smith"
def test_generate_password():
password = StaffService.generate_password()
assert len(password) == 12
# Verify contains characters from each class
def test_link_user_to_staff():
staff = create_test_staff()
user = create_test_user()
result = StaffService.link_user_to_staff(staff, user.id)
assert staff.user == user
def test_unlink_user_from_staff():
staff = create_test_staff_with_user()
result = StaffService.unlink_user_from_staff(staff)
assert staff.user is None
```
### Integration Tests
```python
# Test API endpoints
def test_create_user_account_api():
client = authenticate_as_admin()
staff = create_test_staff()
response = client.post(f'/api/organizations/staff/{staff.id}/create_user_account/')
assert response.status_code == 201
assert staff.user is not None
def test_send_invitation_api():
client = authenticate_as_admin()
staff = create_test_staff_with_user()
response = client.post(f'/api/organizations/staff/{staff.id}/send_invitation/')
assert response.status_code == 200
# Verify email was sent
def test_permissions():
# Test that non-admins cannot create user accounts
client = authenticate_as_staff()
staff = create_test_staff()
response = client.post(f'/api/organizations/staff/{staff.id}/create_user_account/')
assert response.status_code == 403
```
### UI Tests
```python
# Test UI interactions
def test_create_user_button_visible_for_admins():
staff = create_test_staff()
admin_user = create_admin_user()
response = client.get(f'/staff/{staff.id}/')
assert 'Create User Account' in response.content
def test_create_user_button_hidden_for_staff():
staff = create_test_staff()
regular_user = create_regular_user()
response = client.get(f'/staff/{staff.id}/')
assert 'Create User Account' not in response.content
```
## Future Enhancements
### Potential Improvements
1. **Role-Based Permissions**
- Different roles for different staff types
- More granular permissions per role
2. **Bulk Import**
- Import staff from CSV/Excel
- Auto-create user accounts during import
3. **Self-Service**
- Allow staff to request user account
- Approval workflow for requests
4. **Password Reset**
- Integration with Django's password reset
- Self-service password reset
5. **2FA Support**
- Two-factor authentication for staff
- Enhanced security options
6. **Session Management**
- Track active sessions
- Force logout from all devices
7. **Audit Reports**
- Generate audit reports
- Export to PDF/Excel
8. **Email Customization**
- Customizable email templates
- Multi-language support
## Troubleshooting
### Common Issues
**Issue:** "Staff member already has a user account"
- **Cause:** Attempting to create duplicate user account
- **Solution:** Check staff.user before creating, or unlink first
**Issue:** "Staff member must have an email address"
- **Cause:** Creating user account without email
- **Solution:** Add email to staff profile first
**Issue:** Email not sent
- **Cause:** Email configuration issue
- **Solution:** Check EMAIL_* settings, verify SMTP credentials
**Issue:** Username already exists
- **Cause:** Non-unique username generation
- **Solution:** The service handles this automatically by appending numbers
**Issue:** Permission denied
- **Cause:** User lacks required role
- **Solution:** Ensure user is PX Admin or Hospital Admin
**Issue:** User account creation failed
- **Cause:** Invalid data, constraints, or service error
- **Solution:** Check error message, validate staff data
## API Reference
### StaffViewSet Endpoints
| Method | Endpoint | Description | Auth |
|--------|----------|-------------|------|
| GET | /api/organizations/staff/ | List staff | Required |
| POST | /api/organizations/staff/ | Create staff | PX Admin / Hospital Admin |
| GET | /api/organizations/staff/{id}/ | Get staff details | Required |
| PUT | /api/organizations/staff/{id}/ | Update staff | PX Admin / Hospital Admin |
| PATCH | /api/organizations/staff/{id}/ | Partial update | PX Admin / Hospital Admin |
| DELETE | /api/organizations/staff/{id}/ | Delete staff | PX Admin / Hospital Admin |
| POST | /api/organizations/staff/{id}/create_user_account/ | Create user account | PX Admin / Hospital Admin |
| POST | /api/organizations/staff/{id}/link_user/ | Link existing user | PX Admin / Hospital Admin |
| POST | /api/organizations/staff/{id}/unlink_user/ | Unlink user | PX Admin / Hospital Admin |
| POST | /api/organizations/staff/{id}/send_invitation/ | Send invitation | PX Admin / Hospital Admin |
### Response Formats
**Success (201 Created):**
```json
{
"message": "User account created and credentials emailed successfully",
"staff": {
"id": "...",
"first_name": "Ahmed",
"last_name": "Al-Saud",
"user": {
"id": "...",
"email": "ahmed.alsaud@hospital.com",
"username": "ahmed.alsaud"
}
},
"email": "ahmed.alsaud@hospital.com"
}
```
**Error (400 Bad Request):**
```json
{
"error": "Staff member already has a user account"
}
```
**Error (403 Forbidden):**
```json
{
"error": "You do not have permission to create user accounts"
}
```
## Conclusion
The Staff User Account feature provides a complete, production-ready solution for managing staff access to the PX360 system. With robust security, comprehensive audit logging, and a user-friendly interface, administrators can efficiently manage staff user accounts from creation to termination.
### Key Features Summary
✅ Optional one-to-one relationship with User model
✅ Automatic username and password generation
✅ Secure credential delivery via email
✅ Link/unlink existing user accounts
✅ Bulk operations in admin interface
✅ Role-based access control
✅ Complete audit trail
✅ RESTful API with comprehensive endpoints
✅ User-friendly web interface
✅ Internationalization support
✅ Professional email templates
The implementation follows Django best practices and is ready for production use.