diff --git a/src/components/cases/collections-tab.tsx b/src/components/cases/collections-tab.tsx index 400b0c5..9651de9 100644 --- a/src/components/cases/collections-tab.tsx +++ b/src/components/cases/collections-tab.tsx @@ -694,68 +694,10 @@ export function CollectionDetail({ theme: "grid", }); - // ── Amounts Due Breakdown (left), with sub-account subtotals ── - // Group entries by bucket -> account label -> subtotal - const subAccountTotals: Record> = {}; - BUCKETS.forEach((b) => (subAccountTotals[b] = {})); - entries.forEach((e: any) => { - const acctLabel = (e.account && String(e.account).trim()) || "—"; - BUCKETS.forEach((b) => { - const v = num(e[b]); - if (v) { - subAccountTotals[b][acctLabel] = (subAccountTotals[b][acctLabel] || 0) + v; - } - }); - }); + const cardEndY = (doc as any).lastAutoTable?.finalY ?? 150; - const breakdownBody: any[] = []; - // Order matches screenshot: Assessments, Late, Admin, Legal, Violations, Interest, Bank - const DISPLAY_ORDER: Array = [ - "assess", "late", "admin", "legal", "viol", "interest", "bank", - ]; - DISPLAY_ORDER.forEach((b) => { - const bucketTotal = Math.max(0, computed[b] ?? 0); - const label = - b === "interest" - ? `Interest @ ${(effectiveRate || 0).toFixed(2)}%` - : BUCKET_LABEL[b]; - breakdownBody.push([ - { content: label, styles: { fontStyle: "bold", fillColor: [245, 245, 245] } }, - { content: formatCurrency(bucketTotal), styles: { halign: "right", fontStyle: "bold", fillColor: [245, 245, 245] } }, - ]); - // Sub-account rows (only if there's more than one OR the single one differs from the bucket label) - const subs = subAccountTotals[b]; - const subKeys = Object.keys(subs); - if (subKeys.length > 0) { - subKeys - .sort((a, z) => subs[z] - subs[a]) - .forEach((sk) => { - breakdownBody.push([ - { content: ` ${sk}`, styles: { textColor: 90 } }, - { content: formatCurrency(subs[sk]), styles: { halign: "right", textColor: 90 } }, - ]); - }); - } - }); - - autoTable(doc, { - startY: 150, - margin: { left: margin, right: pageWidth / 2 + 20 }, - head: [["Amounts Due Breakdown", "Amount"]], - body: breakdownBody, - styles: { fontSize: 9, cellPadding: 4 }, - headStyles: { fillColor: [20, 20, 20], textColor: 255, fontStyle: "bold" }, - columnStyles: { - 0: { cellWidth: "auto" }, - 1: { halign: "right", cellWidth: 90 }, - }, - theme: "grid", - }); - - const breakdownEndY = (doc as any).lastAutoTable?.finalY ?? 150; - - // ── Transaction ledger table (full width below) ────────────── - const txStartY = breakdownEndY + 24; + // ── Transaction ledger table (full width below header) ────────────── + const txStartY = Math.max(cardEndY + 24, 150); const body: any[] = []; if (opening !== 0) { body.push([ @@ -769,9 +711,18 @@ export function CollectionDetail({ } withRunning.forEach((e: any) => { const isPayment = num(e.payment) > 0; + let desc = e.description || (e.account || "—"); + if (isPayment && e.allocations) { + const allocParts = BUCKETS + .filter((b) => num(e.allocations[b]) > 0) + .map((b) => `${BUCKET_SHORT[b]} ${formatCurrency(num(e.allocations[b]))}`); + if (allocParts.length > 0) { + desc = `${desc}\nApplied → ${allocParts.join(", ")}`; + } + } body.push([ e.entry_date ? formatDate(e.entry_date) : "", - e.description || (e.account || "—"), + desc, num(e.assess) ? num(e.assess).toFixed(2) : "", num(e.late) ? num(e.late).toFixed(2) : "", num(e.admin) ? num(e.admin).toFixed(2) : "",