import type { IsoDate } from '@sims/domain' /** * Tax rates are dated configuration rows, never constants (07-DB-AND-CONFIG §1; * the Sept-2025 GST 2.0 slab change is the precedent). Rates are basis points: * 18% → 1800. Resolution is by the bill's business date, so returns and credit * notes against old bills resolve the rate that was law on that day. */ export interface TaxClassRow { classCode: string ratePctBp: number cessPctBp: number effectiveFrom: IsoDate effectiveTo?: IsoDate } export function resolveTaxClass(rows: TaxClassRow[], classCode: string, onDate: IsoDate): TaxClassRow { let best: TaxClassRow | undefined for (const row of rows) { if (row.classCode !== classCode) continue if (row.effectiveFrom > onDate) continue if (row.effectiveTo !== undefined && row.effectiveTo < onDate) continue if (best === undefined || row.effectiveFrom > best.effectiveFrom) best = row } if (best === undefined) { // Silent defaults on tax are how wrong GST reaches the portal — fail loudly. throw new Error(`No tax rate for class "${classCode}" on ${onDate}`) } return best }