/** * Pure formatting helpers shared between the PDF document and any * preview / preview-text paths. Kept dependency-free so they can be * imported into both client and server contexts without dragging in * @react-pdf/renderer. */ /** "R 1 234.56" — the South African style used on the existing sample PDFs. */ export function formatZAR(amount: number): string { const safe = Number.isFinite(amount) ? amount : 0; const fixed = safe.toFixed(2); const [whole, frac] = fixed.split("."); // Group thousands with spaces (en-ZA locale style), keep the dot for the decimal. const grouped = whole.replace(/\B(?=(\d{3})+(?!\d))/g, " "); return `R ${grouped}.${frac}`; } /** "22 June 2026" — long form date for the Quote No / Date / Valid until block. */ export function formatDateLong(iso: string): string { if (!iso) return ""; const d = new Date(iso); if (isNaN(d.getTime())) return iso; return d.toLocaleDateString("en-GB", { day: "numeric", month: "long", year: "numeric", }); } /** Returns an ISO string for `iso + days`. Used for the "Valid until: +30 days" line. */ export function addDays(iso: string, days: number): string { const d = new Date(iso); d.setUTCDate(d.getUTCDate() + days); return d.toISOString(); } /** Sum of selling_price * quantity across an array of quote items. */ export function quoteSubtotal(items: { quantity: number; selling_price: number }[]): number { return items.reduce((sum, it) => sum + it.quantity * it.selling_price, 0); } /** ZAR VAT is 15% — round to 2dp so the printed total matches the line items. */ export function vatAmount(subtotal: number): number { return Math.round(subtotal * 0.15 * 100) / 100; } /** Default validity period for new quotes — the existing sample PDF shows exactly 30. */ export const QUOTE_VALID_DAYS = 30; /** * Effective "Date" printed on the quote PDF. Prefers the user-editable * `quote_date` (lets the office backdate a quote), falls back to the * PB row's `created` timestamp. */ export function effectiveQuoteDate( quote: { quote_date?: string; created?: string } ): string { return quote.quote_date || quote.created || ""; } /** * Effective validity period in days. Prefers `validity_days` on the * quote (lets the office extend to 2 months etc.), falls back to the * package default. */ export function effectiveValidityDays( quote: { validity_days?: number | null } ): number { return quote.validity_days ?? QUOTE_VALID_DAYS; }