""" Views for Insurance Approvals app. """ from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required from django.contrib import messages from django.db.models import Q, Count, Case, When, IntegerField from django.utils import timezone from django.http import JsonResponse, HttpResponse from django.contrib.contenttypes.models import ContentType from datetime import timedelta from core.mixins import TenantRequiredMixin from django.views.generic import ListView, DetailView, CreateView, UpdateView from django.urls import reverse_lazy from .models import ( InsuranceApprovalRequest, ApprovalDocument, ApprovalCommunicationLog, ApprovalTemplate, ApprovalStatusHistory ) from .forms import ( InsuranceApprovalRequestForm, ApprovalStatusUpdateForm, ApprovalDocumentForm, ApprovalCommunicationForm, ApprovalTemplateForm, ApprovalSearchForm ) # ============================================================================ # Dashboard & List Views # ============================================================================ class ApprovalDashboardView(TenantRequiredMixin, ListView): """Dashboard view showing approval statistics and recent requests.""" model = InsuranceApprovalRequest template_name = 'insurance_approvals/dashboard.html' context_object_name = 'recent_requests' paginate_by = 10 def get_queryset(self): return InsuranceApprovalRequest.objects.filter( tenant=self.request.tenant ).select_related( 'patient', 'insurance_info', 'requesting_provider', 'assigned_to' ).order_by('-created_at')[:10] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) tenant = self.request.tenant # Get statistics all_requests = InsuranceApprovalRequest.objects.filter(tenant=tenant) context['stats'] = { 'total': all_requests.count(), 'pending': all_requests.filter(status='PENDING_SUBMISSION').count(), 'submitted': all_requests.filter(status='SUBMITTED').count(), 'under_review': all_requests.filter(status='UNDER_REVIEW').count(), 'approved': all_requests.filter(status='APPROVED').count(), 'denied': all_requests.filter(status='DENIED').count(), 'urgent': all_requests.filter(is_urgent=True, status__in=['PENDING_SUBMISSION', 'SUBMITTED', 'UNDER_REVIEW']).count(), 'expiring_soon': all_requests.filter( expiration_date__lte=timezone.now().date() + timedelta(days=30), expiration_date__gte=timezone.now().date(), status__in=['APPROVED', 'PARTIALLY_APPROVED'] ).count(), } # Status breakdown context['status_breakdown'] = all_requests.values('status').annotate( count=Count('id') ).order_by('-count') # Priority breakdown context['priority_breakdown'] = all_requests.values('priority').annotate( count=Count('id') ).order_by('-count') # Request type breakdown context['type_breakdown'] = all_requests.values('request_type').annotate( count=Count('id') ).order_by('-count') # My assignments if self.request.user.is_authenticated: context['my_assignments'] = all_requests.filter( assigned_to=self.request.user, status__in=['SUBMITTED', 'UNDER_REVIEW', 'MORE_INFO_REQUIRED'] ).count() return context class ApprovalListView(TenantRequiredMixin, ListView): """List view with filtering and search.""" model = InsuranceApprovalRequest template_name = 'insurance_approvals/approval_list.html' context_object_name = 'approvals' paginate_by = 25 def get_queryset(self): queryset = InsuranceApprovalRequest.objects.filter( tenant=self.request.tenant ).select_related( 'patient', 'insurance_info', 'requesting_provider', 'assigned_to' ).prefetch_related('documents', 'status_history') # Apply filters from search form form = ApprovalSearchForm(self.request.GET) if form.is_valid(): if form.cleaned_data.get('status'): queryset = queryset.filter(status__in=form.cleaned_data['status']) if form.cleaned_data.get('priority'): queryset = queryset.filter(priority__in=form.cleaned_data['priority']) if form.cleaned_data.get('request_type'): queryset = queryset.filter(request_type__in=form.cleaned_data['request_type']) if form.cleaned_data.get('patient_name'): queryset = queryset.filter( Q(patient__first_name__icontains=form.cleaned_data['patient_name']) | Q(patient__last_name__icontains=form.cleaned_data['patient_name']) ) if form.cleaned_data.get('approval_number'): queryset = queryset.filter(approval_number__icontains=form.cleaned_data['approval_number']) if form.cleaned_data.get('authorization_number'): queryset = queryset.filter(authorization_number__icontains=form.cleaned_data['authorization_number']) if form.cleaned_data.get('date_from'): queryset = queryset.filter(created_at__date__gte=form.cleaned_data['date_from']) if form.cleaned_data.get('date_to'): queryset = queryset.filter(created_at__date__lte=form.cleaned_data['date_to']) if form.cleaned_data.get('expiring_soon'): queryset = queryset.filter( expiration_date__lte=timezone.now().date() + timedelta(days=30), expiration_date__gte=timezone.now().date() ) if form.cleaned_data.get('assigned_to_me'): queryset = queryset.filter(assigned_to=self.request.user) # Default ordering return queryset.order_by('-created_at') def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['search_form'] = ApprovalSearchForm(self.request.GET) return context class ApprovalDetailView(TenantRequiredMixin, DetailView): """Detailed view of an approval request.""" model = InsuranceApprovalRequest template_name = 'insurance_approvals/approval_detail.html' context_object_name = 'approval' def get_queryset(self): return InsuranceApprovalRequest.objects.filter( tenant=self.request.tenant ).select_related( 'patient', 'insurance_info', 'requesting_provider', 'assigned_to', 'submitted_by', 'created_by' ).prefetch_related( 'documents', 'status_history', 'communications' ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) approval = self.object # Forms for various actions context['status_form'] = ApprovalStatusUpdateForm() context['document_form'] = ApprovalDocumentForm() context['communication_form'] = ApprovalCommunicationForm() # Get related order if approval.content_type and approval.object_id: try: context['related_order'] = approval.content_object except: context['related_order'] = None # Timeline of events context['timeline'] = self._build_timeline(approval) return context def _build_timeline(self, approval): """Build a timeline of all events for this approval.""" events = [] # Status changes for history in approval.status_history.all(): events.append({ 'type': 'status_change', 'timestamp': history.changed_at, 'user': history.changed_by, 'data': history }) # Communications for comm in approval.communications.all(): events.append({ 'type': 'communication', 'timestamp': comm.communicated_at, 'user': comm.communicated_by, 'data': comm }) # Documents for doc in approval.documents.all(): events.append({ 'type': 'document', 'timestamp': doc.uploaded_at, 'user': doc.uploaded_by, 'data': doc }) # Sort by timestamp events.sort(key=lambda x: x['timestamp'], reverse=True) return events # ============================================================================ # Create & Update Views # ============================================================================ class ApprovalCreateView(TenantRequiredMixin, CreateView): """Create a new approval request.""" model = InsuranceApprovalRequest form_class = InsuranceApprovalRequestForm template_name = 'insurance_approvals/approval_form.html' success_url = reverse_lazy('insurance_approvals:list') def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['tenant'] = self.request.tenant kwargs['user'] = self.request.user # If creating from an order, get the order object if 'content_type_id' in self.request.GET and 'object_id' in self.request.GET: try: content_type = ContentType.objects.get(pk=self.request.GET['content_type_id']) order = content_type.get_object_for_this_type(pk=self.request.GET['object_id']) kwargs['order'] = order except: pass return kwargs def form_valid(self, form): messages.success(self.request, 'Insurance approval request created successfully.') return super().form_valid(form) class ApprovalUpdateView(TenantRequiredMixin, UpdateView): """Update an existing approval request.""" model = InsuranceApprovalRequest form_class = InsuranceApprovalRequestForm template_name = 'insurance_approvals/approval_form.html' def get_queryset(self): return InsuranceApprovalRequest.objects.filter(tenant=self.request.tenant) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['tenant'] = self.request.tenant kwargs['user'] = self.request.user return kwargs def get_success_url(self): return reverse_lazy('insurance_approvals:detail', kwargs={'pk': self.object.pk}) def form_valid(self, form): messages.success(self.request, 'Approval request updated successfully.') return super().form_valid(form) # ============================================================================ # HTMX Views # ============================================================================ @login_required def htmx_update_status(request, pk): """HTMX endpoint to update approval status.""" approval = get_object_or_404( InsuranceApprovalRequest, pk=pk, tenant=request.tenant ) if request.method == 'POST': form = ApprovalStatusUpdateForm(request.POST) if form.is_valid(): # Update status old_status = approval.status approval.status = form.cleaned_data['status'] # Update approval-specific fields if form.cleaned_data.get('authorization_number'): approval.authorization_number = form.cleaned_data['authorization_number'] if form.cleaned_data.get('approved_quantity'): approval.approved_quantity = form.cleaned_data['approved_quantity'] if form.cleaned_data.get('effective_date'): approval.effective_date = form.cleaned_data['effective_date'] if form.cleaned_data.get('expiration_date'): approval.expiration_date = form.cleaned_data['expiration_date'] # Update denial-specific fields if form.cleaned_data.get('denial_reason'): approval.denial_reason = form.cleaned_data['denial_reason'] if form.cleaned_data.get('denial_code'): approval.denial_code = form.cleaned_data['denial_code'] approval.save() # Create status history (signal will handle this, but we can also do it manually) ApprovalStatusHistory.objects.create( approval_request=approval, from_status=old_status, to_status=approval.status, reason=form.cleaned_data.get('reason'), notes=form.cleaned_data.get('notes'), changed_by=request.user ) messages.success(request, f'Status updated to {approval.get_status_display()}') # Return updated status badge return render(request, 'insurance_approvals/partials/status_badge.html', { 'approval': approval }) form = ApprovalStatusUpdateForm() return render(request, 'insurance_approvals/partials/status_update_form.html', { 'form': form, 'approval': approval }) @login_required def htmx_upload_document(request, pk): """HTMX endpoint to upload a document.""" approval = get_object_or_404( InsuranceApprovalRequest, pk=pk, tenant=request.tenant ) if request.method == 'POST': form = ApprovalDocumentForm(request.POST, request.FILES) if form.is_valid(): document = form.save(commit=False) document.approval_request = approval document.uploaded_by = request.user document.save() messages.success(request, 'Document uploaded successfully.') # Return updated document list return render(request, 'insurance_approvals/partials/document_list.html', { 'approval': approval }) form = ApprovalDocumentForm() return render(request, 'insurance_approvals/partials/document_upload_form.html', { 'form': form, 'approval': approval }) @login_required def htmx_log_communication(request, pk): """HTMX endpoint to log a communication.""" approval = get_object_or_404( InsuranceApprovalRequest, pk=pk, tenant=request.tenant ) if request.method == 'POST': form = ApprovalCommunicationForm(request.POST) if form.is_valid(): communication = form.save(commit=False) communication.approval_request = approval communication.communicated_by = request.user communication.save() # Update last contact info on approval approval.last_contact_date = communication.communicated_at approval.last_contact_method = communication.communication_type approval.last_contact_notes = communication.message[:500] approval.save() messages.success(request, 'Communication logged successfully.') # Return updated communication list return render(request, 'insurance_approvals/partials/communication_list.html', { 'approval': approval }) form = ApprovalCommunicationForm() return render(request, 'insurance_approvals/partials/communication_form.html', { 'form': form, 'approval': approval }) @login_required def htmx_dashboard_stats(request): """HTMX endpoint for real-time dashboard statistics.""" tenant = request.tenant all_requests = InsuranceApprovalRequest.objects.filter(tenant=tenant) stats = { 'total': all_requests.count(), 'pending': all_requests.filter(status='PENDING_SUBMISSION').count(), 'submitted': all_requests.filter(status='SUBMITTED').count(), 'under_review': all_requests.filter(status='UNDER_REVIEW').count(), 'approved': all_requests.filter(status='APPROVED').count(), 'denied': all_requests.filter(status='DENIED').count(), 'urgent': all_requests.filter(is_urgent=True, status__in=['PENDING_SUBMISSION', 'SUBMITTED', 'UNDER_REVIEW']).count(), 'expiring_soon': all_requests.filter( expiration_date__lte=timezone.now().date() + timedelta(days=30), expiration_date__gte=timezone.now().date(), status__in=['APPROVED', 'PARTIALLY_APPROVED'] ).count(), } return render(request, 'insurance_approvals/partials/dashboard_stats.html', {'stats': stats}) # ============================================================================ # Utility Views # ============================================================================ @login_required def create_from_order(request, content_type_id, object_id): """Create an approval request from an existing order.""" try: content_type = ContentType.objects.get(pk=content_type_id) order = content_type.get_object_for_this_type(pk=object_id) # Redirect to create view with order info return redirect(f"{reverse_lazy('insurance_approvals:create')}?content_type_id={content_type_id}&object_id={object_id}") except: messages.error(request, 'Could not find the specified order.') return redirect('insurance_approvals:list') @login_required def submit_approval(request, pk): """Submit an approval request to insurance.""" approval = get_object_or_404( InsuranceApprovalRequest, pk=pk, tenant=request.tenant ) if approval.status == 'DRAFT': approval.status = 'PENDING_SUBMISSION' approval.submitted_by = request.user approval.submitted_date = timezone.now() approval.save() messages.success(request, 'Approval request submitted successfully.') else: messages.warning(request, 'This approval request has already been submitted.') return redirect('insurance_approvals:detail', pk=pk)