import autoTable from "jspdf-autotable"; import { createBrandedPdf, addBrandedFooter, COLORS, TABLE_HEAD_STYLES, TABLE_BODY_STYLES, TABLE_STYLES, } from "./pdf-template"; interface PoItem { product_name: string; product_sku: string; quantity: number; unit_price: number; line_total: number; notes?: string | null; } interface PoPdfData { po_number: string; order_date: string; due_date?: string | null; customer_ref?: string | null; status: string; customer_name: string; customer_code: string; customer_email?: string | null; customer_phone?: string | null; customer_address?: string | null; customer_contact?: string | null; notes?: string | null; items: PoItem[]; } function formatCurrency(val: number): string { return new Intl.NumberFormat("en-GB", { style: "currency", currency: "EUR", }).format(val); } export function generatePoPdf(data: PoPdfData) { const { doc, contentY } = createBrandedPdf({ title: "PURCHASE ORDER", documentNumber: data.po_number, date: new Date(data.order_date).toLocaleDateString("en-GB"), recipient: { label: "BILL TO", name: data.customer_name, code: data.customer_code, contact: data.customer_contact, address: data.customer_address, phone: data.customer_phone, email: data.customer_email, }, }); const pageWidth = doc.internal.pageSize.getWidth(); const margin = 14; const contentWidth = pageWidth - margin * 2; // --- Order details row --- let y = contentY; doc.setFillColor(...COLORS.bgLight); doc.setDrawColor(...COLORS.border); doc.setLineWidth(0.2); doc.roundedRect(margin, y, contentWidth, 14, 1.5, 1.5, "FD"); const detailItems = [ { label: "Customer Ref", value: data.customer_ref || "-" }, { label: "Due Date", value: data.due_date ? new Date(data.due_date).toLocaleDateString("en-GB") : "-" }, { label: "Status", value: data.status.charAt(0).toUpperCase() + data.status.slice(1).replace("_", " ") }, ]; detailItems.forEach((item, i) => { const x = margin + 4 + i * (contentWidth / 3); doc.setFontSize(6.5); doc.setFont("helvetica", "normal"); doc.setTextColor(...COLORS.muted); doc.text(item.label, x, y + 5); doc.setFontSize(8); doc.setFont("helvetica", "bold"); doc.setTextColor(...COLORS.primary); doc.text(item.value, x, y + 11); }); y += 20; // --- Line Items table --- doc.setFontSize(9); doc.setFont("helvetica", "bold"); doc.setTextColor(...COLORS.primary); doc.text("Order Items", margin, y); y += 4; const totalValue = data.items.reduce((sum, item) => sum + item.line_total, 0); const totalQty = data.items.reduce((sum, item) => sum + item.quantity, 0); const tableData = data.items.map((item, i) => [ String(i + 1), `${item.product_name}\n${item.product_sku}`, String(item.quantity), formatCurrency(item.unit_price), formatCurrency(item.line_total), ]); autoTable(doc, { startY: y, head: [["#", "Product", "Qty", "Unit Price", "Total"]], body: tableData, foot: [["", "", String(totalQty), "", formatCurrency(totalValue)]], theme: "grid", headStyles: TABLE_HEAD_STYLES, bodyStyles: TABLE_BODY_STYLES, footStyles: { fillColor: [247, 247, 247], textColor: [23, 23, 23], fontSize: 8, fontStyle: "bold", cellPadding: 3, }, alternateRowStyles: { fillColor: [252, 252, 252] }, columnStyles: { 0: { cellWidth: 12, halign: "center" }, 1: { cellWidth: "auto" }, 2: { cellWidth: 20, halign: "center" }, 3: { cellWidth: 30, halign: "right" }, 4: { cellWidth: 30, halign: "right" }, }, margin: { left: margin, right: margin }, styles: TABLE_STYLES, }); // --- Grand Total box --- // eslint-disable-next-line @typescript-eslint/no-explicit-any const finalY = (doc as any).lastAutoTable?.finalY || y + 30; const totalBoxY = finalY + 4; const totalBoxW = 60; const totalBoxX = pageWidth - margin - totalBoxW; doc.setFillColor(...COLORS.bgLight); doc.setDrawColor(...COLORS.border); doc.setLineWidth(0.2); doc.roundedRect(totalBoxX, totalBoxY, totalBoxW, 12, 1.5, 1.5, "FD"); doc.setFontSize(7); doc.setFont("helvetica", "normal"); doc.setTextColor(...COLORS.muted); doc.text("TOTAL", totalBoxX + 4, totalBoxY + 5); doc.setFontSize(10); doc.setFont("helvetica", "bold"); doc.setTextColor(...COLORS.primary); doc.text(formatCurrency(totalValue), totalBoxX + totalBoxW - 4, totalBoxY + 9.5, { align: "right", }); // --- Notes --- if (data.notes) { const noteY = totalBoxY + 18; doc.setFillColor(...COLORS.bgLight); doc.setDrawColor(...COLORS.border); doc.setLineWidth(0.2); doc.roundedRect(margin, noteY, contentWidth, 16, 1.5, 1.5, "FD"); doc.setFontSize(6.5); doc.setFont("helvetica", "bold"); doc.setTextColor(...COLORS.accent); doc.text("NOTES", margin + 4, noteY + 5); doc.setFontSize(7.5); doc.setFont("helvetica", "normal"); doc.setTextColor(...COLORS.secondary); const noteLines = doc.splitTextToSize(data.notes, contentWidth - 8); doc.text(noteLines, margin + 4, noteY + 11); } addBrandedFooter(doc); return doc; }