511 lines
28 KiB
HTML
511 lines
28 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% if object %}Edit Payment{% else %}Record Payment{% 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 Payment{% else %}Record Payment{% 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:payment_list' %}">Payments</a></li>
|
|
<li class="breadcrumb-item active">{% if object %}Edit{% else %}Record{% endif %}</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="btn-group">
|
|
<a href="{% url 'billing:payment_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:payment_detail' object.payment_id %}" class="btn btn-outline-primary">
|
|
<i class="fas fa-eye me-2"></i>View Details
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<form method="post" id="paymentForm" hx-post="{% if object %}{% url 'billing:payment_update' object.payment_id %}{% else %}{% url 'billing:payment_create' %}{% endif %}" hx-target="#form-container">
|
|
{% csrf_token %}
|
|
|
|
<div class="row">
|
|
<!-- Main Form -->
|
|
<div class="col-lg-8">
|
|
<div id="form-container">
|
|
<!-- Bill Information -->
|
|
<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">
|
|
{{ form.medical_bill }}
|
|
<label for="{{ form.medical_bill.id_for_label }}">Medical Bill *</label>
|
|
{% if form.medical_bill.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.medical_bill.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
{{ form.payment_number }}
|
|
<label for="{{ form.payment_number.id_for_label }}">Payment Number *</label>
|
|
{% if form.payment_number.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.payment_number.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</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>Paid Amount:</strong> <span id="bill-paid" class="text-primary"></span><br>
|
|
<strong>Balance:</strong> <span id="bill-balance" class="text-danger"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Payment Details -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-credit-card me-2"></i>Payment Details
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.payment_amount }}
|
|
<label for="{{ form.payment_amount.id_for_label }}">Payment Amount *</label>
|
|
{% if form.payment_amount.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.payment_amount.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.payment_date }}
|
|
<label for="{{ form.payment_date.id_for_label }}">Payment Date *</label>
|
|
{% if form.payment_date.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.payment_date.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="form-floating">
|
|
{{ form.payment_method }}
|
|
<label for="{{ form.payment_method.id_for_label }}">Payment Method *</label>
|
|
{% if form.payment_method.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.payment_method.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
{{ form.reference_number }}
|
|
<label for="{{ form.reference_number.id_for_label }}">Reference Number</label>
|
|
{% if form.reference_number.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.reference_number.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
{{ form.status }}
|
|
<label for="{{ form.status.id_for_label }}">Status *</label>
|
|
{% if form.status.errors %}
|
|
<div class="invalid-feedback d-block">
|
|
{{ form.status.errors.0 }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<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>
|
|
|
|
<!-- Payment Method Specific Fields -->
|
|
<div class="card mb-4" id="payment-method-details" style="display: none;">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-info-circle me-2"></i>Additional Details
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Credit/Debit Card Fields -->
|
|
<div id="card-fields" style="display: none;">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control" id="card_number" name="card_number" placeholder="Card Number">
|
|
<label for="card_number">Card Number (Last 4 digits)</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control" id="card_holder" name="card_holder" placeholder="Card Holder">
|
|
<label for="card_holder">Card Holder Name</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Check Fields -->
|
|
<div id="check-fields" style="display: none;">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control" id="check_number" name="check_number" placeholder="Check Number">
|
|
<label for="check_number">Check Number</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control" id="bank_name" name="bank_name" placeholder="Bank Name">
|
|
<label for="bank_name">Bank Name</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bank Transfer Fields -->
|
|
<div id="transfer-fields" style="display: none;">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control" id="transaction_id" name="transaction_id" placeholder="Transaction ID">
|
|
<label for="transaction_id">Transaction ID</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control" id="bank_account" name="bank_account" placeholder="Bank Account">
|
|
<label for="bank_account">Bank Account (Last 4 digits)</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Insurance Fields -->
|
|
<div id="insurance-fields" style="display: none;">
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control" id="insurance_company" name="insurance_company" placeholder="Insurance Company">
|
|
<label for="insurance_company">Insurance Company</label>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-floating">
|
|
<input type="text" class="form-control" id="claim_number" name="claim_number" placeholder="Claim Number">
|
|
<label for="claim_number">Claim Number</label>
|
|
</div>
|
|
</div>
|
|
</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:payment_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_process" class="btn btn-success">
|
|
<i class="fas fa-check me-2"></i>{% if object %}Update{% else %}Record{% endif %} Payment
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar -->
|
|
<div class="col-lg-4">
|
|
<!-- Payment Summary -->
|
|
<div class="card mb-4">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-calculator me-2"></i>Payment Summary
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Payment Amount:</span>
|
|
<span id="payment-amount-display" class="fw-bold text-success">$0.00</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between mb-2">
|
|
<span>Processing Fee:</span>
|
|
<span id="processing-fee">$0.00</span>
|
|
</div>
|
|
<hr>
|
|
<div class="d-flex justify-content-between fw-bold">
|
|
<span>Total:</span>
|
|
<span id="total-payment">$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="payFullBalance()">
|
|
<i class="fas fa-money-bill-wave me-2"></i>Pay Full Balance
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="calculatePartialPayment()">
|
|
<i class="fas fa-calculator me-2"></i>Calculate Partial
|
|
</button>
|
|
<button type="button" class="btn btn-outline-info" onclick="viewPaymentHistory()">
|
|
<i class="fas fa-history me-2"></i>Payment History
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Help Panel -->
|
|
<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-methods">
|
|
Payment Methods
|
|
</button>
|
|
</h2>
|
|
<div id="help-methods" class="accordion-collapse collapse" data-bs-parent="#helpAccordion">
|
|
<div class="accordion-body">
|
|
<ul class="list-unstyled mb-0">
|
|
<li><i class="fas fa-credit-card text-primary me-2"></i>Credit/Debit Cards</li>
|
|
<li><i class="fas fa-money-check text-success me-2"></i>Checks</li>
|
|
<li><i class="fas fa-university text-info me-2"></i>Bank Transfers</li>
|
|
<li><i class="fas fa-shield-alt text-warning me-2"></i>Insurance Payments</li>
|
|
<li><i class="fas fa-money-bill text-secondary me-2"></i>Cash</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><i class="fas fa-check text-success me-2"></i>Payment amount must be positive</li>
|
|
<li><i class="fas fa-check text-success me-2"></i>Cannot exceed bill balance</li>
|
|
<li><i class="fas fa-check text-success me-2"></i>Reference number required for cards</li>
|
|
<li><i class="fas fa-check text-success me-2"></i>Check number required for checks</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
// Payment method change handler
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const paymentMethodSelect = document.getElementById('{{ form.payment_method.id_for_label }}');
|
|
const paymentMethodDetails = document.getElementById('payment-method-details');
|
|
|
|
function togglePaymentMethodFields() {
|
|
const method = paymentMethodSelect.value;
|
|
|
|
// Hide all method-specific fields
|
|
document.getElementById('card-fields').style.display = 'none';
|
|
document.getElementById('check-fields').style.display = 'none';
|
|
document.getElementById('transfer-fields').style.display = 'none';
|
|
document.getElementById('insurance-fields').style.display = 'none';
|
|
|
|
// Show relevant fields based on method
|
|
if (method === 'CREDIT_CARD' || method === 'DEBIT_CARD') {
|
|
document.getElementById('card-fields').style.display = 'block';
|
|
paymentMethodDetails.style.display = 'block';
|
|
} else if (method === 'CHECK') {
|
|
document.getElementById('check-fields').style.display = 'block';
|
|
paymentMethodDetails.style.display = 'block';
|
|
} else if (method === 'BANK_TRANSFER') {
|
|
document.getElementById('transfer-fields').style.display = 'block';
|
|
paymentMethodDetails.style.display = 'block';
|
|
} else if (method === 'INSURANCE') {
|
|
document.getElementById('insurance-fields').style.display = 'block';
|
|
paymentMethodDetails.style.display = 'block';
|
|
} else {
|
|
paymentMethodDetails.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
paymentMethodSelect.addEventListener('change', togglePaymentMethodFields);
|
|
togglePaymentMethodFields(); // Initialize on page load
|
|
});
|
|
|
|
// Bill selection change handler
|
|
{#document.getElementById('{{ form.medical_bill.id_for_label }}').addEventListener('change', function() {#}
|
|
{# const billId = this.value;#}
|
|
{# if (billId) {#}
|
|
{# // Fetch bill details via HTMX or AJAX#}
|
|
{# fetch(`{% url 'billing:bill_details_api' %}?bill_id=${billId}`)#}
|
|
{# .then(response => response.json())#}
|
|
{# .then(data => {#}
|
|
{# 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 = `$${data.total_amount}`;#}
|
|
{# document.getElementById('bill-paid').textContent = `$${data.paid_amount}`;#}
|
|
{# document.getElementById('bill-balance').textContent = `$${data.balance_amount}`;#}
|
|
{# document.getElementById('bill-details').style.display = 'block';#}
|
|
{# #}
|
|
{# // Set max payment amount to balance#}
|
|
{# const paymentAmountField = document.getElementById('{{ form.payment_amount.id_for_label }}');#}
|
|
{# paymentAmountField.max = data.balance_amount;#}
|
|
{# })#}
|
|
{# .catch(error => {#}
|
|
{# console.error('Error fetching bill details:', error);#}
|
|
{# });#}
|
|
{# } else {#}
|
|
{# document.getElementById('bill-details').style.display = 'none';#}
|
|
{# }#}
|
|
{# });#}
|
|
|
|
// Payment amount change handler
|
|
document.getElementById('{{ form.payment_amount.id_for_label }}').addEventListener('input', function() {
|
|
const amount = parseFloat(this.value) || 0;
|
|
const processingFee = calculateProcessingFee(amount);
|
|
const total = amount + processingFee;
|
|
|
|
document.getElementById('payment-amount-display').textContent = `$${amount.toFixed(2)}`;
|
|
document.getElementById('processing-fee').textContent = `$${processingFee.toFixed(2)}`;
|
|
document.getElementById('total-payment').textContent = `$${total.toFixed(2)}`;
|
|
});
|
|
|
|
function calculateProcessingFee(amount) {
|
|
const method = document.getElementById('{{ form.payment_method.id_for_label }}').value;
|
|
|
|
// Example processing fee calculation
|
|
if (method === 'CREDIT_CARD' || method === 'DEBIT_CARD') {
|
|
return amount * 0.029; // 2.9% for cards
|
|
} else if (method === 'BANK_TRANSFER') {
|
|
return Math.min(amount * 0.01, 5.00); // 1% up to $5
|
|
}
|
|
return 0; // No fee for cash, check, insurance
|
|
}
|
|
|
|
function payFullBalance() {
|
|
const balanceElement = document.getElementById('bill-balance');
|
|
if (balanceElement) {
|
|
const balance = balanceElement.textContent.replace('$', '');
|
|
document.getElementById('{{ form.payment_amount.id_for_label }}').value = balance;
|
|
document.getElementById('{{ form.payment_amount.id_for_label }}').dispatchEvent(new Event('input'));
|
|
}
|
|
}
|
|
|
|
function calculatePartialPayment() {
|
|
const amount = prompt('Enter partial payment amount:');
|
|
if (amount && !isNaN(amount) && amount > 0) {
|
|
document.getElementById('{{ form.payment_amount.id_for_label }}').value = amount;
|
|
document.getElementById('{{ form.payment_amount.id_for_label }}').dispatchEvent(new Event('input'));
|
|
}
|
|
}
|
|
|
|
{#function viewPaymentHistory() {#}
|
|
{# const billId = document.getElementById('{{ form.medical_bill.id_for_label }}').value;#}
|
|
{# if (billId) {#}
|
|
{# window.open(`{% url 'billing:payment_history' %}?bill_id=${billId}`, '_blank');#}
|
|
{# } else {#}
|
|
{# alert('Please select a bill first.');#}
|
|
{# }#}
|
|
{# }#}
|
|
|
|
// Form validation
|
|
document.getElementById('paymentForm').addEventListener('submit', function(e) {
|
|
const paymentAmount = parseFloat(document.getElementById('{{ form.payment_amount.id_for_label }}').value);
|
|
const billBalance = parseFloat(document.getElementById('bill-balance')?.textContent?.replace('$', '') || 0);
|
|
|
|
if (paymentAmount > billBalance) {
|
|
e.preventDefault();
|
|
alert('Payment amount cannot exceed the bill balance.');
|
|
return false;
|
|
}
|
|
|
|
if (paymentAmount <= 0) {
|
|
e.preventDefault();
|
|
alert('Payment amount must be greater than zero.');
|
|
return false;
|
|
}
|
|
});
|
|
</script>
|
|
{% endblock %}
|
|
|