JEISA-WEBAPP/scripts/preview-quote-pdf.mjs

80 lines
2.5 KiB
JavaScript
Raw Normal View History

feat(quotes,customers): address + VAT on quotes, 30-day validity, searchable customer A quote should look like a document from a real company, so this adds the customer details a quote needs, gives every quote a valid-until date, and makes the customer picker usable once there are 20+ of them. Customer details on quotes - The `customers` collection only had a name, so the quote PDF's "Customer Information" block could only ever print a name. Added optional address, VAT number, email and phone fields. - The PDF block now grows to fit whatever is on file and prints the VAT number and contact line, so a filled-in customer produces a professional quote. - The quote details dialog shows the same details. - The customer form gained the new fields, and "Edit customer" now works - it was a menu item that did nothing, so there was no way to add a VAT number to an existing customer. - The customers table shows Address and VAT Number columns. Valid until - Quotes were created with an empty validUntil, so the PDF printed "Valid Until: N/A". The form now defaults it to 30 days after the quote date and keeps it in step when the date moves. The three existing quotes without one were backfilled the same way. Searchable customer picker - The customer dropdown listed every customer in a plain select with no way to add one that was missing. It is now a searchable combobox (by name, address or VAT number) that offers to create the customer when nothing matches, and creates it inline. Sidebar - Settings moved to a gear in the corner next to the app name, and Logs moved into Settings as a tab. The nav is now eight items with no grouping - grouping them would add a layer for no gain at this size. The old /logs link still lands on the logs tab. Also fixes a real PDF bug: item descriptions were cut to 40 characters, which is wider than the description column, so long names printed on top of the SKU column. Descriptions and SKUs are now measured against the real column width and truncated with an ellipsis. Live PocketBase changes (see scripts/add-customer-fields.mjs): - customers update and delete rules were null (superusers only), so a member could not edit the customer they had just added. They now match the other collections.
2026-10-21 02:45:58 +00:00
#!/usr/bin/env node
/**
* Renders a quote PDF against live data so the layout can be checked by
* eye - especially the wrapped item descriptions.
feat(quotes,customers): address + VAT on quotes, 30-day validity, searchable customer A quote should look like a document from a real company, so this adds the customer details a quote needs, gives every quote a valid-until date, and makes the customer picker usable once there are 20+ of them. Customer details on quotes - The `customers` collection only had a name, so the quote PDF's "Customer Information" block could only ever print a name. Added optional address, VAT number, email and phone fields. - The PDF block now grows to fit whatever is on file and prints the VAT number and contact line, so a filled-in customer produces a professional quote. - The quote details dialog shows the same details. - The customer form gained the new fields, and "Edit customer" now works - it was a menu item that did nothing, so there was no way to add a VAT number to an existing customer. - The customers table shows Address and VAT Number columns. Valid until - Quotes were created with an empty validUntil, so the PDF printed "Valid Until: N/A". The form now defaults it to 30 days after the quote date and keeps it in step when the date moves. The three existing quotes without one were backfilled the same way. Searchable customer picker - The customer dropdown listed every customer in a plain select with no way to add one that was missing. It is now a searchable combobox (by name, address or VAT number) that offers to create the customer when nothing matches, and creates it inline. Sidebar - Settings moved to a gear in the corner next to the app name, and Logs moved into Settings as a tab. The nav is now eight items with no grouping - grouping them would add a layer for no gain at this size. The old /logs link still lands on the logs tab. Also fixes a real PDF bug: item descriptions were cut to 40 characters, which is wider than the description column, so long names printed on top of the SKU column. Descriptions and SKUs are now measured against the real column width and truncated with an ellipsis. Live PocketBase changes (see scripts/add-customer-fields.mjs): - customers update and delete rules were null (superusers only), so a member could not edit the customer they had just added. They now match the other collections.
2026-10-21 02:45:58 +00:00
*
* Usage:
* node --experimental-strip-types scripts/preview-quote-pdf.mjs <quote_number> [out.pdf]
feat(quotes,customers): address + VAT on quotes, 30-day validity, searchable customer A quote should look like a document from a real company, so this adds the customer details a quote needs, gives every quote a valid-until date, and makes the customer picker usable once there are 20+ of them. Customer details on quotes - The `customers` collection only had a name, so the quote PDF's "Customer Information" block could only ever print a name. Added optional address, VAT number, email and phone fields. - The PDF block now grows to fit whatever is on file and prints the VAT number and contact line, so a filled-in customer produces a professional quote. - The quote details dialog shows the same details. - The customer form gained the new fields, and "Edit customer" now works - it was a menu item that did nothing, so there was no way to add a VAT number to an existing customer. - The customers table shows Address and VAT Number columns. Valid until - Quotes were created with an empty validUntil, so the PDF printed "Valid Until: N/A". The form now defaults it to 30 days after the quote date and keeps it in step when the date moves. The three existing quotes without one were backfilled the same way. Searchable customer picker - The customer dropdown listed every customer in a plain select with no way to add one that was missing. It is now a searchable combobox (by name, address or VAT number) that offers to create the customer when nothing matches, and creates it inline. Sidebar - Settings moved to a gear in the corner next to the app name, and Logs moved into Settings as a tab. The nav is now eight items with no grouping - grouping them would add a layer for no gain at this size. The old /logs link still lands on the logs tab. Also fixes a real PDF bug: item descriptions were cut to 40 characters, which is wider than the description column, so long names printed on top of the SKU column. Descriptions and SKUs are now measured against the real column width and truncated with an ellipsis. Live PocketBase changes (see scripts/add-customer-fields.mjs): - customers update and delete rules were null (superusers only), so a member could not edit the customer they had just added. They now match the other collections.
2026-10-21 02:45:58 +00:00
*
* Example:
* node --experimental-strip-types scripts/preview-quote-pdf.mjs QT-20260914-001 /tmp/quote.pdf
feat(quotes,customers): address + VAT on quotes, 30-day validity, searchable customer A quote should look like a document from a real company, so this adds the customer details a quote needs, gives every quote a valid-until date, and makes the customer picker usable once there are 20+ of them. Customer details on quotes - The `customers` collection only had a name, so the quote PDF's "Customer Information" block could only ever print a name. Added optional address, VAT number, email and phone fields. - The PDF block now grows to fit whatever is on file and prints the VAT number and contact line, so a filled-in customer produces a professional quote. - The quote details dialog shows the same details. - The customer form gained the new fields, and "Edit customer" now works - it was a menu item that did nothing, so there was no way to add a VAT number to an existing customer. - The customers table shows Address and VAT Number columns. Valid until - Quotes were created with an empty validUntil, so the PDF printed "Valid Until: N/A". The form now defaults it to 30 days after the quote date and keeps it in step when the date moves. The three existing quotes without one were backfilled the same way. Searchable customer picker - The customer dropdown listed every customer in a plain select with no way to add one that was missing. It is now a searchable combobox (by name, address or VAT number) that offers to create the customer when nothing matches, and creates it inline. Sidebar - Settings moved to a gear in the corner next to the app name, and Logs moved into Settings as a tab. The nav is now eight items with no grouping - grouping them would add a layer for no gain at this size. The old /logs link still lands on the logs tab. Also fixes a real PDF bug: item descriptions were cut to 40 characters, which is wider than the description column, so long names printed on top of the SKU column. Descriptions and SKUs are now measured against the real column width and truncated with an ellipsis. Live PocketBase changes (see scripts/add-customer-fields.mjs): - customers update and delete rules were null (superusers only), so a member could not edit the customer they had just added. They now match the other collections.
2026-10-21 02:45:58 +00:00
*/
import PocketBase from "pocketbase";
import fs from "node:fs/promises";
import { buildQuotePdf } from "../src/components/quotes/quotePdf.ts";
feat(quotes,customers): address + VAT on quotes, 30-day validity, searchable customer A quote should look like a document from a real company, so this adds the customer details a quote needs, gives every quote a valid-until date, and makes the customer picker usable once there are 20+ of them. Customer details on quotes - The `customers` collection only had a name, so the quote PDF's "Customer Information" block could only ever print a name. Added optional address, VAT number, email and phone fields. - The PDF block now grows to fit whatever is on file and prints the VAT number and contact line, so a filled-in customer produces a professional quote. - The quote details dialog shows the same details. - The customer form gained the new fields, and "Edit customer" now works - it was a menu item that did nothing, so there was no way to add a VAT number to an existing customer. - The customers table shows Address and VAT Number columns. Valid until - Quotes were created with an empty validUntil, so the PDF printed "Valid Until: N/A". The form now defaults it to 30 days after the quote date and keeps it in step when the date moves. The three existing quotes without one were backfilled the same way. Searchable customer picker - The customer dropdown listed every customer in a plain select with no way to add one that was missing. It is now a searchable combobox (by name, address or VAT number) that offers to create the customer when nothing matches, and creates it inline. Sidebar - Settings moved to a gear in the corner next to the app name, and Logs moved into Settings as a tab. The nav is now eight items with no grouping - grouping them would add a layer for no gain at this size. The old /logs link still lands on the logs tab. Also fixes a real PDF bug: item descriptions were cut to 40 characters, which is wider than the description column, so long names printed on top of the SKU column. Descriptions and SKUs are now measured against the real column width and truncated with an ellipsis. Live PocketBase changes (see scripts/add-customer-fields.mjs): - customers update and delete rules were null (superusers only), so a member could not edit the customer they had just added. They now match the other collections.
2026-10-21 02:45:58 +00:00
const PB_URL = process.env.POCKETBASE_URL || "https://pb.jeistk.apphub.co.za";
const PB_EMAIL = process.env.POCKETBASE_ADMIN_EMAIL || "david@wfsales.co.za";
const PB_PASSWORD = process.env.POCKETBASE_ADMIN_PASSWORD || "1234567890";
const QUOTE_NUMBER = process.argv[2] || "QT-20260914-001";
const OUT = process.argv[3] || `/tmp/quote-${QUOTE_NUMBER}.pdf`;
const pb = new PocketBase(PB_URL);
pb.autoCancellation(false);
await pb.admins.authWithPassword(PB_EMAIL, PB_PASSWORD);
const rec = await pb.collection("quotes").getFirstListItem(
`quote_number = "${QUOTE_NUMBER}"`,
{ expand: "customerName" },
);
const cust = rec.expand?.customerName || {};
const quantities = Array.isArray(rec.quantities) ? rec.quantities : [];
const unitPrices = Array.isArray(rec.unitPrices) ? rec.unitPrices : [];
const itemIds = Array.isArray(rec.items) ? rec.items : [];
let items = [];
if (itemIds.length) {
const filter = itemIds.map((id) => `id = "${id}"`).join(" || ");
const stock = await pb.collection("stock_items").getList(1, 200, { filter });
const byId = new Map(stock.items.map((s) => [s.id, s]));
items = itemIds.map((id, i) => ({
stockItemId: id,
itemName: byId.get(id)?.name || "(item)",
sku: byId.get(id)?.sku || "",
sellingPrice: unitPrices[i] || 0,
}));
}
const quote = {
id: rec.id,
quote_number: rec.quote_number,
customerName: cust.comapny_name || "",
customerRef: rec.customerRef || "",
quoteDate: rec.quoteDate,
validUntil: rec.validUntil,
status: rec.status,
quantities,
unitPrices,
items,
customer: {
name: cust.comapny_name || "",
address: cust.address || "",
email: cust.email || "",
phone: cust.phone || "",
vatNumber: cust.vatNumber || "",
},
notes: rec.notes || "",
};
const doc = buildQuotePdf(quote);
feat(quotes,customers): address + VAT on quotes, 30-day validity, searchable customer A quote should look like a document from a real company, so this adds the customer details a quote needs, gives every quote a valid-until date, and makes the customer picker usable once there are 20+ of them. Customer details on quotes - The `customers` collection only had a name, so the quote PDF's "Customer Information" block could only ever print a name. Added optional address, VAT number, email and phone fields. - The PDF block now grows to fit whatever is on file and prints the VAT number and contact line, so a filled-in customer produces a professional quote. - The quote details dialog shows the same details. - The customer form gained the new fields, and "Edit customer" now works - it was a menu item that did nothing, so there was no way to add a VAT number to an existing customer. - The customers table shows Address and VAT Number columns. Valid until - Quotes were created with an empty validUntil, so the PDF printed "Valid Until: N/A". The form now defaults it to 30 days after the quote date and keeps it in step when the date moves. The three existing quotes without one were backfilled the same way. Searchable customer picker - The customer dropdown listed every customer in a plain select with no way to add one that was missing. It is now a searchable combobox (by name, address or VAT number) that offers to create the customer when nothing matches, and creates it inline. Sidebar - Settings moved to a gear in the corner next to the app name, and Logs moved into Settings as a tab. The nav is now eight items with no grouping - grouping them would add a layer for no gain at this size. The old /logs link still lands on the logs tab. Also fixes a real PDF bug: item descriptions were cut to 40 characters, which is wider than the description column, so long names printed on top of the SKU column. Descriptions and SKUs are now measured against the real column width and truncated with an ellipsis. Live PocketBase changes (see scripts/add-customer-fields.mjs): - customers update and delete rules were null (superusers only), so a member could not edit the customer they had just added. They now match the other collections.
2026-10-21 02:45:58 +00:00
await fs.writeFile(OUT, Buffer.from(doc.output("arraybuffer")));
feat(quotes,customers): address + VAT on quotes, 30-day validity, searchable customer A quote should look like a document from a real company, so this adds the customer details a quote needs, gives every quote a valid-until date, and makes the customer picker usable once there are 20+ of them. Customer details on quotes - The `customers` collection only had a name, so the quote PDF's "Customer Information" block could only ever print a name. Added optional address, VAT number, email and phone fields. - The PDF block now grows to fit whatever is on file and prints the VAT number and contact line, so a filled-in customer produces a professional quote. - The quote details dialog shows the same details. - The customer form gained the new fields, and "Edit customer" now works - it was a menu item that did nothing, so there was no way to add a VAT number to an existing customer. - The customers table shows Address and VAT Number columns. Valid until - Quotes were created with an empty validUntil, so the PDF printed "Valid Until: N/A". The form now defaults it to 30 days after the quote date and keeps it in step when the date moves. The three existing quotes without one were backfilled the same way. Searchable customer picker - The customer dropdown listed every customer in a plain select with no way to add one that was missing. It is now a searchable combobox (by name, address or VAT number) that offers to create the customer when nothing matches, and creates it inline. Sidebar - Settings moved to a gear in the corner next to the app name, and Logs moved into Settings as a tab. The nav is now eight items with no grouping - grouping them would add a layer for no gain at this size. The old /logs link still lands on the logs tab. Also fixes a real PDF bug: item descriptions were cut to 40 characters, which is wider than the description column, so long names printed on top of the SKU column. Descriptions and SKUs are now measured against the real column width and truncated with an ellipsis. Live PocketBase changes (see scripts/add-customer-fields.mjs): - customers update and delete rules were null (superusers only), so a member could not edit the customer they had just added. They now match the other collections.
2026-10-21 02:45:58 +00:00
console.log(`Wrote ${OUT}`);
console.log(` customer: ${quote.customer.name}`);
console.log(` items : ${items.length}`);
for (const it of items) {
console.log(` - "${it.itemName}"`);
}