// Purchase-order tax helpers. The UI works in percent (18 for 18%) because that is how an item's // tax rate is stored (`taxRatePercentage`) and how people say it; the PO API carries the rate as a // fraction 0..1 (0.18) and a stored amount. The server recomputes and stores the amount itself, so // these figures are for display and live totals only. /** 18 -> 0.18. Four decimals, the precision the API stores. */ export function pctToRate(pct: number): number { if (!Number.isFinite(pct) || pct <= 0) return 0 return Math.round(pct * 100) / 10000 } /** 0.18 -> 18. */ export function rateToPct(rate: number): number { if (!Number.isFinite(rate) || rate <= 0) return 0 return Math.round(rate * 10000) / 100 } /** * Tax amount for a line: qty x unit price x pct%, rounded half-up to 2 dp like the server * (`PurchaseOrderService.TaxAmountFor`). The epsilon keeps 1.005-style products from rounding down. */ export function lineTaxAmount(qty: number, unitPrice: number, pct: number): number { const q = Number(qty) const p = Number(unitPrice) const t = Number(pct) if (![q, p, t].every(Number.isFinite) || q <= 0 || p < 0 || t <= 0) return 0 return Math.round(q * p * t + 1e-9) / 100 } /** The text of a percent for an input box: 18 -> "18", 8.25 -> "8.25", 0 -> "0". */ export function pctText(pct: number | null | undefined): string { return pct === null || pct === undefined || !Number.isFinite(pct) ? "0" : String(pct) }