From e11a48e8bfe6470dea7e9573af6afe305f97233f Mon Sep 17 00:00:00 2001 From: renee-png Date: Fri, 19 Jun 2026 00:33:07 -0400 Subject: [PATCH] Reconciliation Checks: self-contained PDF button in the report The page-level exporter wasn't reliably producing the Reconciliation Checks PDF. Add a dedicated 'PDF' button inside the report card that generates it directly from the checks (Check/Assertion/Residual/Status, failing rows in red), independent of the toolbar export path. Co-Authored-By: Claude Opus 4.8 --- .../accounting/AccountingReportsPage.tsx | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/pages/accounting/AccountingReportsPage.tsx b/src/pages/accounting/AccountingReportsPage.tsx index a1721b0..2f0ef71 100644 --- a/src/pages/accounting/AccountingReportsPage.tsx +++ b/src/pages/accounting/AccountingReportsPage.tsx @@ -698,7 +698,7 @@ export default function AccountingReportsPage({ association }: { association?: { )} {active === "reconciliation" && ( - + )} {isFinancial && !(active === "pnl" && pnlMonthView) && ( @@ -1294,21 +1294,48 @@ function buildReconChecks(d: any): RecCheck[] { }); } -function ReconciliationReport({ d, currency }: { d: any; currency: string }) { +function ReconciliationReport({ d, currency, companyName, rangeLabel }: { d: any; currency: string; companyName?: string; rangeLabel?: string }) { if (!d) return
Loading…
; const checks = buildReconChecks(d); const ok = (r: number) => Math.abs(r) < 0.005; const allPass = checks.every((c) => c.pass); + // Self-contained PDF (doesn't depend on the page-level exporter). + const downloadPdf = () => { + const doc = new jsPDF({ unit: "pt", format: "letter" }); + doc.setFontSize(16); doc.setFont("helvetica", "bold"); + doc.text("Reconciliation Checks", 40, 50); + doc.setFontSize(10); doc.setFont("helvetica", "normal"); doc.setTextColor(90); + if (companyName) doc.text(companyName, 40, 68); + if (rangeLabel) doc.text(rangeLabel, 40, 82); + doc.setTextColor(0); + autoTable(doc, { + startY: 96, + head: [["Check", "Assertion", "Residual", "Status"]], + body: checks.map((c) => [c.id, c.label, money(c.residual, currency), c.pass ? "Pass" : "FAIL"]), + styles: { font: "helvetica", fontSize: 9, cellPadding: 5 }, + headStyles: { fillColor: [30, 41, 59], textColor: 255, fontStyle: "bold" }, + columnStyles: { 0: { cellWidth: 50 }, 2: { halign: "right", cellWidth: 90 }, 3: { halign: "right", cellWidth: 60 } }, + didParseCell: ({ section, row, column, cell }) => { + if (section === "body" && !checks[row.index]?.pass && (column.index === 2 || column.index === 3)) { + cell.styles.textColor = [220, 38, 38]; cell.styles.fontStyle = "bold"; + } + }, + margin: { left: 40, right: 40 }, + }); + doc.save(`reconciliation-checks-${(companyName || "company").replace(/[^a-z0-9]+/gi, "_")}.pdf`); + }; + return ( - + Reconciliation Checks {allPass ? "All passing" : "Residuals present"} +