# 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.