2025-10-03 20:11:25 +03:00

344 lines
11 KiB
Python

"""
Admin configuration for Insurance Approvals app.
"""
from django.contrib import admin
from django.utils.html import format_html
from django.urls import reverse
from django.utils import timezone
from .models import (
InsuranceApprovalRequest,
ApprovalDocument,
ApprovalStatusHistory,
ApprovalCommunicationLog,
ApprovalTemplate
)
class ApprovalDocumentInline(admin.TabularInline):
model = ApprovalDocument
extra = 0
fields = ('document_type', 'title', 'file', 'file_size_mb', 'uploaded_at', 'uploaded_by')
readonly_fields = ('file_size_mb', 'uploaded_at', 'uploaded_by')
def file_size_mb(self, obj):
if obj.file_size:
return f"{obj.file_size_mb} MB"
return "-"
file_size_mb.short_description = 'File Size'
class ApprovalStatusHistoryInline(admin.TabularInline):
model = ApprovalStatusHistory
extra = 0
fields = ('from_status', 'to_status', 'reason', 'changed_at', 'changed_by')
readonly_fields = ('changed_at', 'changed_by')
can_delete = False
class ApprovalCommunicationLogInline(admin.TabularInline):
model = ApprovalCommunicationLog
extra = 0
fields = ('communication_type', 'subject', 'contact_person', 'communicated_at', 'follow_up_required')
readonly_fields = ('communicated_at',)
@admin.register(InsuranceApprovalRequest)
class InsuranceApprovalRequestAdmin(admin.ModelAdmin):
list_display = (
'approval_number',
'patient_link',
'request_type',
'status_badge',
'priority_badge',
'insurance_company',
'submitted_date',
'decision_date',
'expiration_status',
'assigned_to'
)
list_filter = (
'status',
'priority',
'request_type',
'is_urgent',
'is_expedited',
'requires_peer_review',
'submitted_date',
'decision_date'
)
search_fields = (
'approval_number',
'authorization_number',
'reference_number',
'patient__first_name',
'patient__last_name',
'patient__mrn',
'insurance_info__insurance_company',
'service_description'
)
readonly_fields = (
'approval_id',
'approval_number',
'created_at',
'updated_at',
'created_by',
'turnaround_time_days',
'days_until_expiry'
)
fieldsets = (
('Identification', {
'fields': ('approval_id', 'approval_number', 'tenant')
}),
('Patient & Insurance', {
'fields': ('patient', 'insurance_info')
}),
('Order Information', {
'fields': ('content_type', 'object_id', 'request_type', 'service_description')
}),
('Medical Codes', {
'fields': ('procedure_codes', 'diagnosis_codes')
}),
('Clinical Information', {
'fields': ('clinical_justification', 'medical_necessity', 'alternative_treatments_tried')
}),
('Requested Services', {
'fields': (
'requested_quantity',
'requested_visits',
'requested_units',
'service_start_date',
'service_end_date'
)
}),
('Status & Priority', {
'fields': ('status', 'priority', 'is_urgent', 'is_expedited', 'requires_peer_review')
}),
('Submission', {
'fields': ('submission_method', 'submitted_date', 'submitted_by')
}),
('Insurance Response', {
'fields': (
'decision_date',
'authorization_number',
'reference_number',
'approved_quantity',
'approved_visits',
'approved_units',
'approved_amount',
'effective_date',
'expiration_date'
)
}),
('Denial & Appeal', {
'fields': ('denial_reason', 'denial_code', 'appeal_date', 'appeal_reason', 'appeal_deadline'),
'classes': ('collapse',)
}),
('Assignment & Tracking', {
'fields': ('assigned_to', 'requesting_provider')
}),
('Communication', {
'fields': ('last_contact_date', 'last_contact_method', 'last_contact_notes'),
'classes': ('collapse',)
}),
('Notes', {
'fields': ('internal_notes', 'insurance_notes'),
'classes': ('collapse',)
}),
('Metadata', {
'fields': ('created_at', 'updated_at', 'created_by', 'turnaround_time_days', 'days_until_expiry'),
'classes': ('collapse',)
})
)
inlines = [ApprovalDocumentInline, ApprovalStatusHistoryInline, ApprovalCommunicationLogInline]
def patient_link(self, obj):
url = reverse('admin:patients_patientprofile_change', args=[obj.patient.id])
return format_html('<a href="{}">{}</a>', url, obj.patient.get_full_name())
patient_link.short_description = 'Patient'
def insurance_company(self, obj):
return obj.insurance_info.insurance_company
insurance_company.short_description = 'Insurance'
def status_badge(self, obj):
colors = {
'DRAFT': 'gray',
'PENDING_SUBMISSION': 'orange',
'SUBMITTED': 'blue',
'UNDER_REVIEW': 'blue',
'ADDITIONAL_INFO_REQUESTED': 'orange',
'APPROVED': 'green',
'PARTIALLY_APPROVED': 'lightgreen',
'DENIED': 'red',
'APPEALED': 'purple',
'APPEAL_APPROVED': 'green',
'APPEAL_DENIED': 'red',
'EXPIRED': 'darkgray',
'CANCELLED': 'gray'
}
color = colors.get(obj.status, 'gray')
return format_html(
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
color,
obj.get_status_display()
)
status_badge.short_description = 'Status'
def priority_badge(self, obj):
colors = {
'ROUTINE': 'green',
'URGENT': 'orange',
'STAT': 'red',
'EMERGENCY': 'darkred'
}
color = colors.get(obj.priority, 'gray')
return format_html(
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
color,
obj.get_priority_display()
)
priority_badge.short_description = 'Priority'
def expiration_status(self, obj):
if not obj.expiration_date:
return '-'
if obj.is_expired:
return format_html('<span style="color: red;">Expired</span>')
elif obj.is_expiring_soon:
days = obj.days_until_expiry
return format_html('<span style="color: orange;">Expires in {} days</span>', days)
else:
return format_html('<span style="color: green;">Valid</span>')
expiration_status.short_description = 'Expiration'
def save_model(self, request, obj, form, change):
if not change:
obj.created_by = request.user
super().save_model(request, obj, form, change)
@admin.register(ApprovalDocument)
class ApprovalDocumentAdmin(admin.ModelAdmin):
list_display = ('title', 'approval_request', 'document_type', 'file_size_mb', 'uploaded_at', 'uploaded_by')
list_filter = ('document_type', 'uploaded_at')
search_fields = ('title', 'description', 'approval_request__approval_number')
readonly_fields = ('document_id', 'file_size_mb', 'uploaded_at', 'uploaded_by')
def save_model(self, request, obj, form, change):
if not change:
obj.uploaded_by = request.user
if obj.file:
obj.file_size = obj.file.size
obj.mime_type = obj.file.content_type or 'application/octet-stream'
super().save_model(request, obj, form, change)
@admin.register(ApprovalStatusHistory)
class ApprovalStatusHistoryAdmin(admin.ModelAdmin):
list_display = ('approval_request', 'from_status', 'to_status', 'changed_at', 'changed_by')
list_filter = ('to_status', 'changed_at')
search_fields = ('approval_request__approval_number', 'reason', 'notes')
readonly_fields = ('changed_at', 'changed_by')
def save_model(self, request, obj, form, change):
if not change:
obj.changed_by = request.user
super().save_model(request, obj, form, change)
@admin.register(ApprovalCommunicationLog)
class ApprovalCommunicationLogAdmin(admin.ModelAdmin):
list_display = (
'approval_request',
'communication_type',
'subject',
'contact_person',
'communicated_at',
'follow_up_badge'
)
list_filter = ('communication_type', 'follow_up_required', 'communicated_at')
search_fields = (
'approval_request__approval_number',
'subject',
'message',
'contact_person'
)
readonly_fields = ('communication_id', 'communicated_at', 'communicated_by')
fieldsets = (
('Approval Request', {
'fields': ('approval_request',)
}),
('Communication Details', {
'fields': (
'communication_id',
'communication_type',
'contact_person',
'contact_number',
'subject',
'message',
'response'
)
}),
('Outcome & Follow-up', {
'fields': ('outcome', 'follow_up_required', 'follow_up_date')
}),
('Metadata', {
'fields': ('communicated_at', 'communicated_by')
})
)
def follow_up_badge(self, obj):
if obj.follow_up_required:
if obj.follow_up_date and obj.follow_up_date < timezone.now().date():
return format_html('<span style="color: red;">Overdue</span>')
return format_html('<span style="color: orange;">Required</span>')
return format_html('<span style="color: green;">None</span>')
follow_up_badge.short_description = 'Follow-up'
def save_model(self, request, obj, form, change):
if not change:
obj.communicated_by = request.user
super().save_model(request, obj, form, change)
@admin.register(ApprovalTemplate)
class ApprovalTemplateAdmin(admin.ModelAdmin):
list_display = ('name', 'request_type', 'insurance_company', 'is_active', 'usage_count', 'created_at')
list_filter = ('request_type', 'is_active', 'created_at')
search_fields = ('name', 'description', 'insurance_company')
readonly_fields = ('template_id', 'usage_count', 'created_at', 'updated_at', 'created_by')
fieldsets = (
('Template Information', {
'fields': ('template_id', 'name', 'description', 'tenant')
}),
('Scope', {
'fields': ('request_type', 'insurance_company')
}),
('Template Content', {
'fields': (
'clinical_justification_template',
'medical_necessity_template',
'required_documents',
'required_codes'
)
}),
('Status & Usage', {
'fields': ('is_active', 'usage_count')
}),
('Metadata', {
'fields': ('created_at', 'updated_at', 'created_by'),
'classes': ('collapse',)
})
)
def save_model(self, request, obj, form, change):
if not change:
obj.created_by = request.user
super().save_model(request, obj, form, change)