This commit is contained in:
2026-02-20 10:09:01 -06:00
parent 444e8555f3
commit 7ba4eef5db
3 changed files with 53 additions and 14 deletions

View File

@@ -147,7 +147,12 @@ function renderInvoiceRow(invoice) {
: `<span class="text-gray-400 italic text-xs">Draft</span>`;
let statusBadge = '';
const amountPaid = parseFloat(invoice.amount_paid) || 0;
const balance = parseFloat(invoice.balance) ?? (parseFloat(invoice.total) - amountPaid);
const isPartiallyPaid = !paid && amountPaid > 0 && balance > 0;
if (paid) statusBadge = `<span class="inline-block px-2 py-0.5 text-xs font-semibold rounded-full bg-green-100 text-green-800" title="Paid ${formatDate(invoice.paid_date)}">Paid</span>`;
else if (isPartiallyPaid) statusBadge = `<span class="inline-block px-2 py-0.5 text-xs font-semibold rounded-full bg-yellow-100 text-yellow-800" title="Paid: $${amountPaid.toFixed(2)} / Balance: $${balance.toFixed(2)}">Partial $${amountPaid.toFixed(2)}</span>`;
else if (overdue) statusBadge = `<span class="inline-block px-2 py-0.5 text-xs font-semibold rounded-full bg-red-100 text-red-800" title="${daysSince(invoice.invoice_date)} days">Overdue</span>`;
// Send Date
@@ -203,7 +208,12 @@ function renderInvoiceRow(invoice) {
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${formatDate(invoice.invoice_date)}</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${sendDateDisplay}</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-500">${invoice.terms}</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 font-semibold">$${parseFloat(invoice.total).toFixed(2)}</td>
<td class="px-4 py-3 whitespace-nowrap text-sm text-gray-900 font-semibold">
${isPartiallyPaid
? `<span class="text-yellow-700">$${balance.toFixed(2)}</span> <span class="text-gray-400 text-xs line-through">$${parseFloat(invoice.total).toFixed(2)}</span>`
: `$${parseFloat(invoice.total).toFixed(2)}`
}
</td>
<td class="px-4 py-3 whitespace-nowrap text-sm font-medium space-x-1">
${editBtn} ${qboBtn} ${pdfBtn} ${htmlBtn} ${paidBtn} ${delBtn}
</td>

View File

@@ -37,9 +37,12 @@ export async function openPaymentModal(invoiceIds = []) {
const res = await fetch(`/api/invoices/${id}`);
const data = await res.json();
if (data.invoice) {
const total = parseFloat(data.invoice.total);
const amountPaid = parseFloat(data.invoice.amount_paid) || 0;
const balance = total - amountPaid;
selectedInvoices.push({
invoice: data.invoice,
payAmount: parseFloat(data.invoice.total)
payAmount: balance > 0 ? balance : total
});
}
} catch (e) { console.error('Error loading invoice:', id, e); }
@@ -82,9 +85,13 @@ async function addInvoiceById() {
const detailRes = await fetch(`/api/invoices/${match.id}`);
const detailData = await detailRes.json();
const detailInv = detailData.invoice;
const detailTotal = parseFloat(detailInv.total);
const detailPaid = parseFloat(detailInv.amount_paid) || 0;
const detailBalance = detailTotal - detailPaid;
selectedInvoices.push({
invoice: detailData.invoice,
payAmount: parseFloat(detailData.invoice.total)
invoice: detailInv,
payAmount: detailBalance > 0 ? detailBalance : detailTotal
});
renderInvoiceList();
@@ -223,8 +230,14 @@ function renderInvoiceList() {
container.innerHTML = selectedInvoices.map(si => {
const inv = si.invoice;
const total = parseFloat(inv.total);
const isPartial = si.payAmount < total;
const isOver = si.payAmount > total;
const amountPaid = parseFloat(inv.amount_paid) || 0;
const balance = total - amountPaid;
const isPartial = si.payAmount < balance;
const isOver = si.payAmount > balance;
const paidInfo = amountPaid > 0
? `<span class="text-green-600 text-xs ml-1">Paid: $${amountPaid.toFixed(2)}</span>`
: '';
return `
<div class="flex items-center justify-between px-4 py-3 border-b border-gray-100 last:border-0 hover:bg-gray-50">
@@ -232,6 +245,7 @@ function renderInvoiceList() {
<span class="font-medium text-gray-900">#${inv.invoice_number || 'Draft'}</span>
<span class="text-gray-500 text-sm ml-2 truncate">${inv.customer_name || ''}</span>
<span class="text-gray-400 text-xs ml-2">(Total: $${total.toFixed(2)})</span>
${paidInfo}
${isPartial ? '<span class="text-xs text-yellow-600 ml-1 font-semibold">Partial</span>' : ''}
${isOver ? '<span class="text-xs text-blue-600 ml-1 font-semibold">Overpay</span>' : ''}
</div>