import { describe, expect, it } from 'vitest' import { openDb, type DB } from '../src/db' import { seedIfEmpty } from '../src/seed' import { commitBill } from '../src/repos' /** * Item-master anchoring (red-team C2, C3, H5, H7 + MRP/unit guards). commitBill is the * trust boundary: it rebuilds every line from OUR item row and treats any price the client * couldn't have gotten from the master as a deviation that needs a bound token or the * committer's own authority. Hermetic in-memory DB; seed items: 107 Amul Butter (master * sale 27500, MRP 28000, GST12, whole unit), 42 Tomato loose (KG, 3 decimals). */ function fresh(): DB { const db = openDb(':memory:') seedIfEmpty(db) return db } const itemId = (db: DB, code: string): string => (db.prepare(`SELECT id FROM item WHERE tenant_id='t1' AND code=?`).get(code) as { id: string }).id const billCount = (db: DB): number => (db.prepare('SELECT COUNT(*) n FROM bill').get() as { n: number }).n const line = (id: string, over: Partial<{ unitPricePaise: number; taxClassCode: string; qty: number }> = {}) => ({ itemId: id, name: 'Amul Butter 500g', hsn: '0405', qty: over.qty ?? 1, unitCode: 'PCS', unitPricePaise: over.unitPricePaise ?? 27_500, priceIncludesTax: true, taxClassCode: over.taxClassCode ?? 'GST12', }) const bill = (cashierId: string, l: ReturnType, payable: number): Parameters[1] => ({ tenantId: 't1', storeId: 's1', counterId: 'c2', cashierId, shiftId: 'sh1', businessDate: '2026-07-10', lines: [l], payments: [{ mode: 'CASH', amountPaise: payable }], clientPayablePaise: payable, }) describe('item-master anchoring', () => { it('commits a normal bill at the master price (happy path)', () => { const db = fresh() const out = commitBill(db, bill('u1', line(itemId(db, '107')), 27_500)) expect(out.docNo).toMatch(/^ST1C22026-/) // 4-digit-FY prefix (M9); business date 2026-07-10 → FY 2026-27 expect(billCount(db)).toBe(1) }) it('C2: a cashier underpricing a ₹275 item to ₹1 without approval is rejected (4xx)', () => { const db = fresh() const err = (() => { try { commitBill(db, bill('u1', line(itemId(db, '107'), { unitPricePaise: 100 }), 100)); return undefined } catch (e) { return e as Error & { status?: number } } })() expect(err).toBeDefined() expect(err!.status).toBeGreaterThanOrEqual(400) expect(err!.status).toBeLessThan(500) expect(billCount(db)).toBe(0) }) it('C3: a forged ZERO tax class is ignored — the bill is taxed at the master GST12 rate', () => { const db = fresh() // Inclusive price, so the ₹275 total is identical under ZERO or GST12; only the split // differs. The server anchors GST12, so the committed bill still carries CGST+SGST. commitBill(db, bill('u1', line(itemId(db, '107'), { taxClassCode: 'ZERO' }), 27_500)) const b = db.prepare(`SELECT cgst_paise, sgst_paise, taxable_paise FROM bill LIMIT 1`).get() as { cgst_paise: number; sgst_paise: number; taxable_paise: number } expect(b.cgst_paise + b.sgst_paise).toBeGreaterThan(0) expect(b.taxable_paise).toBeLessThan(27_500) // some of the sticker price is GST, not taxable base }) it('H5: a line for a non-existent item is rejected, nothing committed', () => { const db = fresh() expect(() => commitBill(db, bill('u1', line('DOES-NOT-EXIST'), 27_500))).toThrow(/E-1105|catalogue/i) expect(billCount(db)).toBe(0) }) it('H7: an owner self-authorises a price change and it is audited from their identity', () => { const db = fresh() commitBill(db, bill('u3', line(itemId(db, '107'), { unitPricePaise: 25_000 }), 25_000)) // u3 = owner const audit = db.prepare(`SELECT after_json FROM audit_log WHERE action='PRICE_OVERRIDE'`).get() as { after_json: string } | undefined expect(audit).toBeDefined() const after = JSON.parse(audit!.after_json) as { approvedByUserId: string; selfAuthorized?: boolean } expect(after.approvedByUserId).toBe('u3') expect(after.selfAuthorized).toBe(true) expect(billCount(db)).toBe(1) }) it('hard-blocks a price above MRP even for an owner (E-1301)', () => { const db = fresh() expect(() => commitBill(db, bill('u3', line(itemId(db, '107'), { unitPricePaise: 30_000 }), 30_000))).toThrow(/E-1301|MRP/i) expect(billCount(db)).toBe(0) }) it('rejects a fractional quantity on a whole-unit item (E-1104)', () => { const db = fresh() expect(() => commitBill(db, bill('u1', line(itemId(db, '107'), { qty: 1.5 }), 41_300))).toThrow(/E-1104|whole/i) expect(billCount(db)).toBe(0) }) })