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/functional-d26.test.ts

85 lines
4.3 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/functional-d26.test.ts — D26: GST summary, document search, bulk ops.
import { describe, it, expect } from 'vitest'
import { openDb } from '../src/db'
import { seedIfEmpty } from '../src/seed'
import { createClient, bulkUpdateClients, getClient } from '../src/repos-clients'
import { createModule, setPrice, assignModule } from '../src/repos-modules'
import { createDraft, issueDocument, createCreditNote, listDocuments, getDocument } from '../src/repos-documents'
import { gstSummary } from '../src/repos-reports'
import { listProjectMilestones, bulkSetMilestone } from '../src/repos-milestones'
async function world() {
const db = openDb(':memory:'); await seedIfEmpty(db) // company state 32, GST18
const c = await createClient(db, 'u1', { name: 'Karapuzha SCB', 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' })
return { db, c, m }
}
const mkInvoice = async (db: any, c: any, m: any, date: string) => {
const inv = await issueDocument(db, 'u1', (await createDraft(db, 'u1', {
docType: 'INVOICE', clientId: c.id, lines: [{ moduleId: m.id, qty: 1, kind: 'yearly' }],
})).id)
await db.run(`UPDATE document SET doc_date=? WHERE id=?`, date, inv.id)
return inv
}
describe('GST summary (D26, money locked)', () => {
it('sums invoices per month; credit notes subtract; CGST+SGST split exact', async () => {
const { db, c, m } = await world()
await mkInvoice(db, c, m, '2026-07-05') // ₹10,000 + 18% = 11,800; CGST 900 SGST 900
const inv2 = await mkInvoice(db, c, m, '2026-07-20')
await mkInvoice(db, c, m, '2026-08-01')
// A full, ISSUED credit note against inv2 (same month) reverses one invoice's tax.
const cnDraft = await createCreditNote(db, 'u1', inv2.id)
await issueDocument(db, 'u1', cnDraft.id) // a CN counts for GST only once numbered
await db.run(`UPDATE document SET doc_date='2026-07-25' WHERE id=?`, cnDraft.id)
void getDocument
const { rows, total } = await gstSummary(db, { from: '2026-07-01', to: '2026-08-31' })
const jul = rows.find((r) => r.period === '2026-07')!
expect(jul).toMatchObject({ invoiceCount: 2, creditNoteCount: 1 })
// 2 invoices 1 credit note = net 1 invoice's worth in July.
expect(jul.taxablePaise).toBe(10_000_00)
expect(jul.cgstPaise).toBe(900_00)
expect(jul.sgstPaise).toBe(900_00)
expect(jul.taxPaise).toBe(1_800_00)
expect(jul.totalPaise).toBe(11_800_00)
const aug = rows.find((r) => r.period === '2026-08')!
expect(aug.totalPaise).toBe(11_800_00)
expect(total.totalPaise).toBe(23_600_00) // net July (1) + Aug (1)
expect(total.taxPaise).toBe(3_600_00)
})
})
describe('document search (D26)', () => {
it('finds documents by number and by client name, case-insensitive', async () => {
const { db, c, m } = await world()
const inv = await mkInvoice(db, c, m, '2026-07-05')
expect((await listDocuments(db, { q: inv.docNo!.toLowerCase() })).total).toBe(1)
expect((await listDocuments(db, { q: 'KARAPUZHA' })).total).toBe(1)
expect((await listDocuments(db, { q: 'nomatch' })).total).toBe(0)
expect((await listDocuments(db, {})).total).toBe(1) // no q = all
})
})
describe('bulk operations (D26)', () => {
it('bulk-updates client fields in one call, each audited', async () => {
const { db } = await world()
const a = await createClient(db, 'u1', { name: 'A', stateCode: '32' })
const b = await createClient(db, 'u1', { name: 'B', stateCode: '32' })
const out = await bulkUpdateClients(db, 'u1', [a.id, b.id], { district: 'Ernakulam' })
expect(out.updated).toBe(2)
expect((await getClient(db, a.id))!.district).toBe('Ernakulam')
expect((await getClient(db, b.id))!.district).toBe('Ernakulam')
})
it('bulk-ticks a milestone across projects', async () => {
const { db, c, m } = await world()
const p = await assignModule(db, 'u1', { clientId: c.id, moduleId: m.id, kind: 'yearly' })
const out = await bulkSetMilestone(db, 'u1', [p.id], 'installed', true, '2026-07-10')
expect(out.updated).toBe(1)
const ms = await listProjectMilestones(db, p.id)
expect(ms.find((x) => x.key === 'installed')).toMatchObject({ done: true, doneOn: '2026-07-10' })
})
})