chore: upload parte 1 - logica
DevSecOps Enterprise Pipeline / security-gate-and-deploy (push) Failing after 41s
DevSecOps Enterprise Pipeline / security-gate-and-deploy (push) Failing after 41s
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user