fix financials
Tests / PHPUnit (push) Failing after 1m21s

This commit is contained in:
root
2026-07-18 22:57:40 -04:00
parent 068e739408
commit a30c1398a1
61 changed files with 10908 additions and 1775 deletions
+16 -53
View File
@@ -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;