aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormihailo.vojinovic <mihailonvojinovic@gmail.com>2026-02-12 17:00:38 +0100
committermihailo.vojinovic <mihailonvojinovic@gmail.com>2026-02-12 17:00:38 +0100
commit6f35d60c329f488aa24d8f4cd159abf050ebb521 (patch)
tree382836af20cefd861213e4cf1ecff762ce519195 /src
parent5705cf6a39a8c4c0bde1b76993e709c9ba45888d (diff)
downloadinvoice-6f35d60c329f488aa24d8f4cd159abf050ebb521.tar.gz
invoice-6f35d60c329f488aa24d8f4cd159abf050ebb521.zip
Update generate to draw items and data
Diffstat (limited to 'src')
-rw-r--r--src/lib/generate.ts131
1 files changed, 129 insertions, 2 deletions
diff --git a/src/lib/generate.ts b/src/lib/generate.ts
index 4787f56..f815fe2 100644
--- a/src/lib/generate.ts
+++ b/src/lib/generate.ts
@@ -4,11 +4,138 @@ import { Invoice } from "@/models/invoice";
import PDFDocument from "pdfkit";
import blobStream from "blob-stream";
+const MARGIN = 30;
+const T_CELL_PADDING = 20;
+const T_PADDING = 4;
+const T_ROW_HEIGHT = 20;
+
+const createTableHeader = (
+ doc: PDFKit.PDFDocument,
+ params: {
+ itemText: string;
+ quantityText: string;
+ rateText: string;
+ amountText: string;
+ },
+) => {
+ doc.rect(doc.x, doc.y, doc.page.width - MARGIN * 2, T_ROW_HEIGHT).fill();
+ doc.fontSize(10).fillColor("white");
+ doc.text(params.itemText, MARGIN + T_PADDING, doc.y + 6);
+ doc.moveUp();
+ doc.text(
+ params.quantityText,
+ doc.page.width -
+ MARGIN -
+ doc.widthOfString(params.quantityText) -
+ (doc.widthOfString(params.amountText) + T_CELL_PADDING) -
+ (doc.widthOfString(params.rateText) + T_CELL_PADDING) -
+ T_PADDING,
+ doc.y,
+ );
+ doc.moveUp();
+ doc.text(
+ params.rateText,
+ doc.page.width -
+ MARGIN -
+ (doc.widthOfString(params.amountText) + T_CELL_PADDING) -
+ doc.widthOfString(params.rateText) -
+ T_PADDING,
+ doc.y,
+ );
+ doc.moveUp();
+ doc.text(
+ params.amountText,
+ doc.page.width - MARGIN - doc.widthOfString(params.amountText) - T_PADDING,
+ doc.y,
+ );
+ doc.fontSize(10).fillColor("black");
+ doc.text("", MARGIN + T_PADDING, doc.y + T_ROW_HEIGHT / 2);
+};
+
export async function generateInvoice(invoice: Invoice): Promise<Blob> {
- const doc = new PDFDocument();
+ const doc = new PDFDocument({ size: "A4", margin: MARGIN });
const stream = doc.pipe(blobStream());
- doc.text("Invoice from " + invoice.from);
+ // Invoice from
+ doc.fontSize(10);
+ doc.text(invoice.from);
+
+ // Invlice number and date
+ doc.moveUp();
+ doc.fontSize(12);
+
+ const invNumber = "# " + invoice.invoice_number;
+ doc.text(invNumber, doc.page.width - MARGIN - doc.widthOfString(invNumber));
+
+ if (invoice.date) {
+ doc.fontSize(10);
+ const invDate = invoice.date.toLocaleDateString();
+ doc.text(invDate, doc.page.width - MARGIN - doc.widthOfString(invDate));
+ }
+
+ // Separator
+ doc.moveTo(MARGIN, doc.y + 5);
+ doc.lineTo(doc.page.width - MARGIN, doc.y + 5).stroke();
+ doc.moveDown();
+
+ // Invoice title
+ doc.fontSize(24);
+ doc.text("INVOICE", MARGIN, doc.y + 3);
+ doc.moveDown();
+
+ // Bill to and details
+ if (invoice.bill_to) {
+ doc.fontSize(8);
+ doc.text("BILL TO");
+ doc.text(invoice.bill_to);
+ doc.moveUp(2);
+ doc.text("", MARGIN + doc.widthOfString("BILL TO") * 3, doc.y);
+ }
+
+ if (invoice.po_number) {
+ doc.fontSize(8);
+ doc.text("DETAILS");
+ doc.text("Po number: " + invoice.po_number);
+ doc.text("", MARGIN, doc.y + 10);
+ }
+
+ const params = {
+ itemText: "ITEM",
+ quantityText: "QUANTITY",
+ rateText: "RATE",
+ amountText: "AMOUNT",
+ };
+
+ createTableHeader(doc, params);
+
+ invoice.line_items.forEach((item) => {
+ const amount = (item.quantity * item.rate).toString();
+ doc.text(item.description);
+ doc.moveUp();
+ doc.text(
+ amount,
+ doc.page.width - MARGIN - doc.widthOfString(amount) - T_PADDING,
+ doc.y,
+ );
+ doc.moveUp();
+ doc.text(
+ item.rate.toString(),
+ doc.x -
+ doc.widthOfString(params.amountText) -
+ doc.widthOfString(item.rate.toString()) / 2 -
+ T_CELL_PADDING,
+ doc.y,
+ );
+ doc.moveUp();
+ doc.text(
+ item.quantity.toString(),
+ doc.x -
+ (doc.widthOfString(params.quantityText + item.quantity) / 2 +
+ T_CELL_PADDING),
+ doc.y,
+ );
+ doc.text("", MARGIN + T_PADDING, doc.y + T_ROW_HEIGHT / 2);
+ });
doc.end();
return new Promise((resolve, reject) => {