WFDASH-WEBAPP/src/lib/pdf/format.ts

70 lines
2.4 KiB
TypeScript
Raw Normal View History

/**
* 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;
}