/** * The smart scan box grammar (09-UX §1.2): one input, no modes. Typed and * scanned input both land here; the caller resolves codes against the item * cache and scale profile. */ export type EntryParse = | { kind: 'empty' } | { kind: 'multiplier'; qty: number; rest: string } | { kind: 'barcode'; code: string } | { kind: 'code'; code: string } | { kind: 'search'; text: string } const MULTIPLIER = /^(\d{1,4}(?:\.\d{1,3})?)\*(.*)$/ export function parseEntry(raw: string): EntryParse { const input = raw.trim() if (input === '') return { kind: 'empty' } const mult = MULTIPLIER.exec(input) if (mult !== null) { const qty = Number(mult[1]) if (qty > 0) return { kind: 'multiplier', qty, rest: mult[2]!.trim() } } if (/^\d+$/.test(input)) { // 8–14 digits reads as a barcode/item code; shorter digits are still codes // (quick-key PLUs); resolution order is the caller's concern. return input.length >= 8 ? { kind: 'barcode', code: input } : { kind: 'code', code: input } } return { kind: 'search', text: input } }