import { describe, expect, it } from 'vitest' import { formatINR, fyOf, fyShort, uuidv7, timestampOfUuidv7, formatDocNo, nextDocNo, rolloverForFy, seriesPrefix, assertGstDocNo, gstinCheckDigit, validateGstin, deriveSupply, type DocSeries, } from '@sims/domain' describe('money — Indian grouping', () => { it('groups lakh/crore style', () => { expect(formatINR(123_456_789)).toBe('₹12,34,567.89') expect(formatINR(100)).toBe('₹1.00') expect(formatINR(-4_50)).toBe('-₹4.50') expect(formatINR(99_999, { symbol: false })).toBe('999.99') }) }) describe('financial year (Day Begin idiom)', () => { it('splits at 1 April', () => { expect(fyOf('2026-03-31')).toBe('2025-26') expect(fyOf('2026-04-01')).toBe('2026-27') expect(fyShort('2026-27')).toBe('26') }) }) describe('uuidv7', () => { it('is well-formed and carries its timestamp', () => { const at = 1_750_000_000_000 const id = uuidv7(at) expect(id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/) expect(timestampOfUuidv7(id)).toBe(at) }) it('sorts by creation time', () => { expect(uuidv7(1000) < uuidv7(2000)).toBe(true) }) }) describe('document series — offline per-counter numbering', () => { const series: DocSeries = { tenantId: 't', storeId: 's', counterId: 'c', docType: 'SALE', fy: '2026-27', prefix: seriesPrefix('ST1', 'C2', '26'), nextSeq: 123, } it('formats within the GST 16-char rule', () => { const { docNo, series: bumped } = nextDocNo(series) expect(docNo).toBe('ST1C2/26-000123') expect(docNo.length).toBeLessThanOrEqual(16) expect(bumped.nextSeq).toBe(124) }) it('rejects numbers that break the GST rule', () => { expect(() => assertGstDocNo('THIS/PREFIX/IS/TOO/LONG-000001')).toThrow(/16-char/) expect(() => formatDocNo('BAD_CHAR', 1)).toThrow(/16-char/) // underscore not allowed }) it('FY rollover restarts the sequence', () => { const rolled = rolloverForFy(series, '2027-28', seriesPrefix('ST1', 'C2', '27')) expect(rolled.nextSeq).toBe(1) expect(rolloverForFy(series, '2026-27', series.prefix)).toBe(series) }) }) describe('GSTIN validation', () => { const body = '32ABCDE1234F1Z' const valid = body + gstinCheckDigit(body) it('accepts a checksum-correct GSTIN and extracts the state', () => { expect(validateGstin(valid)).toEqual({ ok: true, stateCode: '32' }) }) it('rejects wrong checksum and wrong shape', () => { const flipped = body + (valid[14] === 'A' ? 'B' : 'A') expect(validateGstin(flipped).ok).toBe(false) expect(validateGstin('not-a-gstin').ok).toBe(false) }) }) describe('deriveSupply — place of supply at the counter', () => { const store = '32' // MegaMart's state const intraGstin = '32ABCDE1234F1Z' + gstinCheckDigit('32ABCDE1234F1Z') const interGstin = '27ABCDE1234F1Z' + gstinCheckDigit('27ABCDE1234F1Z') it('no GSTIN → B2C, place of supply is the store state (intra)', () => { expect(deriveSupply(undefined, store)).toMatchObject({ b2b: false, placeOfSupplyStateCode: '32', interState: false }) // A walk-in with only a state on file is still B2C — place of supply stays the store. expect(deriveSupply({ stateCode: '27' }, store)).toMatchObject({ b2b: false, placeOfSupplyStateCode: '32', interState: false }) }) it('GSTIN in the same state → B2B intra (CGST/SGST)', () => { expect(deriveSupply({ gstin: intraGstin }, store)).toMatchObject({ b2b: true, placeOfSupplyStateCode: '32', interState: false, buyerGstin: intraGstin, }) }) it('GSTIN in another state → B2B inter (IGST)', () => { expect(deriveSupply({ gstin: interGstin }, store)).toMatchObject({ b2b: true, placeOfSupplyStateCode: '27', interState: true, buyerGstin: interGstin, }) }) })