diff --git a/src/app/api/v1/quotes/[id]/pdf/route.ts b/src/app/api/v1/quotes/[id]/pdf/route.ts index 4e6d962..6cad3ae 100644 --- a/src/app/api/v1/quotes/[id]/pdf/route.ts +++ b/src/app/api/v1/quotes/[id]/pdf/route.ts @@ -142,9 +142,15 @@ export async function POST( try { const pb = await getAdminPocketBase(); - // Quote + customer + // Quote + customer. We expand the customer's company relation + // too so the PDF can show the company name (not just the contact) + // in the "TO:" block. The Company record's `company` field is + // what the form picker shows in the form, and what the approvals + // list / customer search already surface — the PDF was the odd + // one out. PB inlines the relation in the same response, no + // extra round-trip. const quote = await pb.collection("quotes").getOne(quoteId, { - expand: "customer", + expand: "customer,customer.company", }); // No-quote / decline-letter flag. When true, we render every // active item through to the PDF regardless of price — this is diff --git a/src/lib/pdf/quote-document.test.tsx b/src/lib/pdf/quote-document.test.tsx index 5718159..d1323b9 100644 --- a/src/lib/pdf/quote-document.test.tsx +++ b/src/lib/pdf/quote-document.test.tsx @@ -51,16 +51,35 @@ const baseCustomer: Customer = { collectionName: "customers", created: "2026-08-17T00:00:00.000Z", updated: "2026-08-17T00:00:00.000Z", - customer: "Acme Mining (Pty) Ltd", - email: "buyer@acme.co.za", + // Contact name (was the company string before the 2026-09-02 fix + // that surfaces the company separately in the PDF "TO:" block). + customer: "Boetie van der Merwe", + email: "boetie@acme.co.za", number: "+27 11 555 0123", - company: "Acme", + company: "acme1", alternative_email: "", buyer_email: "", dateadded: "2026-01-01", description: "", lastvisiteddate: "", rep: "", + // Expanded company relation — mirrors the shape PB returns when + // the PDF route asks for `expand: "customer,customer.company"`. + // Without this the new "TO:" block can't render the company name. + expand: { + company: { + id: "acme1", + collectionId: "pbc_companies", + collectionName: "companies", + created: "2026-01-01T00:00:00.000Z", + updated: "2026-01-01T00:00:00.000Z", + company: "Acme Mining (Pty) Ltd", + address: "1 Mining Road, Carletonville", + vendor: "ACME001", + company_email: "info@acme.co.za", + company_tel: "+27 11 555 0100", + }, + }, }; const baseQuote: Quote = { diff --git a/src/lib/pdf/quote-document.tsx b/src/lib/pdf/quote-document.tsx index d856a91..fb83426 100644 --- a/src/lib/pdf/quote-document.tsx +++ b/src/lib/pdf/quote-document.tsx @@ -190,10 +190,21 @@ export function QuoteDocument({ {/* QUOTATION heading */} QUOTATION - {/* TO + Quote meta two-column row */} + {/* TO + Quote meta two-column row. + Company name (when present via the customer.company expand) + sits in bold above the contact name, mirroring the form + picker's "Company · Customer" pattern at + quote-form-contact-picker.tsx:94-95. The company line is + only rendered when the relation is expanded AND the + company record has a non-empty name — old PDFs / customers + without a linked company row just keep the contact-only + layout. */} TO: + {customer.expand?.company?.company ? ( + {customer.expand.company.company} + ) : null} {customer.customer} {customer.email} {customer.number} @@ -335,6 +346,16 @@ const styles = StyleSheet.create({ companyName: { fontSize: 16, fontFamily: "Helvetica-Bold", marginBottom: 6 }, body: { fontSize: 10, marginBottom: 2 }, bold: { fontFamily: "Helvetica-Bold" }, + // Company name in the "TO:" block (added 2026-09-02). Sits above + // the contact name in bold, mirroring the form picker's "Company + // bold, Contact smaller" pattern. Same font size as the rest of + // the meta column so it doesn't shout, just signals "this is the + // legal entity" before the contact's personal name. + toCompany: { + fontFamily: "Helvetica-Bold", + fontSize: 10, + marginBottom: 2, + }, // Logo — absolutely-positioned so it floats over the header without // pushing the QUOTATION heading down or fighting @react-pdf's // flexbox-Image placement bug. `top` is from the page top edge