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.
67 lines
3.1 KiB
TypeScript
67 lines
3.1 KiB
TypeScript
// apps/hq/test/milestone-payment.test.ts — payment_id on milestone + link on payment-step tick.
|
|
import { describe, it, expect } from 'vitest'
|
|
import { openDb } from '../src/db'
|
|
import { seedIfEmpty } from '../src/seed'
|
|
import { createClient } from '../src/repos-clients'
|
|
import { createModule, assignModule } from '../src/repos-modules'
|
|
import { setMilestone, listProjectMilestones } from '../src/repos-milestones'
|
|
import { recordPayment } from '../src/repos-payments'
|
|
|
|
async function world() {
|
|
const db = openDb(':memory:')
|
|
await seedIfEmpty(db)
|
|
const c = await createClient(db, 'u1', { name: 'Kothavara SCB', stateCode: '32' })
|
|
const m = await createModule(db, 'u1', { code: 'SMS', name: 'Bulk SMS' })
|
|
const cm = await assignModule(db, 'u1', { clientId: c.id, moduleId: m.id, kind: 'yearly' })
|
|
return { db, clientId: c.id, cmId: cm.id }
|
|
}
|
|
|
|
describe('milestone payment_id link (payment-step tick)', () => {
|
|
it('linking a payment to payment_received stores payment_id and mirrors received_on', async () => {
|
|
const { db, clientId, cmId } = await world()
|
|
const pay = await recordPayment(db, 'u1', {
|
|
clientId, receivedOn: '2026-05-01', mode: 'bank', amountPaise: 1000_00,
|
|
})
|
|
await setMilestone(db, 'u1', cmId, 'payment_received', true, undefined, pay.payment.id)
|
|
const ms = (await listProjectMilestones(db, cmId)).find((m) => m.key === 'payment_received')!
|
|
expect(ms.done).toBe(true)
|
|
expect(ms.doneOn).toBe('2026-05-01')
|
|
expect(ms.paymentId).toBe(pay.payment.id)
|
|
})
|
|
|
|
it('rejects a payment that belongs to a different client', async () => {
|
|
const { db, cmId } = await world()
|
|
const other = await createClient(db, 'u1', { name: 'Other Bank', stateCode: '32' })
|
|
const pay = await recordPayment(db, 'u1', {
|
|
clientId: other.id, receivedOn: '2026-05-01', mode: 'bank', amountPaise: 500_00,
|
|
})
|
|
await expect(
|
|
setMilestone(db, 'u1', cmId, 'payment_received', true, undefined, pay.payment.id),
|
|
).rejects.toThrow(/payment/i)
|
|
})
|
|
|
|
it('unticking clears payment_id along with done_on', async () => {
|
|
const { db, clientId, cmId } = await world()
|
|
const pay = await recordPayment(db, 'u1', {
|
|
clientId, receivedOn: '2026-05-01', mode: 'bank', amountPaise: 1000_00,
|
|
})
|
|
await setMilestone(db, 'u1', cmId, 'payment_received', true, undefined, pay.payment.id)
|
|
await setMilestone(db, 'u1', cmId, 'payment_received', false)
|
|
const ms = (await listProjectMilestones(db, cmId)).find((m) => m.key === 'payment_received')!
|
|
expect(ms.done).toBe(false)
|
|
expect(ms.doneOn).toBeNull()
|
|
expect(ms.paymentId).toBeNull()
|
|
})
|
|
|
|
it('an explicit doneOn wins over the payment received_on', async () => {
|
|
const { db, clientId, cmId } = await world()
|
|
const pay = await recordPayment(db, 'u1', {
|
|
clientId, receivedOn: '2026-05-01', mode: 'bank', amountPaise: 1000_00,
|
|
})
|
|
await setMilestone(db, 'u1', cmId, 'payment_received', true, '2026-05-10', pay.payment.id)
|
|
const ms = (await listProjectMilestones(db, cmId)).find((m) => m.key === 'payment_received')!
|
|
expect(ms.doneOn).toBe('2026-05-10')
|
|
expect(ms.paymentId).toBe(pay.payment.id)
|
|
})
|
|
})
|