""" Billing app admin configuration. """ from django.contrib import admin from django.utils.html import format_html from django.urls import reverse from django.utils.safestring import mark_safe from decimal import Decimal from .models import ( MedicalBill, BillLineItem, InsuranceClaim, Payment, ClaimStatusUpdate, BillingConfiguration ) class BillLineItemInline(admin.TabularInline): """ Inline admin for bill line items. """ model = BillLineItem extra = 0 fields = [ 'line_number', 'service_code', 'service_description', 'quantity', 'unit_price', 'total_price', 'status' ] readonly_fields = ['total_price'] class InsuranceClaimInline(admin.TabularInline): """ Inline admin for insurance claims. """ model = InsuranceClaim extra = 0 fields = [ 'claim_number', 'claim_type', 'insurance_info', 'billed_amount', 'paid_amount', 'status' ] readonly_fields = ['claim_number'] class PaymentInline(admin.TabularInline): """ Inline admin for payments. """ model = Payment extra = 0 fields = [ 'payment_number', 'payment_date', 'payment_amount', 'payment_method', 'payment_source', 'status' ] readonly_fields = ['payment_number'] @admin.register(MedicalBill) class MedicalBillAdmin(admin.ModelAdmin): """ Admin interface for medical bills. """ list_display = [ 'bill_number', 'patient_name', 'bill_type', 'bill_date', 'total_amount', 'paid_amount', 'balance_amount', 'status', 'days_outstanding', 'payment_percentage_display' ] list_filter = [ 'tenant', 'bill_type', 'status', 'bill_date', 'payment_terms', 'collection_status' ] search_fields = [ 'bill_number', 'patient__first_name', 'patient__last_name', 'patient__mrn', 'attending_provider__first_name', 'attending_provider__last_name' ] readonly_fields = [ 'bill_id', 'bill_number', 'total_amount', 'balance_amount', 'days_outstanding', 'payment_percentage', 'is_overdue', 'created_at', 'updated_at' ] fieldsets = [ ('Bill Information', { 'fields': [ 'bill_id', 'bill_number', 'tenant' ] }), ('Patient Information', { 'fields': [ 'patient' ] }), ('Bill Type and Dates', { 'fields': [ 'bill_type', 'service_date_from', 'service_date_to', 'bill_date', 'due_date' ] }), ('Financial Information', { 'fields': [ 'subtotal', 'tax_amount', 'discount_amount', 'adjustment_amount', 'total_amount', 'paid_amount', 'balance_amount' ] }), ('Insurance Information', { 'fields': [ 'primary_insurance', 'secondary_insurance' ] }), ('Bill Status', { 'fields': [ 'status', 'payment_terms' ] }), ('Provider Information', { 'fields': [ 'attending_provider', 'billing_provider' ] }), ('Related Information', { 'fields': [ 'encounter', 'admission' ], 'classes': ['collapse'] }), ('Collection Information', { 'fields': [ 'collection_status', 'last_statement_date' ], 'classes': ['collapse'] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Status Information', { 'fields': [ 'days_outstanding', 'payment_percentage', 'is_overdue' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] inlines = [BillLineItemInline, InsuranceClaimInline, PaymentInline] date_hierarchy = 'bill_date' def patient_name(self, obj): """Display patient name.""" return obj.patient.get_full_name() patient_name.short_description = 'Patient' def payment_percentage_display(self, obj): """Display payment percentage with color coding.""" percentage = obj.payment_percentage if percentage >= 100: color = 'green' elif percentage >= 50: color = 'orange' else: color = 'red' return format_html( '{:.1f}%', color, percentage ) payment_percentage_display.short_description = 'Paid %' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(tenant=request.user.tenant) return qs.select_related('patient', 'attending_provider') @admin.register(BillLineItem) class BillLineItemAdmin(admin.ModelAdmin): """ Admin interface for bill line items. """ list_display = [ 'bill_number', 'line_number', 'service_code', 'service_description_short', 'service_date', 'quantity', 'unit_price', 'total_price', 'rendering_provider_name', 'status' ] list_filter = [ 'medical_bill__tenant', 'service_category', 'status', 'service_date', 'place_of_service' ] search_fields = [ 'medical_bill__bill_number', 'service_code', 'service_description', 'rendering_provider__first_name', 'rendering_provider__last_name' ] readonly_fields = [ 'line_item_id', 'total_price', 'patient', 'tenant', 'created_at', 'updated_at' ] fieldsets = [ ('Line Item Information', { 'fields': [ 'line_item_id', 'medical_bill', 'line_number' ] }), ('Service Information', { 'fields': [ 'service_date', 'service_code', 'service_description', 'service_category' ] }), ('Quantity and Pricing', { 'fields': [ 'quantity', 'unit_of_measure', 'unit_price', 'total_price' ] }), ('Modifiers', { 'fields': [ 'modifier_1', 'modifier_2', 'modifier_3', 'modifier_4' ], 'classes': ['collapse'] }), ('Diagnosis Information', { 'fields': [ 'primary_diagnosis', 'secondary_diagnoses' ], 'classes': ['collapse'] }), ('Provider Information', { 'fields': [ 'rendering_provider', 'supervising_provider' ] }), ('Place of Service', { 'fields': [ 'place_of_service', 'revenue_code' ], 'classes': ['collapse'] }), ('Drug Information', { 'fields': [ 'ndc_code', 'drug_quantity', 'drug_unit' ], 'classes': ['collapse'] }), ('Line Item Status', { 'fields': [ 'status' ] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Related Information', { 'fields': [ 'patient', 'tenant' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at' ], 'classes': ['collapse'] }) ] def bill_number(self, obj): """Display bill number.""" return obj.medical_bill.bill_number bill_number.short_description = 'Bill' def service_description_short(self, obj): """Display shortened service description.""" if obj.service_description: return obj.service_description[:50] + "..." if len(obj.service_description) > 50 else obj.service_description return "-" service_description_short.short_description = 'Service' def rendering_provider_name(self, obj): """Display rendering provider name.""" return obj.rendering_provider.get_full_name() if obj.rendering_provider else "-" rendering_provider_name.short_description = 'Provider' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(medical_bill__tenant=request.user.tenant) return qs.select_related('medical_bill', 'rendering_provider') @admin.register(InsuranceClaim) class InsuranceClaimAdmin(admin.ModelAdmin): """ Admin interface for insurance claims. """ list_display = [ 'claim_number', 'patient_name', 'insurance_company', 'claim_type', 'submission_date', 'billed_amount', 'paid_amount', 'status', 'days_pending', 'payment_percentage_display' ] list_filter = [ 'medical_bill__tenant', 'claim_type', 'status', 'submission_date', 'response_date' ] search_fields = [ 'claim_number', 'medical_bill__bill_number', 'medical_bill__patient__first_name', 'medical_bill__patient__last_name', 'insurance_info__insurance_company' ] readonly_fields = [ 'claim_id', 'claim_number', 'patient', 'tenant', 'days_pending', 'payment_percentage', 'created_at', 'updated_at' ] fieldsets = [ ('Claim Information', { 'fields': [ 'claim_id', 'claim_number', 'medical_bill' ] }), ('Insurance Information', { 'fields': [ 'insurance_info', 'claim_type' ] }), ('Claim Dates', { 'fields': [ 'submission_date', 'service_date_from', 'service_date_to' ] }), ('Financial Information', { 'fields': [ 'billed_amount', 'allowed_amount', 'paid_amount', 'patient_responsibility', 'deductible_amount', 'coinsurance_amount', 'copay_amount' ] }), ('Claim Status', { 'fields': [ 'status' ] }), ('Processing Information', { 'fields': [ 'clearinghouse', 'batch_number' ], 'classes': ['collapse'] }), ('Response Information', { 'fields': [ 'response_date', 'check_number', 'check_date' ], 'classes': ['collapse'] }), ('Denial Information', { 'fields': [ 'denial_reason', 'denial_code' ], 'classes': ['collapse'] }), ('Prior Authorization', { 'fields': [ 'prior_auth_number' ], 'classes': ['collapse'] }), ('Resubmission Information', { 'fields': [ 'original_claim', 'resubmission_count' ], 'classes': ['collapse'] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Status Information', { 'fields': [ 'days_pending', 'payment_percentage' ], 'classes': ['collapse'] }), ('Related Information', { 'fields': [ 'patient', 'tenant' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] inlines = [] date_hierarchy = 'submission_date' def patient_name(self, obj): """Display patient name.""" return obj.patient.get_full_name() patient_name.short_description = 'Patient' def insurance_company(self, obj): """Display insurance company.""" return obj.insurance_info.insurance_company insurance_company.short_description = 'Insurance' def payment_percentage_display(self, obj): """Display payment percentage with color coding.""" percentage = obj.payment_percentage if percentage >= 100: color = 'green' elif percentage >= 50: color = 'orange' else: color = 'red' return format_html( '{}%', color, percentage ) payment_percentage_display.short_description = 'Paid %' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(medical_bill__tenant=request.user.tenant) return qs.select_related('medical_bill__patient', 'insurance_info') @admin.register(Payment) class PaymentAdmin(admin.ModelAdmin): """ Admin interface for payments. """ list_display = [ 'payment_number', 'patient_name', 'payment_date', 'payment_amount', 'payment_method', 'payment_source', 'status', 'received_by_name' ] list_filter = [ 'medical_bill__tenant', 'payment_method', 'payment_source', 'status', 'payment_date', 'deposit_date' ] search_fields = [ 'payment_number', 'medical_bill__bill_number', 'medical_bill__patient__first_name', 'medical_bill__patient__last_name', 'check_number', 'authorization_code' ] readonly_fields = [ 'payment_id', 'payment_number', 'patient', 'tenant', 'net_payment', 'created_at', 'updated_at' ] fieldsets = [ ('Payment Information', { 'fields': [ 'payment_id', 'payment_number', 'medical_bill' ] }), ('Payment Details', { 'fields': [ 'payment_date', 'payment_amount', 'payment_method', 'payment_source' ] }), ('Check Information', { 'fields': [ 'check_number', 'bank_name', 'routing_number' ], 'classes': ['collapse'] }), ('Credit Card Information', { 'fields': [ 'card_type', 'card_last_four', 'authorization_code', 'transaction_id' ], 'classes': ['collapse'] }), ('Insurance Payment Information', { 'fields': [ 'insurance_claim', 'eob_number' ], 'classes': ['collapse'] }), ('Payment Status', { 'fields': [ 'status' ] }), ('Deposit Information', { 'fields': [ 'deposit_date', 'deposit_slip' ], 'classes': ['collapse'] }), ('Refund Information', { 'fields': [ 'refund_amount', 'refund_date', 'refund_reason' ], 'classes': ['collapse'] }), ('Staff Information', { 'fields': [ 'received_by', 'processed_by' ] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Calculated Fields', { 'fields': [ 'net_payment' ], 'classes': ['collapse'] }), ('Related Information', { 'fields': [ 'patient', 'tenant' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at' ], 'classes': ['collapse'] }) ] date_hierarchy = 'payment_date' def patient_name(self, obj): """Display patient name.""" return obj.patient.get_full_name() patient_name.short_description = 'Patient' def received_by_name(self, obj): """Display received by name.""" return obj.received_by.get_full_name() if obj.received_by else "-" received_by_name.short_description = 'Received By' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(medical_bill__tenant=request.user.tenant) return qs.select_related('medical_bill__patient', 'received_by') @admin.register(ClaimStatusUpdate) class ClaimStatusUpdateAdmin(admin.ModelAdmin): """ Admin interface for claim status updates. """ list_display = [ 'claim_number', 'patient_name', 'status_date', 'previous_status', 'new_status', 'update_source', 'updated_by_name' ] list_filter = [ 'insurance_claim__medical_bill__tenant', 'new_status', 'previous_status', 'update_source', 'status_date' ] search_fields = [ 'insurance_claim__claim_number', 'insurance_claim__medical_bill__patient__first_name', 'insurance_claim__medical_bill__patient__last_name', 'response_code', 'response_message' ] readonly_fields = [ 'update_id', 'patient', 'tenant', 'created_at' ] fieldsets = [ ('Update Information', { 'fields': [ 'update_id', 'insurance_claim' ] }), ('Status Information', { 'fields': [ 'previous_status', 'new_status', 'status_date' ] }), ('Update Details', { 'fields': [ 'update_source' ] }), ('Response Information', { 'fields': [ 'response_code', 'response_message' ], 'classes': ['collapse'] }), ('Financial Updates', { 'fields': [ 'allowed_amount', 'paid_amount', 'patient_responsibility' ], 'classes': ['collapse'] }), ('Staff Information', { 'fields': [ 'updated_by' ] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Related Information', { 'fields': [ 'patient', 'tenant' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at' ], 'classes': ['collapse'] }) ] date_hierarchy = 'status_date' def claim_number(self, obj): """Display claim number.""" return obj.insurance_claim.claim_number claim_number.short_description = 'Claim' def patient_name(self, obj): """Display patient name.""" return obj.patient.get_full_name() patient_name.short_description = 'Patient' def updated_by_name(self, obj): """Display updated by name.""" return obj.updated_by.get_full_name() if obj.updated_by else "-" updated_by_name.short_description = 'Updated By' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(insurance_claim__medical_bill__tenant=request.user.tenant) return qs.select_related('insurance_claim__medical_bill__patient', 'updated_by') @admin.register(BillingConfiguration) class BillingConfigurationAdmin(admin.ModelAdmin): """ Admin interface for billing configuration. """ list_display = [ 'tenant_name', 'default_payment_terms', 'tax_rate', 'tax_exempt', 'statement_frequency', 'auto_submit_claims', 'accept_credit_cards', 'payment_portal_enabled' ] list_filter = [ 'tenant', 'default_payment_terms', 'tax_exempt', 'statement_frequency', 'auto_submit_claims', 'accept_credit_cards', 'payment_portal_enabled' ] search_fields = [ 'tenant__name', 'primary_clearinghouse', 'secondary_clearinghouse' ] readonly_fields = [ 'config_id', 'created_at', 'updated_at' ] fieldsets = [ ('Configuration Information', { 'fields': [ 'config_id', 'tenant' ] }), ('Billing Settings', { 'fields': [ 'default_payment_terms' ] }), ('Tax Settings', { 'fields': [ 'tax_rate', 'tax_exempt' ] }), ('Statement Settings', { 'fields': [ 'statement_frequency', 'statement_message' ] }), ('Collection Settings', { 'fields': [ 'first_notice_days', 'second_notice_days', 'final_notice_days', 'collections_days' ] }), ('Interest Settings', { 'fields': [ 'apply_interest', 'interest_rate' ], 'classes': ['collapse'] }), ('Payment Settings', { 'fields': [ 'accept_credit_cards', 'accept_ach', 'payment_portal_enabled' ] }), ('Claim Settings', { 'fields': [ 'auto_submit_claims', 'claim_submission_frequency' ] }), ('Clearinghouse Settings', { 'fields': [ 'primary_clearinghouse', 'secondary_clearinghouse' ], 'classes': ['collapse'] }), ('Reporting Settings', { 'fields': [ 'aging_buckets' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] def tenant_name(self, obj): """Display tenant name.""" return obj.tenant.name tenant_name.short_description = 'Organization' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(tenant=request.user.tenant) return qs.select_related('tenant') # Customize admin site admin.site.site_header = "Hospital Management System - Billing" admin.site.site_title = "Billing Admin" admin.site.index_title = "Billing Administration"