@@ -371,8 +371,7 @@
|
||||
<td>$<?= number_format((float)$invoice['paid_amount'], 2) ?></td>
|
||||
<td>$<?= number_format((float)($invoice['discount'] ?? 0), 2) ?></td>
|
||||
<td>$<?= number_format((float)($invoice['refund_paid'] ?? 0), 2) ?></td>
|
||||
<?php $tblBal = max(0, (float)($invoice['total_amount'] ?? 0) - (float)($invoice['paid_amount'] ?? 0) - (float)($invoice['discount'] ?? 0) - (float)($invoice['refund_paid'] ?? 0)); ?>
|
||||
<td>$<?= number_format($tblBal, 2) ?></td>
|
||||
<td>$<?= number_format((float)($invoice['balance'] ?? 0), 2) ?></td>
|
||||
<td><?= esc($invoice['status']) ?></td>
|
||||
<td><?= esc(!empty($invoice['due_date']) ? local_date($invoice['due_date'], 'm-d-Y') : '') ?></td>
|
||||
</tr>
|
||||
@@ -397,6 +396,7 @@
|
||||
<form method="post" action="<?= base_url('payment/manual_pay_update') ?>" id="addPaymentForm" enctype="multipart/form-data">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="search_term" value="<?= esc($searchTermUsedInSearch ?? '') ?>">
|
||||
<input type="hidden" name="idempotency_key" value="<?= esc(bin2hex(random_bytes(16))) ?>">
|
||||
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addPaymentModalLabel">Add Manual Payment</h5>
|
||||
@@ -419,17 +419,13 @@
|
||||
<option
|
||||
value="<?= esc($invoice['id']) ?>"
|
||||
data-balance="<?= esc($invoice['balance']) ?>"
|
||||
data-total="<?= esc($invoice['total_amount'] ?? $invoice['total'] ?? '') ?>"
|
||||
data-paid="<?= esc($invoice['paid_amount'] ?? 0) ?>"
|
||||
data-discount="<?= esc($invoice['discount'] ?? 0) ?>"
|
||||
data-refund="<?= esc($invoice['refund_paid'] ?? 0) ?>"
|
||||
data-display-total="<?= esc($invoice['display_total'] ?? $invoice['total_amount'] ?? '') ?>"
|
||||
data-balance-due-cents="<?= (int)($invoice['balance_due_cents'] ?? 0) ?>"
|
||||
data-customer-credit-cents="<?= (int)($invoice['customer_credit_cents'] ?? 0) ?>"
|
||||
data-next-installment="<?= (int)($invoice['next_installment'] ?? 1) ?>">
|
||||
<?php
|
||||
$uiTotal = (float)($invoice['total_amount'] ?? 0);
|
||||
$uiPaid = (float)($invoice['paid_amount'] ?? 0);
|
||||
$uiDisc = (float)($invoice['discount'] ?? 0);
|
||||
$uiRef = (float)($invoice['refund_paid'] ?? 0);
|
||||
$uiBal = max(0, $uiTotal - $uiPaid - $uiDisc - $uiRef);
|
||||
$uiBal = (float)($invoice['balance'] ?? 0);
|
||||
?>
|
||||
Invoice <?= esc($invoice['invoice_number']) ?> |
|
||||
Balance: $<?= number_format($uiBal, 2) ?> |
|
||||
@@ -615,20 +611,11 @@
|
||||
function getBalance() {
|
||||
const opt = currentOpt();
|
||||
if (!opt) return 0;
|
||||
// Compute from total - paid FIRST (more reliable if DB balance is stale)
|
||||
const totRaw = opt.getAttribute('data-total');
|
||||
const paidRaw = opt.getAttribute('data-paid');
|
||||
const discRaw = opt.getAttribute('data-discount');
|
||||
const refRaw = opt.getAttribute('data-refund');
|
||||
const tot = (totRaw !== null && totRaw !== '') ? parseFloat(totRaw) : NaN;
|
||||
const paid = (paidRaw !== null && paidRaw !== '') ? parseFloat(paidRaw) : NaN;
|
||||
const disc = (discRaw !== null && discRaw !== '') ? parseFloat(discRaw) : 0;
|
||||
const ref = (refRaw !== null && refRaw !== '') ? parseFloat(refRaw) : 0;
|
||||
if (isFinite(tot) && isFinite(paid)) {
|
||||
const computed = tot - paid - (isFinite(disc) ? disc : 0) - (isFinite(ref) ? ref : 0);
|
||||
if (isFinite(computed)) return Math.max(0, computed);
|
||||
const centsRaw = opt.getAttribute('data-balance-due-cents');
|
||||
const cents = (centsRaw !== null && centsRaw !== '') ? parseInt(centsRaw, 10) : NaN;
|
||||
if (isFinite(cents)) {
|
||||
return Math.max(0, cents / 100);
|
||||
}
|
||||
// Fallback to provided balance
|
||||
const raw = opt.getAttribute('data-balance');
|
||||
const bal = (raw !== null && raw !== '') ? parseFloat(raw) : NaN;
|
||||
return isFinite(bal) ? Math.max(0, bal) : 0;
|
||||
@@ -636,7 +623,7 @@
|
||||
|
||||
function getTotal() {
|
||||
const opt = currentOpt();
|
||||
const v = opt ? parseFloat(opt.getAttribute('data-total') || '0') : 0;
|
||||
const v = opt ? parseFloat(opt.getAttribute('data-display-total') || '0') : 0;
|
||||
return isFinite(v) ? v : 0;
|
||||
}
|
||||
|
||||
@@ -926,25 +913,7 @@
|
||||
}
|
||||
|
||||
const invoiceText = invOpt.text || ('Invoice #' + invOpt.value);
|
||||
// Derive balance: prefer total - paid - discount - refunds, else fallback to data-balance
|
||||
let balance = NaN;
|
||||
if (invOpt) {
|
||||
const rt = invOpt.getAttribute('data-total');
|
||||
const rp = invOpt.getAttribute('data-paid');
|
||||
const rd = invOpt.getAttribute('data-discount');
|
||||
const rr = invOpt.getAttribute('data-refund');
|
||||
const t = (rt !== null && rt !== '') ? parseFloat(rt) : NaN;
|
||||
const p = (rp !== null && rp !== '') ? parseFloat(rp) : NaN;
|
||||
const d = (rd !== null && rd !== '') ? parseFloat(rd) : 0;
|
||||
const r = (rr !== null && rr !== '') ? parseFloat(rr) : 0;
|
||||
if (isFinite(t) && isFinite(p)) {
|
||||
balance = t - p - (isFinite(d) ? d : 0) - (isFinite(r) ? r : 0);
|
||||
}
|
||||
if (!isFinite(balance)) {
|
||||
const rb = invOpt.getAttribute('data-balance');
|
||||
balance = (rb !== null && rb !== '') ? parseFloat(rb) : NaN;
|
||||
}
|
||||
}
|
||||
const balance = getBalance();
|
||||
const nextInst = invOpt.dataset?.nextInstallment || '';
|
||||
const instCount = $instCount() ? ($instCount().value || '') : '';
|
||||
const instAmt = $instAmt() ? ($instAmt().value || '') : '';
|
||||
@@ -1131,19 +1100,13 @@
|
||||
// Normalize initial state
|
||||
const inv = $invoice();
|
||||
if (inv && inv.options.length > 0) {
|
||||
// Prefer the first invoice whose (total - paid) > 0
|
||||
// Prefer the first invoice with a backend-provided amount due.
|
||||
let chosen = inv.selectedIndex;
|
||||
for (let i = 0; i < inv.options.length; i++) {
|
||||
const o = inv.options[i];
|
||||
const rt = o.getAttribute('data-total');
|
||||
const rp = o.getAttribute('data-paid');
|
||||
const rd = o.getAttribute('data-discount');
|
||||
const rr = o.getAttribute('data-refund');
|
||||
const t = (rt !== null && rt !== '') ? parseFloat(rt) : NaN;
|
||||
const p = (rp !== null && rp !== '') ? parseFloat(rp) : NaN;
|
||||
const d = (rd !== null && rd !== '') ? parseFloat(rd) : 0;
|
||||
const r = (rr !== null && rr !== '') ? parseFloat(rr) : 0;
|
||||
let b = (isFinite(t) && isFinite(p)) ? (t - p - (isFinite(d) ? d : 0) - (isFinite(r) ? r : 0)) : NaN;
|
||||
const centsRaw = o.getAttribute('data-balance-due-cents');
|
||||
const cents = (centsRaw !== null && centsRaw !== '') ? parseInt(centsRaw, 10) : NaN;
|
||||
let b = isFinite(cents) ? cents / 100 : NaN;
|
||||
if (!isFinite(b)) {
|
||||
const rb = o.getAttribute('data-balance');
|
||||
b = (rb !== null && rb !== '') ? parseFloat(rb) : NaN;
|
||||
|
||||
Reference in New Issue
Block a user