mirror of
https://github.com/renee-png/acmcc.git
synced 2026-06-21 09:50:01 +00:00
Checks: print PDF now uses Check Setup settings (matches the sample)
Bulk "Print Checks" was rendering via a separate generator (utils/checkPdfGenerator + check_layouts), so the printed PDF ignored the accounting Check Setup: wrong return address (generic company vs association name + mailing address), missing vendor address, and ignored x/y field-position offsets. Route the print through the same generator as the working sample (lib/checkPdf.generateCheckPDF) fed by the accounting company (return address), check_settings (style/offsets/field_positions/signature/MICR gaps), the chosen bank account (routing/account), and the vendor address as payee. DB side effects (checks, transactions, bill paid status, check numbering) unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,8 @@ import { supabase } from "@/integrations/supabase/client";
|
|||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import { useAuth } from "@/contexts/AuthContext";
|
import { useAuth } from "@/contexts/AuthContext";
|
||||||
import { UserCheck, Plus, Search, Eye, Upload, X, ArrowUpDown, Edit, Trash2, MoreHorizontal, AlertTriangle, Loader2, Bell, Printer, Sparkles, Download } from "lucide-react";
|
import { UserCheck, Plus, Search, Eye, Upload, X, ArrowUpDown, Edit, Trash2, MoreHorizontal, AlertTriangle, Loader2, Bell, Printer, Sparkles, Download } from "lucide-react";
|
||||||
import { downloadChecksPdf, type CheckData } from "@/utils/checkPdfGenerator";
|
import { generateCheckPDF, type CheckData } from "@/pages/accounting/lib/checkPdf";
|
||||||
|
import { accounting } from "@/lib/accountingClient";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -697,13 +698,19 @@ export default function BillApprovalsPage({ boardAssociationIds }: { boardAssoci
|
|||||||
try {
|
try {
|
||||||
const { data: userData } = await supabase.auth.getUser();
|
const { data: userData } = await supabase.auth.getUser();
|
||||||
|
|
||||||
// Load per-association check layouts (optional)
|
// Load the accounting company (return address = association name + mailing
|
||||||
const { data: layoutsData } = await supabase
|
// address) and the saved Check Setup settings per association, so printed
|
||||||
.from("check_layouts")
|
// checks match the Check Setup sample exactly.
|
||||||
.select("*")
|
const { data: companiesData } = await accounting
|
||||||
.in("association_id", assocIds);
|
.from("companies").select("id, association_id, name, address").in("association_id", assocIds);
|
||||||
const layoutsByAssoc: Record<string, any> = {};
|
const companyByAssoc: Record<string, any> = {};
|
||||||
(layoutsData || []).forEach((l: any) => { layoutsByAssoc[l.association_id] = l; });
|
(companiesData || []).forEach((c: any) => { companyByAssoc[c.association_id] = c; });
|
||||||
|
const companyIds = (companiesData || []).map((c: any) => c.id);
|
||||||
|
const settingsByCompany: Record<string, any> = {};
|
||||||
|
if (companyIds.length) {
|
||||||
|
const { data: settingsData } = await accounting.from("check_settings").select("*").in("company_id", companyIds);
|
||||||
|
(settingsData || []).forEach((s: any) => { settingsByCompany[s.company_id] = s; });
|
||||||
|
}
|
||||||
|
|
||||||
let totalPrinted = 0;
|
let totalPrinted = 0;
|
||||||
let totalReprinted = 0;
|
let totalReprinted = 0;
|
||||||
@@ -717,7 +724,10 @@ export default function BillApprovalsPage({ boardAssociationIds }: { boardAssoci
|
|||||||
const billStatusUpdates: Array<{ billId: string; checkId: string; checkNumber: string }> = [];
|
const billStatusUpdates: Array<{ billId: string; checkId: string; checkNumber: string }> = [];
|
||||||
const txInserts: any[] = [];
|
const txInserts: any[] = [];
|
||||||
const today = new Date().toISOString().slice(0, 10);
|
const today = new Date().toISOString().slice(0, 10);
|
||||||
|
const todayDisplay = new Date(today + "T00:00:00").toLocaleDateString("en-US", { month: "2-digit", day: "2-digit", year: "numeric" });
|
||||||
const assocName = associations.find((a: any) => a.id === assocId)?.name || "";
|
const assocName = associations.find((a: any) => a.id === assocId)?.name || "";
|
||||||
|
const company = companyByAssoc[assocId] || null;
|
||||||
|
const settings: any = company ? (settingsByCompany[company.id] || {}) : {};
|
||||||
|
|
||||||
// Group bills by payee (vendor) when combining is enabled.
|
// Group bills by payee (vendor) when combining is enabled.
|
||||||
// Otherwise treat each bill as its own group.
|
// Otherwise treat each bill as its own group.
|
||||||
@@ -845,18 +855,27 @@ export default function BillApprovalsPage({ boardAssociationIds }: { boardAssoci
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkDataList.push({
|
checkDataList.push({
|
||||||
check_number: checkNumber,
|
companyName: company?.name || assocName || "",
|
||||||
check_date: today,
|
companyAddress: company?.address || undefined,
|
||||||
|
bankName: bank.bank_name || undefined,
|
||||||
|
bankAddress: settings.bank_address || undefined,
|
||||||
|
routingNumber: printIncludeMicr ? (bank.routing_number || undefined) : undefined,
|
||||||
|
accountNumber: printIncludeMicr ? (bank.account_number || undefined) : undefined,
|
||||||
|
fractionalRouting: settings.fractional_routing || undefined,
|
||||||
|
checkNumber: Number(checkNumber) || 0,
|
||||||
|
date: todayDisplay,
|
||||||
payee,
|
payee,
|
||||||
payee_address: payeeAddress,
|
payeeAddress: payeeAddress || undefined,
|
||||||
amount: totalAmount,
|
amount: totalAmount,
|
||||||
memo,
|
memo: memo || undefined,
|
||||||
line_items: lineItems.length > 1 ? lineItems : null,
|
lineItems: lineItems.length > 1
|
||||||
bank_account_name: bank.account_name,
|
? lineItems.map((li: any) => ({
|
||||||
bank_routing_number: bank.routing_number,
|
description: [li.invoice_number ? `Inv #${li.invoice_number}` : null, li.description].filter(Boolean).join(" — ") || "Payment",
|
||||||
bank_account_number: bank.account_number,
|
amount: Number(li.amount) || 0,
|
||||||
association_name: assocName,
|
}))
|
||||||
layout: (layoutsByAssoc[assocId] as any) || null,
|
: undefined,
|
||||||
|
printSignature: !!settings.print_signature,
|
||||||
|
signatureDataUrl: settings.signature_url || undefined,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -882,7 +901,24 @@ export default function BillApprovalsPage({ boardAssociationIds }: { boardAssoci
|
|||||||
}
|
}
|
||||||
|
|
||||||
const safeName = (assocName || "association").replace(/[^a-z0-9-_]+/gi, "_");
|
const safeName = (assocName || "association").replace(/[^a-z0-9-_]+/gi, "_");
|
||||||
await downloadChecksPdf(checkDataList, `checks-${safeName}-${today}.pdf`, { includeMicr: printIncludeMicr });
|
const opts: any = {
|
||||||
|
style: settings.default_style || "voucher",
|
||||||
|
position: settings.default_position || "top",
|
||||||
|
fontSize: settings.font_size || "medium",
|
||||||
|
offsetX: Number(settings.offset_x ?? 0),
|
||||||
|
offsetY: Number(settings.offset_y ?? 0),
|
||||||
|
micrOffsetY: Number(settings.micr_offset_y ?? 0),
|
||||||
|
micrGap1: Number(settings.micr_gap_1 ?? 1),
|
||||||
|
micrGap2: Number(settings.micr_gap_2 ?? 1),
|
||||||
|
fieldPositions: { ...(settings.field_positions || {}), ...(printIncludeMicr ? {} : { micr: { hidden: true } }) },
|
||||||
|
};
|
||||||
|
const dataUrl = generateCheckPDF(checkDataList, opts);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = dataUrl;
|
||||||
|
a.download = `checks-${safeName}-${today}.pdf`;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user