1576 lines
56 KiB
Python
1576 lines
56 KiB
Python
"""
|
||
Billing app forms for hospital management system.
|
||
Provides forms for medical billing, insurance claims, and revenue cycle management.
|
||
"""
|
||
|
||
from django import forms
|
||
from django.core.exceptions import ValidationError
|
||
from django.utils import timezone
|
||
from decimal import Decimal
|
||
from datetime import date, datetime
|
||
from .models import (
|
||
MedicalBill, BillLineItem, InsuranceClaim, Payment,
|
||
ClaimStatusUpdate, BillingConfiguration
|
||
)
|
||
from patients.models import PatientProfile, InsuranceInfo
|
||
from accounts.models import User
|
||
from emr.models import Encounter
|
||
from inpatients.models import Admission
|
||
|
||
|
||
class MedicalBillForm(forms.ModelForm):
|
||
"""
|
||
Form for creating and editing medical bills.
|
||
"""
|
||
|
||
class Meta:
|
||
model = MedicalBill
|
||
fields = [
|
||
'patient', 'bill_type', 'service_date_from', 'service_date_to',
|
||
'bill_date', 'due_date', 'attending_provider', 'billing_provider',
|
||
'primary_insurance', 'secondary_insurance', 'encounter', 'admission',
|
||
'subtotal', 'tax_amount', 'discount_amount', 'adjustment_amount',
|
||
'payment_terms', 'notes'
|
||
]
|
||
widgets = {
|
||
'patient': forms.Select(attrs={
|
||
'class': 'form-select',
|
||
'required': True
|
||
}),
|
||
'bill_type': forms.Select(attrs={
|
||
'class': 'form-select',
|
||
'required': True
|
||
}),
|
||
'service_date_from': forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date',
|
||
'required': True
|
||
}),
|
||
'service_date_to': forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date',
|
||
'required': True
|
||
}),
|
||
'bill_date': forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date',
|
||
'required': True
|
||
}),
|
||
'due_date': forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date'
|
||
}),
|
||
'attending_provider': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'billing_provider': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'primary_insurance': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'secondary_insurance': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'encounter': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'admission': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'subtotal': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0'
|
||
}),
|
||
'tax_amount': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0'
|
||
}),
|
||
'discount_amount': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0'
|
||
}),
|
||
'adjustment_amount': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01'
|
||
}),
|
||
'payment_terms': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'notes': forms.Textarea(attrs={
|
||
'class': 'form-control',
|
||
'rows': 4,
|
||
'placeholder': 'Additional notes about this bill...'
|
||
})
|
||
}
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
self.user = kwargs.pop('user', None)
|
||
super().__init__(*args, **kwargs)
|
||
|
||
if self.user and hasattr(self.user, 'tenant'):
|
||
# Filter choices by tenant
|
||
self.fields['patient'].queryset = PatientProfile.objects.filter(
|
||
tenant=self.user.tenant
|
||
)
|
||
self.fields['attending_provider'].queryset = User.objects.filter(
|
||
tenant=self.user.tenant,
|
||
is_active=True
|
||
)
|
||
self.fields['billing_provider'].queryset = User.objects.filter(
|
||
tenant=self.user.tenant,
|
||
is_active=True
|
||
)
|
||
self.fields['encounter'].queryset = Encounter.objects.filter(
|
||
tenant=self.user.tenant
|
||
)
|
||
self.fields['admission'].queryset = Admission.objects.filter(
|
||
tenant=self.user.tenant
|
||
)
|
||
|
||
def clean(self):
|
||
cleaned_data = super().clean()
|
||
service_date_from = cleaned_data.get('service_date_from')
|
||
service_date_to = cleaned_data.get('service_date_to')
|
||
bill_date = cleaned_data.get('bill_date')
|
||
due_date = cleaned_data.get('due_date')
|
||
|
||
# Validate service date range
|
||
if service_date_from and service_date_to:
|
||
if service_date_from > service_date_to:
|
||
raise ValidationError({
|
||
'service_date_to': 'Service end date must be after start date.'
|
||
})
|
||
|
||
# Validate bill date
|
||
if bill_date and service_date_to:
|
||
if bill_date < service_date_to:
|
||
raise ValidationError({
|
||
'bill_date': 'Bill date cannot be before service end date.'
|
||
})
|
||
|
||
# Validate due date
|
||
if due_date and bill_date:
|
||
if due_date < bill_date:
|
||
raise ValidationError({
|
||
'due_date': 'Due date cannot be before bill date.'
|
||
})
|
||
|
||
return cleaned_data
|
||
|
||
|
||
class BillLineItemForm(forms.ModelForm):
|
||
"""
|
||
Form for creating and editing bill line items.
|
||
"""
|
||
|
||
class Meta:
|
||
model = BillLineItem
|
||
fields = [
|
||
'line_number', 'service_date', 'service_code', 'service_description',
|
||
'service_category', 'quantity', 'unit_of_measure', 'unit_price',
|
||
'modifier_1', 'modifier_2', 'modifier_3', 'modifier_4',
|
||
'primary_diagnosis', 'secondary_diagnoses', 'rendering_provider',
|
||
'supervising_provider', 'place_of_service', 'revenue_code',
|
||
'ndc_code', 'drug_quantity', 'drug_unit', 'notes'
|
||
]
|
||
widgets = {
|
||
'line_number': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'min': '1'
|
||
}),
|
||
'service_date': forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date',
|
||
'required': True
|
||
}),
|
||
'service_code': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'required': True,
|
||
'placeholder': 'CPT/HCPCS code'
|
||
}),
|
||
'service_description': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'required': True,
|
||
'placeholder': 'Service description'
|
||
}),
|
||
'service_category': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'quantity': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0.01',
|
||
'required': True
|
||
}),
|
||
'unit_of_measure': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'UN (Units)'
|
||
}),
|
||
'unit_price': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0',
|
||
'required': True
|
||
}),
|
||
'modifier_1': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'maxlength': '2'
|
||
}),
|
||
'modifier_2': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'maxlength': '2'
|
||
}),
|
||
'modifier_3': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'maxlength': '2'
|
||
}),
|
||
'modifier_4': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'maxlength': '2'
|
||
}),
|
||
'primary_diagnosis': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'ICD-10 code'
|
||
}),
|
||
'secondary_diagnoses': forms.Textarea(attrs={
|
||
'class': 'form-control',
|
||
'rows': 2,
|
||
'placeholder': 'Additional ICD-10 codes (comma-separated)'
|
||
}),
|
||
'rendering_provider': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'supervising_provider': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'place_of_service': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'maxlength': '2'
|
||
}),
|
||
'revenue_code': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'maxlength': '4'
|
||
}),
|
||
'ndc_code': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'NDC code for drugs'
|
||
}),
|
||
'drug_quantity': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0'
|
||
}),
|
||
'drug_unit': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'ML, GM, etc.'
|
||
}),
|
||
'notes': forms.Textarea(attrs={
|
||
'class': 'form-control',
|
||
'rows': 3,
|
||
'placeholder': 'Line item notes...'
|
||
})
|
||
}
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
self.user = kwargs.pop('user', None)
|
||
super().__init__(*args, **kwargs)
|
||
|
||
if self.user and hasattr(self.user, 'tenant'):
|
||
# Filter provider choices by tenant
|
||
self.fields['rendering_provider'].queryset = User.objects.filter(
|
||
tenant=self.user.tenant,
|
||
is_active=True
|
||
)
|
||
self.fields['supervising_provider'].queryset = User.objects.filter(
|
||
tenant=self.user.tenant,
|
||
is_active=True
|
||
)
|
||
|
||
def clean(self):
|
||
cleaned_data = super().clean()
|
||
quantity = cleaned_data.get('quantity')
|
||
unit_price = cleaned_data.get('unit_price')
|
||
|
||
# Validate quantity and unit price
|
||
if quantity and quantity <= 0:
|
||
raise ValidationError({
|
||
'quantity': 'Quantity must be greater than zero.'
|
||
})
|
||
|
||
if unit_price and unit_price < 0:
|
||
raise ValidationError({
|
||
'unit_price': 'Unit price cannot be negative.'
|
||
})
|
||
|
||
return cleaned_data
|
||
|
||
|
||
class InsuranceClaimForm(forms.ModelForm):
|
||
"""
|
||
Form for creating and editing insurance claims.
|
||
"""
|
||
|
||
class Meta:
|
||
model = InsuranceClaim
|
||
fields = [
|
||
'insurance_info', 'claim_type', 'service_date_from', 'service_date_to',
|
||
'billed_amount', 'clearinghouse', 'batch_number', 'prior_auth_number',
|
||
'notes'
|
||
]
|
||
widgets = {
|
||
'insurance_info': forms.Select(attrs={
|
||
'class': 'form-select',
|
||
'required': True
|
||
}),
|
||
'claim_type': forms.Select(attrs={
|
||
'class': 'form-select',
|
||
'required': True
|
||
}),
|
||
'service_date_from': forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date',
|
||
'required': True
|
||
}),
|
||
'service_date_to': forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date',
|
||
'required': True
|
||
}),
|
||
'billed_amount': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0',
|
||
'required': True
|
||
}),
|
||
'clearinghouse': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Clearinghouse name'
|
||
}),
|
||
'batch_number': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Batch number'
|
||
}),
|
||
'prior_auth_number': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Prior authorization number'
|
||
}),
|
||
'notes': forms.Textarea(attrs={
|
||
'class': 'form-control',
|
||
'rows': 4,
|
||
'placeholder': 'Claim notes...'
|
||
})
|
||
}
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
self.user = kwargs.pop('user', None)
|
||
self.medical_bill = kwargs.pop('medical_bill', None)
|
||
super().__init__(*args, **kwargs)
|
||
|
||
if self.medical_bill:
|
||
# Filter insurance info by patient
|
||
self.fields['insurance_info'].queryset = InsuranceInfo.objects.filter(
|
||
patient=self.medical_bill.patient,
|
||
is_active=True
|
||
)
|
||
# Set default values from medical bill
|
||
self.fields['service_date_from'].initial = self.medical_bill.service_date_from
|
||
self.fields['service_date_to'].initial = self.medical_bill.service_date_to
|
||
self.fields['billed_amount'].initial = self.medical_bill.total_amount
|
||
|
||
def clean(self):
|
||
cleaned_data = super().clean()
|
||
service_date_from = cleaned_data.get('service_date_from')
|
||
service_date_to = cleaned_data.get('service_date_to')
|
||
billed_amount = cleaned_data.get('billed_amount')
|
||
|
||
# Validate service date range
|
||
if service_date_from and service_date_to:
|
||
if service_date_from > service_date_to:
|
||
raise ValidationError({
|
||
'service_date_to': 'Service end date must be after start date.'
|
||
})
|
||
|
||
# Validate billed amount
|
||
if billed_amount and billed_amount <= 0:
|
||
raise ValidationError({
|
||
'billed_amount': 'Billed amount must be greater than zero.'
|
||
})
|
||
|
||
return cleaned_data
|
||
|
||
|
||
class PaymentForm(forms.ModelForm):
|
||
"""
|
||
Form for creating and editing payments.
|
||
"""
|
||
|
||
class Meta:
|
||
model = Payment
|
||
fields = [
|
||
'payment_date', 'payment_amount', 'payment_method', 'payment_source',
|
||
'check_number', 'bank_name', 'routing_number', 'card_type',
|
||
'card_last_four', 'authorization_code', 'transaction_id',
|
||
'insurance_claim', 'eob_number', 'received_by', 'processed_by',
|
||
'notes'
|
||
]
|
||
widgets = {
|
||
'payment_date': forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date',
|
||
'required': True
|
||
}),
|
||
'payment_amount': forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0.01',
|
||
'required': True
|
||
}),
|
||
'payment_method': forms.Select(attrs={
|
||
'class': 'form-select',
|
||
'required': True
|
||
}),
|
||
'payment_source': forms.Select(attrs={
|
||
'class': 'form-select',
|
||
'required': True
|
||
}),
|
||
'check_number': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Check number'
|
||
}),
|
||
'bank_name': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Bank name'
|
||
}),
|
||
'routing_number': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Routing number'
|
||
}),
|
||
'card_type': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'card_last_four': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'maxlength': '4',
|
||
'placeholder': 'Last 4 digits'
|
||
}),
|
||
'authorization_code': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Authorization code'
|
||
}),
|
||
'transaction_id': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Transaction ID'
|
||
}),
|
||
'insurance_claim': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'eob_number': forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'EOB number'
|
||
}),
|
||
'received_by': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'processed_by': forms.Select(attrs={
|
||
'class': 'form-select'
|
||
}),
|
||
'notes': forms.Textarea(attrs={
|
||
'class': 'form-control',
|
||
'rows': 4,
|
||
'placeholder': 'Payment notes...'
|
||
})
|
||
}
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
self.user = kwargs.pop('user', None)
|
||
self.medical_bill = kwargs.pop('medical_bill', None)
|
||
super().__init__(*args, **kwargs)
|
||
|
||
if self.user and hasattr(self.user, 'tenant'):
|
||
# Filter choices by tenant
|
||
self.fields['received_by'].queryset = User.objects.filter(
|
||
tenant=self.user.tenant,
|
||
is_active=True
|
||
)
|
||
self.fields['processed_by'].queryset = User.objects.filter(
|
||
tenant=self.user.tenant,
|
||
is_active=True
|
||
)
|
||
|
||
if self.medical_bill:
|
||
# Filter insurance claims by medical bill
|
||
self.fields['insurance_claim'].queryset = InsuranceClaim.objects.filter(
|
||
medical_bill=self.medical_bill
|
||
)
|
||
|
||
def clean(self):
|
||
cleaned_data = super().clean()
|
||
payment_amount = cleaned_data.get('payment_amount')
|
||
payment_method = cleaned_data.get('payment_method')
|
||
payment_date = cleaned_data.get('payment_date')
|
||
|
||
# Validate payment amount
|
||
if payment_amount and payment_amount <= 0:
|
||
raise ValidationError({
|
||
'payment_amount': 'Payment amount must be greater than zero.'
|
||
})
|
||
|
||
# Validate payment date
|
||
if payment_date and payment_date > timezone.now().date():
|
||
raise ValidationError({
|
||
'payment_date': 'Payment date cannot be in the future.'
|
||
})
|
||
|
||
# Method-specific validations
|
||
if payment_method == 'check':
|
||
check_number = cleaned_data.get('check_number')
|
||
if not check_number:
|
||
raise ValidationError({
|
||
'check_number': 'Check number is required for check payments.'
|
||
})
|
||
|
||
elif payment_method == 'credit_card':
|
||
card_last_four = cleaned_data.get('card_last_four')
|
||
authorization_code = cleaned_data.get('authorization_code')
|
||
if not card_last_four:
|
||
raise ValidationError({
|
||
'card_last_four': 'Last 4 digits are required for credit card payments.'
|
||
})
|
||
if not authorization_code:
|
||
raise ValidationError({
|
||
'authorization_code': 'Authorization code is required for credit card payments.'
|
||
})
|
||
|
||
return cleaned_data
|
||
|
||
|
||
class BillingSearchForm(forms.Form):
|
||
"""
|
||
Form for searching and filtering billing records.
|
||
"""
|
||
|
||
search = forms.CharField(
|
||
required=False,
|
||
widget=forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Search bills, patients, providers...'
|
||
})
|
||
)
|
||
|
||
bill_type = forms.ChoiceField(
|
||
required=False,
|
||
choices=[('', 'All Types')] + [
|
||
('INPATIENT', 'Inpatient'),
|
||
('OUTPATIENT', 'Outpatient'),
|
||
('EMERGENCY', 'Emergency'),
|
||
('SURGERY', 'Surgery'),
|
||
('LABORATORY', 'Laboratory'),
|
||
('RADIOLOGY', 'Radiology'),
|
||
('PHARMACY', 'Pharmacy'),
|
||
('PROFESSIONAL', 'Professional Services'),
|
||
],
|
||
widget=forms.Select(attrs={
|
||
'class': 'form-select'
|
||
})
|
||
)
|
||
|
||
status = forms.ChoiceField(
|
||
required=False,
|
||
choices=[('', 'All Statuses')] + [
|
||
('DRAFT', 'Draft'),
|
||
('PENDING', 'Pending'),
|
||
('SUBMITTED', 'Submitted'),
|
||
('PARTIAL_PAID', 'Partially Paid'),
|
||
('PAID', 'Paid'),
|
||
('OVERDUE', 'Overdue'),
|
||
('COLLECTIONS', 'Collections'),
|
||
('WRITTEN_OFF', 'Written Off'),
|
||
],
|
||
widget=forms.Select(attrs={
|
||
'class': 'form-select'
|
||
})
|
||
)
|
||
|
||
date_from = forms.DateField(
|
||
required=False,
|
||
widget=forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date'
|
||
})
|
||
)
|
||
|
||
date_to = forms.DateField(
|
||
required=False,
|
||
widget=forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date'
|
||
})
|
||
)
|
||
|
||
amount_min = forms.DecimalField(
|
||
required=False,
|
||
widget=forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0'
|
||
})
|
||
)
|
||
|
||
amount_max = forms.DecimalField(
|
||
required=False,
|
||
widget=forms.NumberInput(attrs={
|
||
'class': 'form-control',
|
||
'step': '0.01',
|
||
'min': '0'
|
||
})
|
||
)
|
||
|
||
overdue_only = forms.BooleanField(
|
||
required=False,
|
||
widget=forms.CheckboxInput(attrs={
|
||
'class': 'form-check-input'
|
||
})
|
||
)
|
||
|
||
def __init__(self, *args, **kwargs):
|
||
self.user = kwargs.pop('user', None)
|
||
super().__init__(*args, **kwargs)
|
||
|
||
|
||
class ClaimSearchForm(forms.Form):
|
||
"""
|
||
Form for searching and filtering insurance claims.
|
||
"""
|
||
|
||
search = forms.CharField(
|
||
required=False,
|
||
widget=forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Search claims, patients, insurance...'
|
||
})
|
||
)
|
||
|
||
claim_type = forms.ChoiceField(
|
||
required=False,
|
||
choices=[('', 'All Types')] + [
|
||
('PRIMARY', 'Primary Claim'),
|
||
('SECONDARY', 'Secondary Claim'),
|
||
('TERTIARY', 'Tertiary Claim'),
|
||
('CORRECTED', 'Corrected Claim'),
|
||
('VOID', 'Void Claim'),
|
||
('REPLACEMENT', 'Replacement Claim'),
|
||
],
|
||
widget=forms.Select(attrs={
|
||
'class': 'form-select'
|
||
})
|
||
)
|
||
|
||
status = forms.ChoiceField(
|
||
required=False,
|
||
choices=[('', 'All Statuses')] + [
|
||
('DRAFT', 'Draft'),
|
||
('SUBMITTED', 'Submitted'),
|
||
('PENDING', 'Pending'),
|
||
('PROCESSING', 'Processing'),
|
||
('PAID', 'Paid'),
|
||
('DENIED', 'Denied'),
|
||
('REJECTED', 'Rejected'),
|
||
('APPEALED', 'Appealed'),
|
||
],
|
||
widget=forms.Select(attrs={
|
||
'class': 'form-select'
|
||
})
|
||
)
|
||
|
||
submission_date_from = forms.DateField(
|
||
required=False,
|
||
widget=forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date'
|
||
})
|
||
)
|
||
|
||
submission_date_to = forms.DateField(
|
||
required=False,
|
||
widget=forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date'
|
||
})
|
||
)
|
||
|
||
pending_only = forms.BooleanField(
|
||
required=False,
|
||
widget=forms.CheckboxInput(attrs={
|
||
'class': 'form-check-input'
|
||
})
|
||
)
|
||
|
||
|
||
class PaymentSearchForm(forms.Form):
|
||
"""
|
||
Form for searching and filtering payments.
|
||
"""
|
||
|
||
search = forms.CharField(
|
||
required=False,
|
||
widget=forms.TextInput(attrs={
|
||
'class': 'form-control',
|
||
'placeholder': 'Search payments, patients, check numbers...'
|
||
})
|
||
)
|
||
|
||
payment_method = forms.ChoiceField(
|
||
required=False,
|
||
choices=[('', 'All Methods')] + [
|
||
('CASH', 'Cash'),
|
||
('CHECK', 'Check'),
|
||
('CREDIT_CARD', 'Credit Card'),
|
||
('DEBIT_CARD', 'Debit Card'),
|
||
('BANK_TRANSFER', 'Bank Transfer'),
|
||
('ACH', 'ACH Transfer'),
|
||
('WIRE', 'Wire Transfer'),
|
||
('MONEY_ORDER', 'Money Order'),
|
||
],
|
||
widget=forms.Select(attrs={
|
||
'class': 'form-select'
|
||
})
|
||
)
|
||
|
||
payment_source = forms.ChoiceField(
|
||
required=False,
|
||
choices=[('', 'All Sources')] + [
|
||
('PATIENT', 'Patient'),
|
||
('INSURANCE', 'Insurance'),
|
||
('GUARANTOR', 'Guarantor'),
|
||
('GOVERNMENT', 'Government'),
|
||
('CHARITY', 'Charity'),
|
||
('OTHER', 'Other'),
|
||
],
|
||
widget=forms.Select(attrs={
|
||
'class': 'form-select'
|
||
})
|
||
)
|
||
|
||
status = forms.ChoiceField(
|
||
required=False,
|
||
choices=[('', 'All Statuses')] + [
|
||
('PENDING', 'Pending'),
|
||
('PROCESSED', 'Processed'),
|
||
('CLEARED', 'Cleared'),
|
||
('BOUNCED', 'Bounced'),
|
||
('REVERSED', 'Reversed'),
|
||
('REFUNDED', 'Refunded'),
|
||
],
|
||
widget=forms.Select(attrs={
|
||
'class': 'form-select'
|
||
})
|
||
)
|
||
|
||
date_from = forms.DateField(
|
||
required=False,
|
||
widget=forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date'
|
||
})
|
||
)
|
||
|
||
date_to = forms.DateField(
|
||
required=False,
|
||
widget=forms.DateInput(attrs={
|
||
'class': 'form-control',
|
||
'type': 'date'
|
||
})
|
||
)
|
||
|
||
|
||
# from django import forms
|
||
# from django.core.exceptions import ValidationError
|
||
# from django.utils import timezone
|
||
# from django.contrib.auth.models import User
|
||
# from crispy_forms.helper import FormHelper
|
||
# from crispy_forms.layout import Layout, Fieldset, Submit, Row, Column, HTML, Div
|
||
# from crispy_forms.bootstrap import FormActions
|
||
# from decimal import Decimal
|
||
# import json
|
||
#
|
||
# from .models import (
|
||
# Bill, BillItem, InsuranceClaim, Payment, PaymentMethod,
|
||
# InsuranceProvider, ClaimDenial, PaymentPlan
|
||
# )
|
||
# from patients.models import Patient
|
||
#
|
||
#
|
||
# class MedicalBillingForm(forms.ModelForm):
|
||
# """
|
||
# Form for medical billing creation
|
||
# """
|
||
# patient_search = forms.CharField(
|
||
# required=False,
|
||
# widget=forms.TextInput(attrs={
|
||
# 'class': 'form-control',
|
||
# 'placeholder': 'Search patient by name, ID, or insurance...',
|
||
# 'data-toggle': 'patient-search'
|
||
# })
|
||
# )
|
||
# insurance_verification = forms.BooleanField(
|
||
# required=False,
|
||
# initial=True,
|
||
# widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||
# )
|
||
# auto_submit_primary = forms.BooleanField(
|
||
# required=False,
|
||
# initial=True,
|
||
# widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||
# )
|
||
# auto_submit_secondary = forms.BooleanField(
|
||
# required=False,
|
||
# widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||
# )
|
||
# generate_patient_statement = forms.BooleanField(
|
||
# required=False,
|
||
# initial=True,
|
||
# widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||
# )
|
||
#
|
||
# class Meta:
|
||
# model = Bill
|
||
# fields = [
|
||
# 'patient', 'encounter', 'bill_type', 'service_date',
|
||
# 'diagnosis_codes', 'procedure_codes', 'provider',
|
||
# 'facility', 'insurance_verification', 'auto_submit_primary',
|
||
# 'auto_submit_secondary', 'generate_patient_statement'
|
||
# ]
|
||
# widgets = {
|
||
# 'patient': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'encounter': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'bill_type': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'service_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
||
# 'diagnosis_codes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
||
# 'procedure_codes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
||
# 'provider': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'facility': forms.Select(attrs={'class': 'form-control'})
|
||
# }
|
||
#
|
||
# def __init__(self, *args, **kwargs):
|
||
# tenant = kwargs.pop('tenant', None)
|
||
# super().__init__(*args, **kwargs)
|
||
#
|
||
# if tenant:
|
||
# self.fields['patient'].queryset = Patient.objects.filter(tenant=tenant)
|
||
# self.fields['provider'].queryset = User.objects.filter(
|
||
# tenant=tenant,
|
||
# groups__name__in=['Doctors', 'Nurses', 'Specialists']
|
||
# )
|
||
#
|
||
# self.helper = FormHelper()
|
||
# self.helper.layout = Layout(
|
||
# Fieldset(
|
||
# 'Patient Information',
|
||
# 'patient_search',
|
||
# 'patient',
|
||
# 'encounter'
|
||
# ),
|
||
# Fieldset(
|
||
# 'Service Details',
|
||
# Row(
|
||
# Column('bill_type', css_class='form-group col-md-6 mb-0'),
|
||
# Column('service_date', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# Row(
|
||
# Column('provider', css_class='form-group col-md-6 mb-0'),
|
||
# Column('facility', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# 'diagnosis_codes',
|
||
# 'procedure_codes'
|
||
# ),
|
||
# Fieldset(
|
||
# 'Billing Options',
|
||
# HTML('<div class="form-check">'),
|
||
# 'insurance_verification',
|
||
# HTML(
|
||
# '<label class="form-check-label" for="id_insurance_verification">Verify insurance eligibility</label>'),
|
||
# HTML('</div>'),
|
||
# HTML('<div class="form-check">'),
|
||
# 'auto_submit_primary',
|
||
# HTML(
|
||
# '<label class="form-check-label" for="id_auto_submit_primary">Auto-submit to primary insurance</label>'),
|
||
# HTML('</div>'),
|
||
# HTML('<div class="form-check">'),
|
||
# 'auto_submit_secondary',
|
||
# HTML(
|
||
# '<label class="form-check-label" for="id_auto_submit_secondary">Auto-submit to secondary insurance</label>'),
|
||
# HTML('</div>'),
|
||
# HTML('<div class="form-check">'),
|
||
# 'generate_patient_statement',
|
||
# HTML(
|
||
# '<label class="form-check-label" for="id_generate_patient_statement">Generate patient statement</label>'),
|
||
# HTML('</div>')
|
||
# ),
|
||
# FormActions(
|
||
# Submit('submit', 'Create Bill', css_class='btn btn-primary'),
|
||
# HTML('<a href="{% url \'billing:bill_list\' %}" class="btn btn-secondary">Cancel</a>')
|
||
# )
|
||
# )
|
||
#
|
||
# def clean_diagnosis_codes(self):
|
||
# codes = self.cleaned_data.get('diagnosis_codes')
|
||
# if codes:
|
||
# # Validate ICD-10 codes format
|
||
# code_list = [code.strip() for code in codes.split(',')]
|
||
# for code in code_list:
|
||
# if not self.validate_icd10_code(code):
|
||
# raise ValidationError(f'Invalid ICD-10 code format: {code}')
|
||
# return codes
|
||
#
|
||
# def clean_procedure_codes(self):
|
||
# codes = self.cleaned_data.get('procedure_codes')
|
||
# if codes:
|
||
# # Validate CPT codes format
|
||
# code_list = [code.strip() for code in codes.split(',')]
|
||
# for code in code_list:
|
||
# if not self.validate_cpt_code(code):
|
||
# raise ValidationError(f'Invalid CPT code format: {code}')
|
||
# return codes
|
||
#
|
||
# def validate_icd10_code(self, code):
|
||
# """Validate ICD-10 code format"""
|
||
# # Basic ICD-10 validation (simplified)
|
||
# return len(code) >= 3 and code[0].isalpha()
|
||
#
|
||
# def validate_cpt_code(self, code):
|
||
# """Validate CPT code format"""
|
||
# # Basic CPT validation (simplified)
|
||
# return len(code) == 5 and code.isdigit()
|
||
#
|
||
#
|
||
# class BillItemForm(forms.ModelForm):
|
||
# """
|
||
# Form for bill item creation/editing
|
||
# """
|
||
# quantity = forms.DecimalField(
|
||
# max_digits=10,
|
||
# decimal_places=2,
|
||
# initial=1.00,
|
||
# widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'})
|
||
# )
|
||
# unit_price = forms.DecimalField(
|
||
# max_digits=10,
|
||
# decimal_places=2,
|
||
# widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'})
|
||
# )
|
||
# discount_amount = forms.DecimalField(
|
||
# max_digits=10,
|
||
# decimal_places=2,
|
||
# initial=0.00,
|
||
# required=False,
|
||
# widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'})
|
||
# )
|
||
#
|
||
# class Meta:
|
||
# model = BillItem
|
||
# fields = [
|
||
# 'service_code', 'description', 'quantity', 'unit_price',
|
||
# 'discount_amount', 'modifier_codes', 'revenue_code'
|
||
# ]
|
||
# widgets = {
|
||
# 'service_code': forms.TextInput(attrs={'class': 'form-control'}),
|
||
# 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}),
|
||
# 'modifier_codes': forms.TextInput(attrs={'class': 'form-control'}),
|
||
# 'revenue_code': forms.TextInput(attrs={'class': 'form-control'})
|
||
# }
|
||
#
|
||
# def __init__(self, *args, **kwargs):
|
||
# super().__init__(*args, **kwargs)
|
||
#
|
||
# self.helper = FormHelper()
|
||
# self.helper.layout = Layout(
|
||
# Fieldset(
|
||
# 'Service Information',
|
||
# Row(
|
||
# Column('service_code', css_class='form-group col-md-6 mb-0'),
|
||
# Column('revenue_code', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# 'description',
|
||
# 'modifier_codes'
|
||
# ),
|
||
# Fieldset(
|
||
# 'Pricing',
|
||
# Row(
|
||
# Column('quantity', css_class='form-group col-md-4 mb-0'),
|
||
# Column('unit_price', css_class='form-group col-md-4 mb-0'),
|
||
# Column('discount_amount', css_class='form-group col-md-4 mb-0'),
|
||
# css_class='form-row'
|
||
# )
|
||
# ),
|
||
# FormActions(
|
||
# Submit('submit', 'Save Item', css_class='btn btn-primary'),
|
||
# HTML('<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>')
|
||
# )
|
||
# )
|
||
#
|
||
# def clean(self):
|
||
# cleaned_data = super().clean()
|
||
# quantity = cleaned_data.get('quantity')
|
||
# unit_price = cleaned_data.get('unit_price')
|
||
# discount_amount = cleaned_data.get('discount_amount', Decimal('0.00'))
|
||
#
|
||
# if quantity and unit_price:
|
||
# total_amount = quantity * unit_price
|
||
# if discount_amount > total_amount:
|
||
# raise ValidationError('Discount amount cannot exceed total amount.')
|
||
#
|
||
# return cleaned_data
|
||
#
|
||
#
|
||
# class InsuranceClaimForm(forms.ModelForm):
|
||
# """
|
||
# Form for insurance claim submission
|
||
# """
|
||
# claim_type = forms.ChoiceField(
|
||
# choices=[
|
||
# ('primary', 'Primary Insurance'),
|
||
# ('secondary', 'Secondary Insurance'),
|
||
# ('tertiary', 'Tertiary Insurance')
|
||
# ],
|
||
# required=True,
|
||
# widget=forms.Select(attrs={'class': 'form-control'})
|
||
# )
|
||
# submit_electronically = forms.BooleanField(
|
||
# required=False,
|
||
# initial=True,
|
||
# widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||
# )
|
||
# prior_authorization = forms.CharField(
|
||
# required=False,
|
||
# widget=forms.TextInput(attrs={'class': 'form-control'})
|
||
# )
|
||
# referral_number = forms.CharField(
|
||
# required=False,
|
||
# widget=forms.TextInput(attrs={'class': 'form-control'})
|
||
# )
|
||
#
|
||
# class Meta:
|
||
# model = InsuranceClaim
|
||
# fields = [
|
||
# 'bill', 'insurance_provider', 'claim_type', 'policy_number',
|
||
# 'group_number', 'subscriber_id', 'prior_authorization',
|
||
# 'referral_number', 'submit_electronically'
|
||
# ]
|
||
# widgets = {
|
||
# 'bill': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'insurance_provider': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'policy_number': forms.TextInput(attrs={'class': 'form-control'}),
|
||
# 'group_number': forms.TextInput(attrs={'class': 'form-control'}),
|
||
# 'subscriber_id': forms.TextInput(attrs={'class': 'form-control'})
|
||
# }
|
||
#
|
||
# def __init__(self, *args, **kwargs):
|
||
# tenant = kwargs.pop('tenant', None)
|
||
# super().__init__(*args, **kwargs)
|
||
#
|
||
# if tenant:
|
||
# self.fields['bill'].queryset = Bill.objects.filter(tenant=tenant)
|
||
# self.fields['insurance_provider'].queryset = InsuranceProvider.objects.filter(tenant=tenant)
|
||
#
|
||
# self.helper = FormHelper()
|
||
# self.helper.layout = Layout(
|
||
# Fieldset(
|
||
# 'Claim Information',
|
||
# Row(
|
||
# Column('bill', css_class='form-group col-md-6 mb-0'),
|
||
# Column('claim_type', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# 'insurance_provider'
|
||
# ),
|
||
# Fieldset(
|
||
# 'Insurance Details',
|
||
# Row(
|
||
# Column('policy_number', css_class='form-group col-md-6 mb-0'),
|
||
# Column('group_number', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# 'subscriber_id'
|
||
# ),
|
||
# Fieldset(
|
||
# 'Authorization',
|
||
# Row(
|
||
# Column('prior_authorization', css_class='form-group col-md-6 mb-0'),
|
||
# Column('referral_number', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# )
|
||
# ),
|
||
# Fieldset(
|
||
# 'Submission Options',
|
||
# HTML('<div class="form-check">'),
|
||
# 'submit_electronically',
|
||
# HTML('<label class="form-check-label" for="id_submit_electronically">Submit electronically</label>'),
|
||
# HTML('</div>')
|
||
# ),
|
||
# FormActions(
|
||
# Submit('submit', 'Submit Claim', css_class='btn btn-primary'),
|
||
# HTML('<a href="{% url \'billing:claim_list\' %}" class="btn btn-secondary">Cancel</a>')
|
||
# )
|
||
# )
|
||
#
|
||
#
|
||
# class PaymentProcessingForm(forms.ModelForm):
|
||
# """
|
||
# Form for payment processing
|
||
# """
|
||
# payment_amount = forms.DecimalField(
|
||
# max_digits=10,
|
||
# decimal_places=2,
|
||
# widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'})
|
||
# )
|
||
# payment_date = forms.DateField(
|
||
# initial=timezone.now().date(),
|
||
# widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
|
||
# )
|
||
# reference_number = forms.CharField(
|
||
# required=False,
|
||
# widget=forms.TextInput(attrs={'class': 'form-control'})
|
||
# )
|
||
# notes = forms.CharField(
|
||
# required=False,
|
||
# widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3})
|
||
# )
|
||
# send_receipt = forms.BooleanField(
|
||
# required=False,
|
||
# initial=True,
|
||
# widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||
# )
|
||
#
|
||
# class Meta:
|
||
# model = Payment
|
||
# fields = [
|
||
# 'bill', 'payment_method', 'payment_source', 'payment_amount',
|
||
# 'payment_date', 'reference_number', 'notes', 'send_receipt'
|
||
# ]
|
||
# widgets = {
|
||
# 'bill': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'payment_method': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'payment_source': forms.Select(attrs={'class': 'form-control'})
|
||
# }
|
||
#
|
||
# def __init__(self, *args, **kwargs):
|
||
# tenant = kwargs.pop('tenant', None)
|
||
# bill = kwargs.pop('bill', None)
|
||
# super().__init__(*args, **kwargs)
|
||
#
|
||
# if tenant:
|
||
# self.fields['bill'].queryset = Bill.objects.filter(tenant=tenant)
|
||
# self.fields['payment_method'].queryset = PaymentMethod.objects.filter(tenant=tenant)
|
||
#
|
||
# if bill:
|
||
# self.fields['bill'].initial = bill
|
||
# self.fields['payment_amount'].initial = bill.balance_due
|
||
#
|
||
# self.helper = FormHelper()
|
||
# self.helper.layout = Layout(
|
||
# Fieldset(
|
||
# 'Payment Information',
|
||
# Row(
|
||
# Column('bill', css_class='form-group col-md-6 mb-0'),
|
||
# Column('payment_source', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# Row(
|
||
# Column('payment_method', css_class='form-group col-md-6 mb-0'),
|
||
# Column('payment_amount', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# Row(
|
||
# Column('payment_date', css_class='form-group col-md-6 mb-0'),
|
||
# Column('reference_number', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# 'notes'
|
||
# ),
|
||
# Fieldset(
|
||
# 'Options',
|
||
# HTML('<div class="form-check">'),
|
||
# 'send_receipt',
|
||
# HTML('<label class="form-check-label" for="id_send_receipt">Send payment receipt</label>'),
|
||
# HTML('</div>')
|
||
# ),
|
||
# FormActions(
|
||
# Submit('submit', 'Process Payment', css_class='btn btn-primary'),
|
||
# HTML('<a href="{% url \'billing:payment_list\' %}" class="btn btn-secondary">Cancel</a>')
|
||
# )
|
||
# )
|
||
#
|
||
# def clean_payment_amount(self):
|
||
# payment_amount = self.cleaned_data.get('payment_amount')
|
||
# bill = self.cleaned_data.get('bill')
|
||
#
|
||
# if payment_amount and bill:
|
||
# if payment_amount > bill.balance_due:
|
||
# raise ValidationError('Payment amount cannot exceed balance due.')
|
||
# if payment_amount <= 0:
|
||
# raise ValidationError('Payment amount must be greater than zero.')
|
||
#
|
||
# return payment_amount
|
||
#
|
||
#
|
||
# class DenialManagementForm(forms.ModelForm):
|
||
# """
|
||
# Form for denial management
|
||
# """
|
||
# denial_reason = forms.ChoiceField(
|
||
# choices=[
|
||
# ('invalid_code', 'Invalid Procedure/Diagnosis Code'),
|
||
# ('not_covered', 'Service Not Covered'),
|
||
# ('prior_auth', 'Prior Authorization Required'),
|
||
# ('duplicate', 'Duplicate Claim'),
|
||
# ('incomplete', 'Incomplete Information'),
|
||
# ('timely_filing', 'Timely Filing Limit Exceeded'),
|
||
# ('other', 'Other')
|
||
# ],
|
||
# required=True,
|
||
# widget=forms.Select(attrs={'class': 'form-control'})
|
||
# )
|
||
# appeal_deadline = forms.DateField(
|
||
# required=False,
|
||
# widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
|
||
# )
|
||
# corrective_action = forms.CharField(
|
||
# required=False,
|
||
# widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 4})
|
||
# )
|
||
# resubmit_claim = forms.BooleanField(
|
||
# required=False,
|
||
# widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||
# )
|
||
# file_appeal = forms.BooleanField(
|
||
# required=False,
|
||
# widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||
# )
|
||
#
|
||
# class Meta:
|
||
# model = ClaimDenial
|
||
# fields = [
|
||
# 'claim', 'denial_reason', 'denial_description', 'denied_amount',
|
||
# 'appeal_deadline', 'corrective_action', 'resubmit_claim', 'file_appeal'
|
||
# ]
|
||
# widgets = {
|
||
# 'claim': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'denial_description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
||
# 'denied_amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'})
|
||
# }
|
||
#
|
||
# def __init__(self, *args, **kwargs):
|
||
# tenant = kwargs.pop('tenant', None)
|
||
# super().__init__(*args, **kwargs)
|
||
#
|
||
# if tenant:
|
||
# self.fields['claim'].queryset = InsuranceClaim.objects.filter(tenant=tenant)
|
||
#
|
||
# self.helper = FormHelper()
|
||
# self.helper.layout = Layout(
|
||
# Fieldset(
|
||
# 'Denial Information',
|
||
# 'claim',
|
||
# Row(
|
||
# Column('denial_reason', css_class='form-group col-md-6 mb-0'),
|
||
# Column('denied_amount', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# 'denial_description',
|
||
# 'appeal_deadline'
|
||
# ),
|
||
# Fieldset(
|
||
# 'Corrective Actions',
|
||
# 'corrective_action',
|
||
# HTML('<div class="form-check">'),
|
||
# 'resubmit_claim',
|
||
# HTML('<label class="form-check-label" for="id_resubmit_claim">Resubmit claim with corrections</label>'),
|
||
# HTML('</div>'),
|
||
# HTML('<div class="form-check">'),
|
||
# 'file_appeal',
|
||
# HTML('<label class="form-check-label" for="id_file_appeal">File appeal</label>'),
|
||
# HTML('</div>')
|
||
# ),
|
||
# FormActions(
|
||
# Submit('submit', 'Process Denial', css_class='btn btn-primary'),
|
||
# HTML('<a href="{% url \'billing:denial_list\' %}" class="btn btn-secondary">Cancel</a>')
|
||
# )
|
||
# )
|
||
#
|
||
#
|
||
# class PaymentPlanForm(forms.ModelForm):
|
||
# """
|
||
# Form for payment plan setup
|
||
# """
|
||
# plan_type = forms.ChoiceField(
|
||
# choices=[
|
||
# ('monthly', 'Monthly Payments'),
|
||
# ('weekly', 'Weekly Payments'),
|
||
# ('biweekly', 'Bi-weekly Payments'),
|
||
# ('custom', 'Custom Schedule')
|
||
# ],
|
||
# required=True,
|
||
# widget=forms.Select(attrs={'class': 'form-control'})
|
||
# )
|
||
# number_of_payments = forms.IntegerField(
|
||
# min_value=2,
|
||
# max_value=60,
|
||
# widget=forms.NumberInput(attrs={'class': 'form-control'})
|
||
# )
|
||
# payment_amount = forms.DecimalField(
|
||
# max_digits=10,
|
||
# decimal_places=2,
|
||
# widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'})
|
||
# )
|
||
# first_payment_date = forms.DateField(
|
||
# widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
|
||
# )
|
||
# auto_payment = forms.BooleanField(
|
||
# required=False,
|
||
# widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
||
# )
|
||
#
|
||
# class Meta:
|
||
# model = PaymentPlan
|
||
# fields = [
|
||
# 'bill', 'plan_type', 'total_amount', 'number_of_payments',
|
||
# 'payment_amount', 'first_payment_date', 'auto_payment'
|
||
# ]
|
||
# widgets = {
|
||
# 'bill': forms.Select(attrs={'class': 'form-control'}),
|
||
# 'total_amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'})
|
||
# }
|
||
#
|
||
# def __init__(self, *args, **kwargs):
|
||
# tenant = kwargs.pop('tenant', None)
|
||
# super().__init__(*args, **kwargs)
|
||
#
|
||
# if tenant:
|
||
# self.fields['bill'].queryset = Bill.objects.filter(tenant=tenant, balance_due__gt=0)
|
||
#
|
||
# # Set minimum date to tomorrow
|
||
# tomorrow = timezone.now().date() + timezone.timedelta(days=1)
|
||
# self.fields['first_payment_date'].widget.attrs['min'] = tomorrow.isoformat()
|
||
#
|
||
# self.helper = FormHelper()
|
||
# self.helper.layout = Layout(
|
||
# Fieldset(
|
||
# 'Payment Plan Details',
|
||
# 'bill',
|
||
# Row(
|
||
# Column('plan_type', css_class='form-group col-md-6 mb-0'),
|
||
# Column('total_amount', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# Row(
|
||
# Column('number_of_payments', css_class='form-group col-md-6 mb-0'),
|
||
# Column('payment_amount', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# 'first_payment_date'
|
||
# ),
|
||
# Fieldset(
|
||
# 'Options',
|
||
# HTML('<div class="form-check">'),
|
||
# 'auto_payment',
|
||
# HTML('<label class="form-check-label" for="id_auto_payment">Enable automatic payments</label>'),
|
||
# HTML('</div>')
|
||
# ),
|
||
# FormActions(
|
||
# Submit('submit', 'Create Payment Plan', css_class='btn btn-primary'),
|
||
# HTML('<a href="{% url \'billing:payment_plan_list\' %}" class="btn btn-secondary">Cancel</a>')
|
||
# )
|
||
# )
|
||
#
|
||
# def clean(self):
|
||
# cleaned_data = super().clean()
|
||
# total_amount = cleaned_data.get('total_amount')
|
||
# number_of_payments = cleaned_data.get('number_of_payments')
|
||
# payment_amount = cleaned_data.get('payment_amount')
|
||
#
|
||
# if total_amount and number_of_payments and payment_amount:
|
||
# calculated_total = payment_amount * number_of_payments
|
||
# if abs(calculated_total - total_amount) > Decimal('0.01'):
|
||
# raise ValidationError('Payment amount × number of payments must equal total amount.')
|
||
#
|
||
# return cleaned_data
|
||
#
|
||
#
|
||
# class CollectionsForm(forms.Form):
|
||
# """
|
||
# Form for collections management
|
||
# """
|
||
# collection_action = forms.ChoiceField(
|
||
# choices=[
|
||
# ('letter_1', 'Send First Collection Letter'),
|
||
# ('letter_2', 'Send Second Collection Letter'),
|
||
# ('letter_3', 'Send Final Collection Letter'),
|
||
# ('phone_call', 'Schedule Phone Call'),
|
||
# ('external_agency', 'Send to External Agency'),
|
||
# ('write_off', 'Write Off Balance')
|
||
# ],
|
||
# required=True,
|
||
# widget=forms.Select(attrs={'class': 'form-control'})
|
||
# )
|
||
# collection_notes = forms.CharField(
|
||
# required=False,
|
||
# widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 4})
|
||
# )
|
||
# follow_up_date = forms.DateField(
|
||
# required=False,
|
||
# widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
|
||
# )
|
||
# write_off_reason = forms.ChoiceField(
|
||
# choices=[
|
||
# ('uncollectible', 'Uncollectible'),
|
||
# ('patient_deceased', 'Patient Deceased'),
|
||
# ('bankruptcy', 'Bankruptcy'),
|
||
# ('small_balance', 'Small Balance'),
|
||
# ('other', 'Other')
|
||
# ],
|
||
# required=False,
|
||
# widget=forms.Select(attrs={'class': 'form-control'})
|
||
# )
|
||
#
|
||
# def __init__(self, *args, **kwargs):
|
||
# super().__init__(*args, **kwargs)
|
||
#
|
||
# self.helper = FormHelper()
|
||
# self.helper.layout = Layout(
|
||
# Fieldset(
|
||
# 'Collection Action',
|
||
# 'collection_action',
|
||
# 'collection_notes',
|
||
# 'follow_up_date'
|
||
# ),
|
||
# Fieldset(
|
||
# 'Write-off Details',
|
||
# 'write_off_reason',
|
||
# HTML('<small class="form-text text-muted">Required only for write-off actions</small>')
|
||
# ),
|
||
# FormActions(
|
||
# Submit('submit', 'Execute Collection Action', css_class='btn btn-primary'),
|
||
# HTML('<a href="{% url \'billing:collections_list\' %}" class="btn btn-secondary">Cancel</a>')
|
||
# )
|
||
# )
|
||
#
|
||
#
|
||
# class InsuranceVerificationForm(forms.Form):
|
||
# """
|
||
# Form for insurance verification
|
||
# """
|
||
# verification_type = forms.ChoiceField(
|
||
# choices=[
|
||
# ('eligibility', 'Eligibility Verification'),
|
||
# ('benefits', 'Benefits Verification'),
|
||
# ('authorization', 'Prior Authorization'),
|
||
# ('referral', 'Referral Verification')
|
||
# ],
|
||
# required=True,
|
||
# widget=forms.Select(attrs={'class': 'form-control'})
|
||
# )
|
||
# service_date = forms.DateField(
|
||
# widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
|
||
# )
|
||
# procedure_codes = forms.CharField(
|
||
# widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3})
|
||
# )
|
||
# verification_notes = forms.CharField(
|
||
# required=False,
|
||
# widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 4})
|
||
# )
|
||
#
|
||
# def __init__(self, *args, **kwargs):
|
||
# super().__init__(*args, **kwargs)
|
||
#
|
||
# self.helper = FormHelper()
|
||
# self.helper.layout = Layout(
|
||
# Fieldset(
|
||
# 'Verification Details',
|
||
# Row(
|
||
# Column('verification_type', css_class='form-group col-md-6 mb-0'),
|
||
# Column('service_date', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# 'procedure_codes',
|
||
# 'verification_notes'
|
||
# ),
|
||
# FormActions(
|
||
# Submit('submit', 'Verify Insurance', css_class='btn btn-primary'),
|
||
# HTML('<a href="{% url \'billing:verification_list\' %}" class="btn btn-secondary">Cancel</a>')
|
||
# )
|
||
# )
|
||
#
|
||
#
|
||
# class BulkBillingForm(forms.Form):
|
||
# """
|
||
# Form for bulk billing operations
|
||
# """
|
||
# action = forms.ChoiceField(
|
||
# choices=[
|
||
# ('submit_claims', 'Submit Claims'),
|
||
# ('generate_statements', 'Generate Patient Statements'),
|
||
# ('send_reminders', 'Send Payment Reminders'),
|
||
# ('update_status', 'Update Status'),
|
||
# ('apply_adjustments', 'Apply Adjustments')
|
||
# ],
|
||
# required=True,
|
||
# widget=forms.Select(attrs={'class': 'form-control'})
|
||
# )
|
||
# bill_ids = forms.CharField(
|
||
# widget=forms.HiddenInput()
|
||
# )
|
||
# new_status = forms.ChoiceField(
|
||
# choices=[
|
||
# ('pending', 'Pending'),
|
||
# ('submitted', 'Submitted'),
|
||
# ('paid', 'Paid'),
|
||
# ('denied', 'Denied'),
|
||
# ('cancelled', 'Cancelled')
|
||
# ],
|
||
# required=False,
|
||
# widget=forms.Select(attrs={'class': 'form-control'})
|
||
# )
|
||
# adjustment_amount = forms.DecimalField(
|
||
# max_digits=10,
|
||
# decimal_places=2,
|
||
# required=False,
|
||
# widget=forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'})
|
||
# )
|
||
# adjustment_reason = forms.CharField(
|
||
# required=False,
|
||
# widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3})
|
||
# )
|
||
#
|
||
# def __init__(self, *args, **kwargs):
|
||
# super().__init__(*args, **kwargs)
|
||
#
|
||
# self.helper = FormHelper()
|
||
# self.helper.layout = Layout(
|
||
# 'bill_ids',
|
||
# Fieldset(
|
||
# 'Bulk Operation',
|
||
# 'action',
|
||
# 'new_status',
|
||
# Row(
|
||
# Column('adjustment_amount', css_class='form-group col-md-6 mb-0'),
|
||
# css_class='form-row'
|
||
# ),
|
||
# 'adjustment_reason'
|
||
# ),
|
||
# FormActions(
|
||
# Submit('submit', 'Execute Bulk Operation', css_class='btn btn-primary'),
|
||
# HTML('<a href="{% url \'billing:bill_list\' %}" class="btn btn-secondary">Cancel</a>')
|
||
# )
|
||
# )
|
||
#
|
||
#
|
||
#
|