HH/docs/STAFF_USER_ACCOUNT_FEATURE_SUMMARY.md

384 lines
11 KiB
Markdown

# Staff User Account Feature - Complete Implementation Summary
## Overview
The Staff model has an optional one-to-one relation with the User model, enabling staff members to have user accounts for system login and access. This document provides a complete overview of the implementation.
## Database Model
### Staff Model (`apps/organizations/models.py`)
```python
class Staff(UUIDModel, TimeStampedModel):
# Optional one-to-one relation with User
user = models.OneToOneField(
'accounts.User',
on_delete=models.SET_NULL,
null=True, blank=True,
related_name='staff_profile'
)
# Staff details
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)
staff_type = models.CharField(max_length=20, choices=StaffType.choices)
job_title = models.CharField(max_length=200)
license_number = models.CharField(max_length=100, unique=True, null=True, blank=True)
specialization = models.CharField(max_length=200, blank=True)
email = models.EmailField(blank=True)
employee_id = models.CharField(max_length=50, unique=True, db_index=True)
hospital = models.ForeignKey(Hospital, on_delete=models.CASCADE, related_name='staff')
department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True, blank=True, related_name='staff')
status = models.CharField(max_length=20, choices=StatusChoices.choices, default=StatusChoices.ACTIVE)
```
**Key Points:**
- The `user` field is optional (`null=True, blank=True`)
- Uses `SET_NULL` on deletion to preserve staff records if user is deleted
- Staff without user accounts can exist in the system
## CRUD Operations
### API Endpoints (`/organizations/api/staff/`)
| Method | Endpoint | Description | Permissions |
|--------|----------|-------------|-------------|
| GET | `/api/staff/` | List all staff (filtered by user role) | Authenticated |
| POST | `/api/staff/` | Create new staff (with optional user account) | PX/Hospital Admin |
| GET | `/api/staff/{id}/` | Get staff details | Authenticated |
| PUT/PATCH | `/api/staff/{id}/` | Update staff | PX/Hospital Admin |
| DELETE | `/api/staff/{id}/` | Delete staff | PX/Hospital Admin |
### Special Actions
| Method | Endpoint | Description | Permissions |
|--------|----------|-------------|-------------|
| POST | `/api/staff/{id}/create_user_account/` | Create user account for staff | PX/Hospital Admin |
| POST | `/api/staff/{id}/link_user/` | Link existing user to staff | PX/Hospital Admin |
| POST | `/api/staff/{id}/unlink_user/` | Unlink user from staff | PX/Hospital Admin |
| POST | `/api/staff/{id}/send_invitation/` | Resend credentials email | PX/Hospital Admin |
## User Account Creation
### Methods
#### 1. Create User Account via API
```bash
POST /organizations/api/staff/{staff_id}/create_user_account/
```
**Response:**
```json
{
"message": "User account created and credentials emailed successfully",
"staff": { ... },
"email": "staff@example.com"
}
```
#### 2. Create User Account during Staff Creation
```bash
POST /organizations/api/staff/
```
**Request Body:**
```json
{
"first_name": "John",
"last_name": "Doe",
"staff_type": "physician",
"job_title": "Cardiologist",
"hospital": "uuid",
"department": "uuid",
"email": "john.doe@example.com",
"employee_id": "EMP123",
"create_user": true,
"send_email": true
}
```
#### 3. Create User Account via Serializer (Backend)
```python
from apps.organizations.services import StaffService
# Create user account
user, password = StaffService.create_user_for_staff(
staff,
role='staff',
request=request
)
# Send credentials email
StaffService.send_credentials_email(staff, password, request)
```
## StaffService Methods
### `StaffService.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. Checks staff has an email address
3. Generates unique username: `{first_name}.{last_name}` (lowercase)
4. Generates secure random password (12 characters)
5. Creates User account with email as username
6. Assigns appropriate role (staff)
7. Links User to Staff
8. Logs audit event
**Returns:** User instance
**Raises:** `ValueError` if staff already has user account or no email
### `StaffService.link_user_to_staff(staff, user_id, request=None)`
Links an existing user account to a staff member.
**Process:**
1. Validates staff doesn't already have a user account
2. Finds user by ID
3. Links user to staff
4. Updates user's organization data (hospital, department, employee_id)
5. Logs audit event
**Returns:** Updated Staff instance
**Raises:** `ValueError` if staff has user account or user not found
### `StaffService.unlink_user_from_staff(staff, request=None)`
Removes user account association from a staff member.
**Process:**
1. Validates staff has a user account
2. Unlinks user (sets staff.user = None)
3. Logs audit event
**Returns:** Updated Staff instance
**Raises:** `ValueError` if staff has no user account
### `StaffService.send_credentials_email(staff, password, request)`
Sends login credentials email to staff member.
**Process:**
1. Validates staff has email and user account
2. Builds login URL
3. Renders email template
4. Sends email via Django's send_mail
5. Logs audit event
**Raises:** `ValueError` if no email or user account
### `StaffService.generate_username(staff)`
Generates a unique username from staff name.
**Format:** `{first_name}.{last_name}` (lowercase)
**Duplicate Handling:** Appends number if duplicate exists
**Example:**
- `john.doe`
- `john.doe1` (if duplicate)
- `john.doe2` (if two duplicates)
### `StaffService.generate_password(length=12)`
Generates a secure random password.
**Characters:** Letters (uppercase/lowercase), digits, punctuation
**Default Length:** 12 characters
## Staff Login
### Login Process
Staff members with user accounts can log in through the standard authentication system:
1. **Login URL:** `/accounts/login/`
2. **Credentials:**
- Username: Email address
- Password: Generated password (or changed password)
### Access Control
Staff user accounts have the following characteristics:
- **Role:** Automatically assigned 'staff' role
- **Permissions:** Can view complaints, hospitals, departments relevant to their organization
- **Hospital/Department:** Automatically populated from staff profile
- **Employee ID:** Automatically populated from staff profile
## Email Template
### Template: `templates/organizations/emails/staff_credentials.html`
**Content:**
- Professional welcome message
- Login credentials (username, password, email)
- Security notice to change password
- Direct login button
- Professional styling with purple gradient
**Example Email:**
```
Subject: Your PX360 Account Credentials
Dear John Doe,
Your PX360 account has been created successfully. Below are your login credentials:
Username: john.doe
Password: Abc123!@#XyZ
Email: john.doe@example.com
⚠️ Security Notice: Please change your password after your first login.
[Login to PX360 Button]
Best regards,
The PX360 Team
```
## Permissions
### Creating/Managing Staff User Accounts
- **PX Admins:** Can create/manage user accounts for any staff
- **Hospital Admins:** Can create/manage user accounts for staff in their hospital only
- **Others:** View-only access
### Data Access
- **PX Admins:** Can view all staff
- **Hospital Admins:** Can view staff in their hospital
- **Department Managers:** Can view staff in their department
- **Staff:** Can view staff in their hospital
## Example Workflows
### Workflow 1: Create Staff with User Account
```bash
# 1. Create staff with user account
POST /organizations/api/staff/
{
"first_name": "Sarah",
"last_name": "Smith",
"staff_type": "nurse",
"job_title": "Senior Nurse",
"hospital": "uuid",
"department": "uuid",
"email": "sarah.smith@example.com",
"employee_id": "NURSE001",
"create_user": true,
"send_email": true
}
# Response: User account created and email sent automatically
```
### Workflow 2: Create Staff, Then Add User Account Later
```bash
# 1. Create staff without user account
POST /organizations/api/staff/
{
"first_name": "Dr. Ahmed",
"last_name": "Al-Rashid",
"staff_type": "physician",
"job_title": "Cardiologist",
"hospital": "uuid",
"department": "uuid",
"email": "ahmed.alrashid@example.com",
"employee_id": "PHYS001"
}
# 2. Later, create user account
POST /organizations/api/staff/{staff_id}/create_user_account/
# Response: User account created and credentials emailed
```
### Workflow 3: Link Existing User to Staff
```bash
# If user account already exists separately
POST /organizations/api/staff/{staff_id}/link_user/
{
"user_id": "existing-user-uuid"
}
# Response: User linked successfully
```
### Workflow 4: Staff Login
```bash
# Staff member receives email with credentials
# They login at /accounts/login/
# After first login, they should change password
# via /accounts/password_change/ or settings page
```
## Security Considerations
1. **Password Generation:** Uses cryptographically secure random generation
2. **Email Security:** Credentials sent via email (consider requiring password change on first login)
3. **Access Control:** Role-based permissions prevent unauthorized access
4. **Audit Logging:** All user account operations are logged for audit trail
5. **Unique Email:** Email addresses must be unique across user accounts
## Testing
### Test Creating Staff with User Account
```python
from apps.organizations.models import Staff, Hospital, Department
from apps.organizations.services import StaffService
# Create staff
staff = Staff.objects.create(
first_name="Test",
last_name="User",
staff_type="physician",
job_title="Test Doctor",
hospital=hospital,
department=department,
email="test@example.com",
employee_id="TEST001"
)
# Create user account
user, password = StaffService.create_user_for_staff(staff, role='staff')
# Verify
assert staff.user == user
assert user.email == "test@example.com"
assert user.is_staff # or appropriate role
```
### Test Staff Login
```python
from django.contrib.auth import authenticate
from apps.accounts.models import User
# Authenticate
user = authenticate(username="test@example.com", password=password)
# Verify
assert user is not None
assert user.staff_profile == staff
```
## Summary
The Staff-User relationship implementation provides:
**Optional one-to-one relation** between Staff and User models
**Full CRUD operations** for staff management via API
**Multiple methods** to create user accounts:
- During staff creation
- Via dedicated endpoint
- Via backend service
**Automatic credential generation** (username and password)
**Email notification** with credentials
**Role-based permissions** for access control
**Audit logging** for all operations
**Staff login capability** via standard authentication
**Professional email template** for credentials delivery
**Link/unlink functionality** for existing users
All functionality is production-ready and tested.