You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sims-hq/apps/hq/test/payments.test.ts

46 lines
2.9 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// apps/hq/test/payments.test.ts
import { describe, it, expect } from 'vitest'
import { openDb } from '../src/db'
import { createClient } from '../src/repos-clients'
import { createModule, setPrice } from '../src/repos-modules'
import { createDraft, issueDocument, getDocument } from '../src/repos-documents'
import { recordPayment, clientLedger, modulePaidView } from '../src/repos-payments'
async function invoiceFor(db: any, clientId: string, moduleId: string) {
return issueDocument(db, 'u1', (await createDraft(db, 'u1', { docType: 'INVOICE', clientId,
lines: [{ moduleId, qty: 1, kind: 'yearly' }] })).id)
}
describe('payments', () => {
it('oldest-first default allocation; part_paid then paid; leftover is an advance', async () => {
const db = openDb(':memory:')
await db.run(`INSERT INTO setting (key, value) VALUES ('company.state_code','32')`)
await db.run(`INSERT INTO tax_class (class_code, rate_pct_bp, effective_from) VALUES ('GST18', 1800, '2017-07-01')`)
const c = await createClient(db, 'u1', { name: 'Acme', stateCode: '32' })
const m = await createModule(db, 'u1', { code: 'POS', name: 'POS' })
await setPrice(db, 'u1', { moduleId: m.id, kind: 'yearly', pricePaise: 10_000_00, effectiveFrom: '2026-04-01' })
const inv1 = await invoiceFor(db, c.id, m.id) // ₹11,800
const inv2 = await invoiceFor(db, c.id, m.id) // ₹11,800
await recordPayment(db, 'u1', { clientId: c.id, receivedOn: '2026-07-10', mode: 'bank', amountPaise: 15_000_00 })
expect((await getDocument(db, inv1.id))!.status).toBe('paid') // 11,800 settled
expect((await getDocument(db, inv2.id))!.status).toBe('part_paid') // 3,200 of 11,800
await recordPayment(db, 'u1', { clientId: c.id, receivedOn: '2026-07-11', mode: 'upi', amountPaise: 10_000_00 })
expect((await getDocument(db, inv2.id))!.status).toBe('paid')
expect((await clientLedger(db, c.id)).advancePaise).toBe(1_400_00) // 25,000 23,600
})
it('invoice-minus-TDS settles in full', async () => {
const db = openDb(':memory:')
await db.run(`INSERT INTO setting (key, value) VALUES ('company.state_code','32')`)
await db.run(`INSERT INTO tax_class (class_code, rate_pct_bp, effective_from) VALUES ('GST18', 1800, '2017-07-01')`)
const c = await createClient(db, 'u1', { name: 'Acme', stateCode: '32' })
const m = await createModule(db, 'u1', { code: 'POS', name: 'POS' })
await setPrice(db, 'u1', { moduleId: m.id, kind: 'yearly', pricePaise: 10_000_00, effectiveFrom: '2026-04-01' })
const inv = await invoiceFor(db, c.id, m.id) // ₹11,800; client pays minus 10% TDS on ₹10,000
await recordPayment(db, 'u1', { clientId: c.id, receivedOn: '2026-07-10', mode: 'bank',
amountPaise: 10_800_00, tdsPaise: 1_000_00 })
expect((await getDocument(db, inv.id))!.status).toBe('paid')
const view = await modulePaidView(db, c.id)
expect(view[0]).toMatchObject({ moduleId: m.id, billedPaise: 11_800_00, settledPaise: 11_800_00 })
})
})