# 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 ' ``` **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.