agdar/referrals/models.py
Marwan Alwali 2f1681b18c update
2025-11-11 13:44:48 +03:00

250 lines
7.6 KiB
Python

"""
Referrals models for cross-clinic patient referrals.
This module handles referrals between departments/clinics,
referral tracking, and reception notifications.
"""
from django.db import models
from django.utils.translation import gettext_lazy as _
from simple_history.models import HistoricalRecords
from core.models import (
UUIDPrimaryKeyMixin,
TimeStampedMixin,
TenantOwnedMixin,
)
class Referral(UUIDPrimaryKeyMixin, TimeStampedMixin, TenantOwnedMixin):
"""
Patient referrals between clinics/departments.
Tracks referral workflow from initiation to appointment booking.
"""
class Status(models.TextChoices):
PENDING = 'PENDING', _('Pending')
ACKNOWLEDGED = 'ACKNOWLEDGED', _('Acknowledged')
APPOINTMENT_BOOKED = 'APPOINTMENT_BOOKED', _('Appointment Booked')
COMPLETED = 'COMPLETED', _('Completed')
DECLINED = 'DECLINED', _('Declined')
CANCELLED = 'CANCELLED', _('Cancelled')
class Priority(models.TextChoices):
ROUTINE = 'ROUTINE', _('Routine')
URGENT = 'URGENT', _('Urgent')
EMERGENCY = 'EMERGENCY', _('Emergency')
class ReferralType(models.TextChoices):
INITIAL_ASSESSMENT = 'INITIAL_ASSESSMENT', _('Initial Assessment')
CONSULTATION = 'CONSULTATION', _('Consultation')
FOLLOW_UP = 'FOLLOW_UP', _('Follow-up')
SECOND_OPINION = 'SECOND_OPINION', _('Second Opinion')
# Patient & Clinics
patient = models.ForeignKey(
'core.Patient',
on_delete=models.CASCADE,
related_name='referrals',
verbose_name=_("Patient")
)
from_clinic = models.ForeignKey(
'core.Clinic',
on_delete=models.CASCADE,
related_name='outgoing_referrals',
verbose_name=_("From Clinic")
)
to_clinic = models.ForeignKey(
'core.Clinic',
on_delete=models.CASCADE,
related_name='incoming_referrals',
verbose_name=_("To Clinic")
)
# Referral Details
referral_type = models.CharField(
max_length=30,
choices=ReferralType.choices,
default=ReferralType.CONSULTATION,
verbose_name=_("Referral Type")
)
priority = models.CharField(
max_length=15,
choices=Priority.choices,
default=Priority.ROUTINE,
verbose_name=_("Priority")
)
reason = models.TextField(
verbose_name=_("Reason for Referral"),
help_text=_("Clinical reason for referring to another department")
)
clinical_notes = models.TextField(
blank=True,
verbose_name=_("Clinical Notes"),
help_text=_("Additional clinical information for receiving clinic")
)
# Workflow
referred_by = models.ForeignKey(
'core.User',
on_delete=models.SET_NULL,
null=True,
related_name='made_referrals',
verbose_name=_("Referred By")
)
referred_to = models.ForeignKey(
'core.User',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='received_referrals',
verbose_name=_("Referred To"),
help_text=_("Specific therapist if known")
)
# Status Tracking
status = models.CharField(
max_length=30,
choices=Status.choices,
default=Status.PENDING,
verbose_name=_("Status")
)
acknowledged_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("Acknowledged At")
)
acknowledged_by = models.ForeignKey(
'core.User',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='acknowledged_referrals',
verbose_name=_("Acknowledged By")
)
# Appointment Linkage
appointment = models.ForeignKey(
'appointments.Appointment',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='referrals',
verbose_name=_("Appointment"),
help_text=_("Appointment booked for this referral")
)
appointment_booked_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("Appointment Booked At")
)
appointment_booked_by = models.ForeignKey(
'core.User',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='booked_referral_appointments',
verbose_name=_("Appointment Booked By")
)
# Completion
completed_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("Completed At")
)
outcome_notes = models.TextField(
blank=True,
verbose_name=_("Outcome Notes"),
help_text=_("Notes about the referral outcome")
)
# Decline/Cancel
declined_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("Declined At")
)
declined_by = models.ForeignKey(
'core.User',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='declined_referrals',
verbose_name=_("Declined By")
)
decline_reason = models.TextField(
blank=True,
verbose_name=_("Decline Reason")
)
history = HistoricalRecords()
class Meta:
verbose_name = _("Referral")
verbose_name_plural = _("Referrals")
ordering = ['-created_at']
indexes = [
models.Index(fields=['patient', 'status']),
models.Index(fields=['from_clinic', 'status']),
models.Index(fields=['to_clinic', 'status']),
models.Index(fields=['referred_by', 'status']),
models.Index(fields=['status', 'created_at']),
models.Index(fields=['tenant', 'status']),
]
def __str__(self):
return f"Referral: {self.patient} from {self.from_clinic.name_en} to {self.to_clinic.name_en}"
def acknowledge(self, user):
"""Acknowledge receipt of referral."""
from django.utils import timezone
self.status = self.Status.ACKNOWLEDGED
self.acknowledged_at = timezone.now()
self.acknowledged_by = user
self.save()
def book_appointment(self, appointment, user):
"""Link appointment to referral."""
from django.utils import timezone
self.appointment = appointment
self.status = self.Status.APPOINTMENT_BOOKED
self.appointment_booked_at = timezone.now()
self.appointment_booked_by = user
self.save()
def complete(self, outcome_notes=""):
"""Mark referral as completed."""
from django.utils import timezone
self.status = self.Status.COMPLETED
self.completed_at = timezone.now()
self.outcome_notes = outcome_notes
self.save()
def decline(self, user, reason):
"""Decline the referral."""
from django.utils import timezone
self.status = self.Status.DECLINED
self.declined_at = timezone.now()
self.declined_by = user
self.decline_reason = reason
self.save()
@classmethod
def get_pending_for_clinic(cls, clinic, tenant):
"""Get pending referrals for a clinic."""
return cls.objects.filter(
to_clinic=clinic,
tenant=tenant,
status=cls.Status.PENDING
).select_related('patient', 'from_clinic', 'referred_by')
@classmethod
def get_pending_for_reception(cls, tenant):
"""Get referrals pending appointment booking."""
return cls.objects.filter(
tenant=tenant,
status__in=[cls.Status.PENDING, cls.Status.ACKNOWLEDGED],
appointment__isnull=True
).select_related('patient', 'from_clinic', 'to_clinic', 'referred_by')