// apps/hq/test/money-guards.test.ts — D24 (red-team): money-integrity guards. import { describe, it, expect } from 'vitest' import { openDb } from '../src/db' import { seedIfEmpty } from '../src/seed' import { createClient } from '../src/repos-clients' import { createModule, setPrice } from '../src/repos-modules' import { createDraft, issueDocument, createCreditNote, markStatus, getDocument } from '../src/repos-documents' import { recordPayment } from '../src/repos-payments' async function world() { const db = openDb(':memory:'); await seedIfEmpty(db) const c = await createClient(db, 'u1', { name: 'Bank', stateCode: '32' }) const m = await createModule(db, 'u1', { code: 'CORE', name: 'Core', allowedKinds: ['yearly'] }) await setPrice(db, 'u1', { moduleId: m.id, kind: 'yearly', pricePaise: 10_000_00, effectiveFrom: '2026-01-01' }) const inv = await issueDocument(db, 'u1', (await createDraft(db, 'u1', { docType: 'INVOICE', clientId: c.id, lines: [{ moduleId: m.id, qty: 1, kind: 'yearly' }], })).id) return { db, c, m, inv } } describe('credit-note cap (D24)', () => { it('a full-value CN succeeds; a second CN on the same invoice is refused (would exceed it)', async () => { const { db, inv } = await world() const cn1 = await createCreditNote(db, 'u1', inv.id) // full value expect(cn1.payablePaise).toBe(inv.payablePaise) await expect(createCreditNote(db, 'u1', inv.id)).rejects.toThrow(/exceeds the invoice/i) }) }) describe('markStatus type guard (D24)', () => { it('an issued invoice cannot be marked accepted/lost', async () => { const { db, inv } = await world() await expect(markStatus(db, 'u1', inv.id, 'lost')).rejects.toThrow(/Only quotations/i) await expect(markStatus(db, 'u1', inv.id, 'accepted')).rejects.toThrow(/Only quotations/i) // The invoice keeps its real status — still visible to overdue chasing. expect((await getDocument(db, inv.id))!.status).not.toBe('lost') }) }) describe('integer quantity guard (D24)', () => { it('a fractional / non-integer line quantity is rejected at draft', async () => { const { db, c, m } = await world() await expect(createDraft(db, 'u1', { docType: 'INVOICE', clientId: c.id, lines: [{ moduleId: m.id, qty: 1.5, kind: 'yearly' }], })).rejects.toThrow(/whole number/i) }) }) describe('TDS bound (D24)', () => { it('TDS greater than the cash received is refused', async () => { const { db, c, inv } = await world() await expect(recordPayment(db, 'u1', { clientId: c.id, receivedOn: '2026-07-10', mode: 'bank', amountPaise: 1_000_00, tdsPaise: 2_000_00, allocations: [{ documentId: inv.id, amountPaise: 3_000_00 }], })).rejects.toThrow(/TDS cannot exceed/i) }) })