Marwan Alwali 23158e9fbf update
2025-09-08 03:00:23 +03:00

580 lines
26 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit Claim{% else %}Submit Insurance Claim{% endif %} - {{ block.super }}{% endblock %}
{% block content %}
<div class="container-fluid">
<!-- Page Header -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h1 class="h3 mb-1">{% if object %}Edit Insurance Claim{% else %}Submit Insurance Claim{% endif %}</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="{% url 'billing:dashboard' %}">Billing</a></li>
<li class="breadcrumb-item"><a href="{% url 'billing:claim_list' %}">Insurance Claims</a></li>
<li class="breadcrumb-item active">{% if object %}Edit{% else %}Submit{% endif %}</li>
</ol>
</nav>
{% if object %}
<div class="mt-2 small text-muted">
<strong>Claim #:</strong> <code>{{ object.claim_number }}</code>
</div>
{% endif %}
</div>
<div class="btn-group">
<a href="{% url 'billing:claim_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to List
</a>
{% if object %}
<a href="{% url 'billing:claim_detail' object.claim_id %}" class="btn btn-outline-primary">
<i class="fas fa-eye me-2"></i>View Details
</a>
{% endif %}
</div>
</div>
<form method="post"
id="claimForm"
hx-post="{% if object %}{% url 'billing:claim_update' object.claim_id %}{% else %}{% url 'billing:claim_create' %}{% endif %}"
hx-target="#form-container">
{% csrf_token %}
<div class="row">
<!-- Main Form -->
<div class="col-lg-8">
<div id="form-container">
<!-- Bill Selection (not a form field; handled in view) -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-file-invoice me-2"></i>Bill Information
</h5>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<div class="form-floating">
<select id="id_medical_bill" name="medical_bill" class="form-select">
<option value="">Select a bill...</option>
{% for bill in available_bills %}
<option value="{{ bill.bill_id }}"
{% if object and object.medical_bill.bill_id == bill.bill_id %}selected{% endif %}>
{{ bill.bill_number }} — {{ bill.patient.get_full_name }}
</option>
{% endfor %}
</select>
<label for="id_medical_bill">Medical Bill *</label>
</div>
</div>
{% if object %}
<div class="col-md-6">
<div class="alert alert-secondary mb-0 small">
<strong>Current Bill:</strong> {{ object.medical_bill.bill_number }}<br>
<strong>Patient:</strong> {{ object.medical_bill.patient.get_full_name }}
</div>
</div>
{% endif %}
</div>
<!-- Bill Details Display -->
<div id="bill-details" class="mt-3" style="display:none;">
<div class="alert alert-info">
<h6 class="alert-heading">Bill Details</h6>
<div class="row">
<div class="col-md-6">
<strong>Patient:</strong> <span id="bill-patient"></span><br>
<strong>Bill Number:</strong> <span id="bill-number"></span><br>
<strong>Bill Date:</strong> <span id="bill-date"></span>
</div>
<div class="col-md-6">
<strong>Total Amount:</strong> <span id="bill-total" class="text-success"></span><br>
<strong>Service Range:</strong> <span id="bill-service-range"></span><br>
<strong>Billing Provider:</strong> <span id="bill-provider"></span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Insurance & Dates (uses form fields) -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-shield-alt me-2"></i>Insurance & Service Dates
</h5>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<div class="form-floating">
{{ form.insurance_info }}
<label for="{{ form.insurance_info.id_for_label }}">Insurance Information *</label>
{% if form.insurance_info.errors %}<div class="invalid-feedback d-block">{{ form.insurance_info.errors.0 }}</div>{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="form-floating">
{{ form.service_date_from }}
<label for="{{ form.service_date_from.id_for_label }}">Service From *</label>
{% if form.service_date_from.errors %}<div class="invalid-feedback d-block">{{ form.service_date_from.errors.0 }}</div>{% endif %}
</div>
</div>
<div class="col-md-3">
<div class="form-floating">
{{ form.service_date_to }}
<label for="{{ form.service_date_to.id_for_label }}">Service To *</label>
{% if form.service_date_to.errors %}<div class="invalid-feedback d-block">{{ form.service_date_to.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
<!-- Insurance Details Display -->
<div id="insurance-details" class="mt-3" style="display:none;">
<div class="alert alert-light">
<h6 class="alert-heading">Insurance Details</h6>
<div class="row">
<div class="col-md-6">
<strong>Company:</strong> <span id="insurance-company"></span><br>
<strong>Policy #:</strong> <span id="insurance-policy"></span><br>
<strong>Group #:</strong> <span id="insurance-group"></span>
</div>
<div class="col-md-6">
<strong>Member ID:</strong> <span id="insurance-member"></span><br>
<strong>Plan Type:</strong> <span id="insurance-plan"></span><br>
<strong>Copay:</strong> <span id="insurance-copay"></span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Claim Details (from form) -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">
<i class="fas fa-file-medical me-2"></i>Claim Details
</h5>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<div class="form-floating">
{{ form.billed_amount }}
<label for="{{ form.billed_amount.id_for_label }}">Billed Amount *</label>
{% if form.billed_amount.errors %}<div class="invalid-feedback d-block">{{ form.billed_amount.errors.0 }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
{{ form.claim_type }}
<label for="{{ form.claim_type.id_for_label }}">Claim Type *</label>
{% if form.claim_type.errors %}<div class="invalid-feedback d-block">{{ form.claim_type.errors.0 }}</div>{% endif %}
</div>
</div>
<div class="col-md-4">
<div class="form-floating">
{{ form.clearinghouse }}
<label for="{{ form.clearinghouse.id_for_label }}">Clearinghouse</label>
{% if form.clearinghouse.errors %}<div class="invalid-feedback d-block">{{ form.clearinghouse.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
<div class="row g-3 mt-1">
<div class="col-md-6">
<div class="form-floating">
{{ form.batch_number }}
<label for="{{ form.batch_number.id_for_label }}">Batch Number</label>
{% if form.batch_number.errors %}<div class="invalid-feedback d-block">{{ form.batch_number.errors.0 }}</div>{% endif %}
</div>
</div>
<div class="col-md-6">
<div class="form-floating">
{{ form.prior_auth_number }}
<label for="{{ form.prior_auth_number.id_for_label }}">Prior Auth Number</label>
{% if form.prior_auth_number.errors %}<div class="invalid-feedback d-block">{{ form.prior_auth_number.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
<div class="row g-3 mt-1">
<div class="col-12">
<div class="form-floating">
{{ form.notes }}
<label for="{{ form.notes.id_for_label }}">Notes</label>
{% if form.notes.errors %}<div class="invalid-feedback d-block">{{ form.notes.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- Claim Line Items (client-side helper; not bound to form) -->
<div class="card mb-4">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-list me-2"></i>Claim Line Items
</h5>
<button type="button" class="btn btn-sm btn-outline-primary" onclick="addLineItem()">
<i class="fas fa-plus me-1"></i>Add Line Item
</button>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0" id="line-items-table">
<thead class="table-light">
<tr>
<th>Service Code *</th>
<th>Description *</th>
<th>Service Date *</th>
<th width="100">Quantity *</th>
<th width="120">Amount *</th>
<th width="80">Actions</th>
</tr>
</thead>
<tbody id="line-items-body"></tbody>
</table>
</div>
<div class="p-3 bg-light">
<div class="d-flex justify-content-between">
<span class="fw-bold">Total Billed Amount:</span>
<span class="fw-bold text-primary" id="total-billed-amount">$0.00</span>
</div>
</div>
</div>
</div>
<!-- Form Actions -->
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<a href="{% url 'billing:claim_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-times me-2"></i>Cancel
</a>
</div>
<div class="btn-group">
<button type="submit" name="action" value="save_draft" class="btn btn-outline-primary">
<i class="fas fa-save me-2"></i>Save as Draft
</button>
<button type="submit" name="action" value="save_and_submit" class="btn btn-success">
<i class="fas fa-paper-plane me-2"></i>{% if object %}Update{% else %}Submit{% endif %} Claim
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Claim Summary -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-calculator me-2"></i>Claim Summary</h5>
</div>
<div class="card-body">
<div class="d-flex justify-content-between mb-2">
<span>Total Billed:</span>
<span id="summary-billed" class="fw-bold text-primary">$0.00</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Line Items:</span>
<span id="summary-items">0</span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Expected Copay:</span>
<span id="summary-copay" class="text-warning">$0.00</span>
</div>
<hr>
<div class="d-flex justify-content-between fw-bold">
<span>Expected Payment:</span>
<span id="summary-expected" class="text-success">$0.00</span>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-bolt me-2"></i>Quick Actions</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button type="button" class="btn btn-outline-primary" onclick="copyFromBill()">
<i class="fas fa-copy me-2"></i>Copy from Bill
</button>
<button type="button" class="btn btn-outline-secondary" onclick="validateClaim()">
<i class="fas fa-check-circle me-2"></i>Validate Claim
</button>
<button type="button" class="btn btn-outline-info" onclick="previewClaim()">
<i class="fas fa-eye me-2"></i>Preview Claim
</button>
<button type="button" class="btn btn-outline-warning" onclick="checkEligibility()">
<i class="fas fa-shield-alt me-2"></i>Check Eligibility
</button>
</div>
</div>
</div>
<!-- Help -->
<div class="card">
<div class="card-header">
<h5 class="mb-0"><i class="fas fa-question-circle me-2"></i>Help & Guidelines</h5>
</div>
<div class="card-body">
<div class="accordion accordion-flush" id="helpAccordion">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#help-codes">
Coding Tips
</button>
</h2>
<div id="help-codes" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<ul class="list-unstyled mb-0">
<li><i class="fas fa-code text-primary me-2"></i>ICD-10 for diagnosis</li>
<li><i class="fas fa-code text-success me-2"></i>CPT/HCPCS for procedures</li>
<li><i class="fas fa-search text-info me-2"></i>Verify current codes</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#help-validation">
Validation Rules
</button>
</h2>
<div id="help-validation" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<ul class="list-unstyled mb-0">
<li>Service dates within coverage period</li>
<li>Valid codes & correct POS</li>
<li>Provider credentials verified</li>
</ul>
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#help-submission">
Submission Tips
</button>
</h2>
<div id="help-submission" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
<div class="accordion-body">
<ul class="list-unstyled mb-0">
<li>File within time limits</li>
<li>Attach required docs</li>
<li>Review before submit</li>
</ul>
</div>
</div>
</div>
</div> <!-- /accordion -->
</div>
</div>
</div>
</div>
</form>
</div>
<script>
let lineItemCounter = 0;
// Helpers
function toMoney(val) {
const n = parseFloat(val);
return isNaN(n) ? '0.00' : `${n.toFixed(2)}`;
}
// Hook up once the DOM is ready
document.addEventListener('DOMContentLoaded', function () {
const billSelect = document.getElementById('id_medical_bill');
if (!billSelect) return;
billSelect.addEventListener('change', function () {
const billId = this.value;
const details = document.getElementById('bill-details');
if (!billId) {
if (details) details.style.display = 'none';
return;
}
// Use the correct URL name and pass the billId
fetch(`{% url 'billing:bill_details_api' '00000000-0000-0000-0000-000000000000' %}`.replace('00000000-0000-0000-0000-000000000000', billId))
.then(r => {
if (!r.ok) throw new Error('Failed to fetch bill details');
return r.json();
})
.then(data => {
// Update the info panel
document.getElementById('bill-patient').textContent = data.patient_name || '-';
document.getElementById('bill-number').textContent = data.bill_number || '-';
document.getElementById('bill-date').textContent = data.bill_date || '-';
document.getElementById('bill-total').textContent = toMoney(data.total_amount);
// IMPORTANT: make sure this ID matches your template (bill-service-date vs bill-service-range)
const serviceEl = document.getElementById('bill-service-date') || document.getElementById('bill-service-range');
if (serviceEl) {
const from = data.service_date_from || '-';
const to = data.service_date_to || '-';
serviceEl.textContent = from && to ? `${from}${to}` : '-';
}
document.getElementById('bill-provider').textContent = data.billing_provider || '-';
if (details) details.style.display = 'block';
// Push defaults into the claim form
const billedInput = document.getElementById('{{ form.billed_amount.id_for_label }}') || document.querySelector('[name="billed_amount"]');
if (billedInput) billedInput.value = (parseFloat(data.total_amount) || 0).toFixed(2);
const fromInput = document.getElementById('{{ form.service_date_from.id_for_label }}') || document.querySelector('[name="service_date_from"]');
const toInput = document.getElementById('{{ form.service_date_to.id_for_label }}') || document.querySelector('[name="service_date_to"]');
if (fromInput && data.service_date_from) fromInput.value = data.service_date_from;
if (toInput && data.service_date_to) toInput.value = data.service_date_to;
if (typeof updateSummary === 'function') updateSummary();
})
.catch(err => {
console.error('Bill details error:', err);
if (details) details.style.display = 'none';
});
});
});
// Insurance selection -> details
const insuranceSelect = document.getElementById('{{ form.insurance_info.id_for_label }}');
{#if (insuranceSelect){#}
{# insuranceSelect.addEventListener('change', function(){#}
{# const insuranceId = this.value;#}
{# if (!insuranceId){#}
{# document.getElementById('insurance-details').style.display = 'none';#}
{# return;#}
{# }#}
{# fetch(`{% url 'billing:insurance_details_api' %}?insurance_id=${insuranceId}`)#}
{# .then(r => r.json())#}
{# .then(data => {#}
{# document.getElementById('insurance-company').textContent = data.company || '-';#}
{# document.getElementById('insurance-policy').textContent = data.policy_number || '-';#}
{# document.getElementById('insurance-group').textContent = data.group_number || '-';#}
{# document.getElementById('insurance-member').textContent = data.member_id || '-';#}
{# document.getElementById('insurance-plan').textContent = data.plan_type || '-';#}
{# document.getElementById('insurance-copay').textContent = data.copay_amount ? toMoney(data.copay_amount) : '0.00';#}
{# document.getElementById('insurance-details').style.display = 'block';#}
{# updateSummary();#}
{# })#}
{# .catch(err => console.error('Insurance details error:', err));#}
{# });#}
{# }#}
// Line items
function addLineItem(){
lineItemCounter++;
const tbody = document.getElementById('line-items-body');
const tr = document.createElement('tr');
tr.innerHTML = `
<td><input type="text" class="form-control form-control-sm" name="li_${lineItemCounter}_service_code" required></td>
<td><input type="text" class="form-control form-control-sm" name="li_${lineItemCounter}_description" required></td>
<td><input type="date" class="form-control form-control-sm" name="li_${lineItemCounter}_service_date" required></td>
<td><input type="number" class="form-control form-control-sm" name="li_${lineItemCounter}_quantity" value="1" min="1" required></td>
<td><input type="number" step="0.01" min="0" class="form-control form-control-sm line-item-amount" name="li_${lineItemCounter}_amount" value="0.00" required></td>
<td><button type="button" class="btn btn-sm btn-outline-danger" onclick="removeLineItem(this)"><i class="fas fa-trash"></i></button></td>
`;
tbody.appendChild(tr);
tr.querySelector('.line-item-amount').addEventListener('input', updateSummary);
updateSummary();
}
function removeLineItem(btn){
btn.closest('tr').remove(); updateSummary();
}
function updateSummary(){
let total = 0, items = 0;
document.querySelectorAll('.line-item-amount').forEach(inp=>{
const v = parseFloat(inp.value)||0;
total += v; if (v>0) items++;
});
document.getElementById('total-billed-amount').textContent = toMoney(total);
document.getElementById('summary-billed').textContent = toMoney(total);
document.getElementById('summary-items').textContent = items;
const copayText = (document.getElementById('insurance-copay')?.textContent||'$0').replace('$','');
const copay = parseFloat(copayText)||0;
const expected = Math.max(0, total - copay);
document.getElementById('summary-copay').textContent = toMoney(copay);
document.getElementById('summary-expected').textContent = toMoney(expected);
// Push to billed_amount field
const billedInput = document.getElementById('{{ form.billed_amount.id_for_label }}');
if (billedInput) billedInput.value = total.toFixed(2);
}
// Quick actions (optional endpoints)
function copyFromBill(){
const billId = document.getElementById('id_medical_bill').value;
if (!billId){ alert('Select a bill first.'); return; }
fetch(`{% url 'billing:bill_line_items_api' '00000000-0000-0000-0000-000000000000' %}`.replace('00000000-0000-0000-0000-000000000000', billId))
.then(r=>r.json())
.then(data=>{
document.getElementById('line-items-body').innerHTML='';
lineItemCounter=0;
(data.line_items||[]).forEach(item=>{
addLineItem();
const row = document.getElementById('line-items-body').lastElementChild;
row.querySelector(`[name="li_${lineItemCounter}_service_code"]`).value = item.service_code || '';
row.querySelector(`[name="li_${lineItemCounter}_description"]`).value = item.description || '';
row.querySelector(`[name="li_${lineItemCounter}_service_date"]`).value = item.service_date || '';
row.querySelector(`[name="li_${lineItemCounter}_quantity"]`).value = item.quantity || 1;
row.querySelector(`[name="li_${lineItemCounter}_amount"]`).value = item.unit_price || 0;
});
updateSummary();
})
.catch(err=>{ console.error(err); alert('Unable to copy line items from bill.'); });
}
function validateClaim(){
let ok = true;
document.querySelectorAll('[required]').forEach(el=>{
if (!String(el.value||'').trim()){ el.classList.add('is-invalid'); ok=false; }
else { el.classList.remove('is-invalid'); }
});
alert(ok ? 'Claim validation passed.' : 'Please complete all required fields.');
}
{#function previewClaim(){#}
{# const fd = new FormData(document.getElementById('claimForm'));#}
{# const w = window.open('', '_blank', 'width=900,height=700');#}
{# w.document.write('<h3>Claim Preview</h3><p>Loading…</p>');#}
{# fetch('{% url "billing:claim_preview" %}', {method:'POST', body:fd})#}
{# .then(r=>r.text())#}
{# .then(html=>{ w.document.open(); w.document.write(html); w.document.close(); })#}
{# .catch(()=>{ w.document.write('<p>Error generating preview.</p>'); });#}
{# }#}
function checkEligibility(){
const ins = document.getElementById('{{ form.insurance_info.id_for_label }}').value;
const from = document.getElementById('{{ form.service_date_from.id_for_label }}').value;
if (!ins || !from){ alert('Select insurance and service date(s) first.'); return; }
alert('Eligibility check initiated…');
setTimeout(()=>alert('Eligibility verified for the specified dates.'), 1200);
}
// Seed one row on create
{% if not object %}
document.addEventListener('DOMContentLoaded', ()=>{ addLineItem(); });
{% endif %}
// Final guard before submit
document.getElementById('claimForm').addEventListener('submit', function(e){
const rows = document.querySelectorAll('#line-items-body tr');
if (!rows.length){ e.preventDefault(); alert('Please add at least one line item.'); return false; }
const billed = parseFloat(document.getElementById('{{ form.billed_amount.id_for_label }}').value||0);
if (billed <= 0){ e.preventDefault(); alert('Billed amount must be greater than zero.'); return false; }
// Ensure summary → billed field is synced:
updateSummary();
});
</script>
{% endblock %}