feat(hq): payment receipts — own RCT/ series, zero-GST acknowledgment PDF
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>feat/client-detail-redesign
parent
42cfa3d0b2
commit
b7085ce56e
@ -0,0 +1,53 @@
|
||||
// apps/hq/test/receipt.test.ts
|
||||
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, getDocument } from '../src/repos-documents'
|
||||
import { recordPayment, receiptForPayment } from '../src/repos-payments'
|
||||
import { documentHtml } from '../src/templates'
|
||||
|
||||
function setup() {
|
||||
const db = openDb(':memory:'); seedIfEmpty(db)
|
||||
const c = createClient(db, 'u1', { name: 'Acme', code: 'ACME', stateCode: '32', contacts: [{ name: 'R', email: 'r@acme.in' }] })
|
||||
const m = createModule(db, 'u1', { code: 'POS', name: 'POS', allowedKinds: ['yearly'] })
|
||||
setPrice(db, 'u1', { moduleId: m.id, kind: 'yearly', pricePaise: 10_000_00, effectiveFrom: '2026-01-01' })
|
||||
const inv = issueDocument(db, 'u1', createDraft(db, 'u1', { docType: 'INVOICE', clientId: c.id, lines: [{ moduleId: m.id, qty: 1, kind: 'yearly' }] }).id)
|
||||
return { db, c, inv }
|
||||
}
|
||||
|
||||
describe('payment receipts', () => {
|
||||
it('issues a zero-GST RECEIPT with its own RCT/ series on recordPayment', () => {
|
||||
const { db, c, inv } = setup()
|
||||
const out = recordPayment(db, 'u1', {
|
||||
clientId: c.id, receivedOn: '2026-07-10', mode: 'bank', amountPaise: 11_800_00,
|
||||
allocations: [{ documentId: inv.id, amountPaise: 11_800_00 }], issueReceipt: true,
|
||||
})
|
||||
expect(out.receipt).toBeDefined()
|
||||
const rc = out.receipt!
|
||||
expect(rc.docType).toBe('RECEIPT')
|
||||
expect(rc.docNo?.startsWith('RCT/')).toBe(true)
|
||||
expect(rc.payablePaise).toBe(11_800_00)
|
||||
expect(rc.cgstPaise + rc.sgstPaise + rc.igstPaise).toBe(0) // acknowledgment, not a tax document
|
||||
expect(rc.payload.receipt?.allocations[0]).toMatchObject({ docNo: inv.docNo, amountPaise: 11_800_00 })
|
||||
})
|
||||
it('generates a receipt after the fact from a stored payment', () => {
|
||||
const { db, c, inv } = setup()
|
||||
const out = recordPayment(db, 'u1', {
|
||||
clientId: c.id, receivedOn: '2026-07-10', mode: 'upi', amountPaise: 5_000_00,
|
||||
allocations: [{ documentId: inv.id, amountPaise: 5_000_00 }],
|
||||
})
|
||||
const rc = receiptForPayment(db, 'u1', out.payment.id)
|
||||
expect(rc.docType).toBe('RECEIPT')
|
||||
expect(getDocument(db, rc.id)!.docNo).toBe(rc.docNo)
|
||||
})
|
||||
it('renders a receipt acknowledgment (no SAC/GST table)', () => {
|
||||
const { db, c, inv } = setup()
|
||||
const out = recordPayment(db, 'u1', { clientId: c.id, receivedOn: '2026-07-10', mode: 'bank', amountPaise: 11_800_00, allocations: [{ documentId: inv.id, amountPaise: 11_800_00 }], issueReceipt: true })
|
||||
const html = documentHtml(out.receipt!, c, { 'company.name': 'Tecnostac' })
|
||||
expect(html).toContain('RECEIPT')
|
||||
expect(html).toContain('Received with thanks')
|
||||
expect(html).not.toContain('>SAC<') // the GST line table header is absent on receipts
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue