262 lines
7.6 KiB
Python
262 lines
7.6 KiB
Python
"""
|
|
Services for Staff management
|
|
"""
|
|
import secrets
|
|
import string
|
|
from django.contrib.auth import get_user_model
|
|
from django.core.mail import send_mail
|
|
from django.template.loader import render_to_string
|
|
from django.conf import settings
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from apps.core.services import AuditService
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class StaffService:
|
|
"""Service for managing staff user accounts"""
|
|
|
|
@staticmethod
|
|
def generate_username(staff):
|
|
"""
|
|
Generate a unique username from staff name.
|
|
Format: first.last (lowercase)
|
|
If duplicate exists, append number.
|
|
"""
|
|
base_username = f"{staff.first_name.lower()}.{staff.last_name.lower()}"
|
|
username = base_username
|
|
counter = 1
|
|
|
|
# Ensure uniqueness
|
|
while User.objects.filter(username=username).exists():
|
|
username = f"{base_username}{counter}"
|
|
counter += 1
|
|
|
|
return username
|
|
|
|
@staticmethod
|
|
def generate_password(length=12):
|
|
"""
|
|
Generate a secure random password.
|
|
"""
|
|
alphabet = string.ascii_letters + string.digits + string.punctuation
|
|
password = ''.join(secrets.choice(alphabet) for _ in range(length))
|
|
return password
|
|
|
|
@staticmethod
|
|
def create_user_for_staff(staff, role='staff', request=None):
|
|
"""
|
|
Create a User account for a Staff member.
|
|
|
|
Args:
|
|
staff: Staff instance
|
|
role: Role name to assign (default: 'staff')
|
|
request: HTTP request for audit logging
|
|
|
|
Returns:
|
|
User: Created user instance
|
|
|
|
Raises:
|
|
ValueError: If staff already has a user account
|
|
"""
|
|
if staff.user:
|
|
raise ValueError("Staff member already has a user account")
|
|
|
|
# Generate email (required for authentication)
|
|
if not staff.email:
|
|
raise ValueError("Staff member must have an email address")
|
|
|
|
# Generate username (optional, for backward compatibility)
|
|
username = StaffService.generate_username(staff)
|
|
password = StaffService.generate_password()
|
|
|
|
# Create user - email is now the username field
|
|
user = User.objects.create_user(
|
|
email=staff.email,
|
|
password=password,
|
|
first_name=staff.first_name,
|
|
last_name=staff.last_name,
|
|
username=username, # Optional field
|
|
employee_id=staff.employee_id,
|
|
hospital=staff.hospital,
|
|
department=staff.department,
|
|
is_active=True,
|
|
is_provisional=False
|
|
)
|
|
|
|
# Assign role
|
|
from .models import Role as RoleModel
|
|
try:
|
|
role_obj = RoleModel.objects.get(name=role)
|
|
user.groups.add(role_obj.group)
|
|
except RoleModel.DoesNotExist:
|
|
pass
|
|
|
|
# Link to staff
|
|
staff.user = user
|
|
staff.save(update_fields=['user'])
|
|
|
|
# Log the action
|
|
if request:
|
|
AuditService.log_from_request(
|
|
event_type='user_creation',
|
|
description=f"User account created for staff member {staff.get_full_name()}",
|
|
request=request,
|
|
content_object=user,
|
|
metadata={
|
|
'staff_id': str(staff.id),
|
|
'staff_name': staff.get_full_name(),
|
|
'role': role
|
|
}
|
|
)
|
|
|
|
return user
|
|
|
|
@staticmethod
|
|
def link_user_to_staff(staff, user_id, request=None):
|
|
"""
|
|
Link an existing User account to a Staff member.
|
|
|
|
Args:
|
|
staff: Staff instance
|
|
user_id: UUID of the user to link
|
|
request: HTTP request for audit logging
|
|
|
|
Returns:
|
|
Staff: Updated staff instance
|
|
|
|
Raises:
|
|
ValueError: If staff already has a user account or user not found
|
|
"""
|
|
if staff.user:
|
|
raise ValueError("Staff member already has a user account")
|
|
|
|
try:
|
|
user = User.objects.get(id=user_id)
|
|
except User.DoesNotExist:
|
|
raise ValueError("User not found")
|
|
|
|
# Link to staff
|
|
staff.user = user
|
|
staff.save(update_fields=['user'])
|
|
|
|
# Update user's organization data
|
|
if not user.hospital:
|
|
user.hospital = staff.hospital
|
|
if not user.department:
|
|
user.department = staff.department
|
|
if not user.employee_id:
|
|
user.employee_id = staff.employee_id
|
|
user.save(update_fields=['hospital', 'department', 'employee_id'])
|
|
|
|
# Log the action
|
|
if request:
|
|
AuditService.log_from_request(
|
|
event_type='other',
|
|
description=f"User {user.email} linked to staff member {staff.get_full_name()}",
|
|
request=request,
|
|
content_object=staff,
|
|
metadata={'user_id': str(user.id)}
|
|
)
|
|
|
|
return staff
|
|
|
|
@staticmethod
|
|
def unlink_user_from_staff(staff, request=None):
|
|
"""
|
|
Remove User account association from a Staff member.
|
|
|
|
Args:
|
|
staff: Staff instance
|
|
request: HTTP request for audit logging
|
|
|
|
Returns:
|
|
Staff: Updated staff instance
|
|
|
|
Raises:
|
|
ValueError: If staff has no user account
|
|
"""
|
|
if not staff.user:
|
|
raise ValueError("Staff member has no user account")
|
|
|
|
user = staff.user
|
|
staff.user = None
|
|
staff.save(update_fields=['user'])
|
|
|
|
# Log the action
|
|
if request:
|
|
AuditService.log_from_request(
|
|
event_type='other',
|
|
description=f"User {user.email} unlinked from staff member {staff.get_full_name()}",
|
|
request=request,
|
|
content_object=staff,
|
|
metadata={'user_id': str(user.id)}
|
|
)
|
|
|
|
return staff
|
|
|
|
@staticmethod
|
|
def send_credentials_email(staff, password, request):
|
|
"""
|
|
Send login credentials email to staff member.
|
|
|
|
Args:
|
|
staff: Staff instance
|
|
password: Generated password
|
|
request: HTTP request for building absolute URLs
|
|
"""
|
|
if not staff.email:
|
|
raise ValueError("Staff member has no email address")
|
|
|
|
user = staff.user
|
|
if not user:
|
|
raise ValueError("Staff member has no user account")
|
|
|
|
# Build login URL
|
|
login_url = request.build_absolute_uri(reverse('accounts:login'))
|
|
|
|
# Render email content
|
|
context = {
|
|
'staff': staff,
|
|
'user': user,
|
|
'password': password,
|
|
'login_url': login_url,
|
|
}
|
|
|
|
subject = "Your PX360 Account Credentials"
|
|
message = render_to_string('organizations/emails/staff_credentials.html', context)
|
|
|
|
# Send email
|
|
send_mail(
|
|
subject,
|
|
'',
|
|
settings.DEFAULT_FROM_EMAIL,
|
|
[staff.email],
|
|
html_message=message,
|
|
fail_silently=False
|
|
)
|
|
|
|
# Log the action
|
|
AuditService.log_from_request(
|
|
event_type='other',
|
|
description=f"Credentials email sent to {staff.email} for staff member {staff.get_full_name()}",
|
|
request=request,
|
|
content_object=staff
|
|
)
|
|
|
|
@staticmethod
|
|
def get_staff_type_role(staff_type):
|
|
"""
|
|
Map staff_type to role name.
|
|
Currently all staff get the 'staff' role.
|
|
"""
|
|
role_mapping = {
|
|
'physician': 'staff',
|
|
'nurse': 'staff',
|
|
'admin': 'staff',
|
|
'other': 'staff'
|
|
}
|
|
return role_mapping.get(staff_type, 'staff')
|