161 lines
5.3 KiB
TypeScript
161 lines
5.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { getPool } from './setup'
|
|
import { insertAuthUser, insertCompany, insertFiscalPeriod } from './fixtures'
|
|
|
|
describe('BFL retention expiry', () => {
|
|
it('runs the corrected retention trigger after the original migration 017 trigger', async () => {
|
|
const result = await getPool().query<{
|
|
trigger_name: string
|
|
trigger_definition: string
|
|
}>(
|
|
`SELECT
|
|
trigger.tgname AS trigger_name,
|
|
pg_get_triggerdef(trigger.oid) AS trigger_definition
|
|
FROM pg_trigger trigger
|
|
JOIN pg_class target ON target.oid = trigger.tgrelid
|
|
JOIN pg_namespace schema ON schema.oid = target.relnamespace
|
|
WHERE schema.nspname = 'public'
|
|
AND target.relname = 'fiscal_periods'
|
|
AND NOT trigger.tgisinternal
|
|
AND trigger.tgname IN (
|
|
'calculate_retention_expiry',
|
|
'zz_set_bfl_retention_expiry',
|
|
'enforce_period_start_day'
|
|
)
|
|
ORDER BY trigger.tgname`,
|
|
)
|
|
|
|
expect(result.rows).toEqual([
|
|
{
|
|
trigger_name: 'calculate_retention_expiry',
|
|
trigger_definition:
|
|
'CREATE TRIGGER calculate_retention_expiry BEFORE INSERT OR UPDATE ON public.fiscal_periods FOR EACH ROW EXECUTE FUNCTION calculate_retention_expiry()',
|
|
},
|
|
{
|
|
trigger_name: 'enforce_period_start_day',
|
|
trigger_definition:
|
|
'CREATE TRIGGER enforce_period_start_day BEFORE INSERT OR UPDATE OF company_id, period_start ON public.fiscal_periods FOR EACH ROW EXECUTE FUNCTION enforce_first_of_month_for_subsequent_periods()',
|
|
},
|
|
{
|
|
trigger_name: 'zz_set_bfl_retention_expiry',
|
|
trigger_definition:
|
|
'CREATE TRIGGER zz_set_bfl_retention_expiry BEFORE INSERT OR UPDATE ON public.fiscal_periods FOR EACH ROW EXECUTE FUNCTION set_bfl_retention_expiry()',
|
|
},
|
|
])
|
|
})
|
|
|
|
it('retains a fiscal year through the end of the seventh following calendar year', async () => {
|
|
const userId = await insertAuthUser()
|
|
const companyId = await insertCompany({ createdBy: userId })
|
|
const fiscalPeriodId = await insertFiscalPeriod({
|
|
userId,
|
|
companyId,
|
|
periodStart: '2025-07-01',
|
|
periodEnd: '2026-06-30',
|
|
name: '2025/2026',
|
|
})
|
|
|
|
const result = await getPool().query<{ retention_expires_at: string }>(
|
|
`SELECT retention_expires_at::text
|
|
FROM public.fiscal_periods
|
|
WHERE id = $1`,
|
|
[fiscalPeriodId],
|
|
)
|
|
|
|
expect(result.rows[0].retention_expires_at).toBe('2034-01-01')
|
|
})
|
|
|
|
it('recalculates the first allowed deletion date when an open period end changes', async () => {
|
|
const userId = await insertAuthUser()
|
|
const companyId = await insertCompany({ createdBy: userId })
|
|
const fiscalPeriodId = await insertFiscalPeriod({ userId, companyId })
|
|
|
|
await getPool().query(
|
|
`UPDATE public.fiscal_periods
|
|
SET period_end = '2027-03-31'
|
|
WHERE id = $1`,
|
|
[fiscalPeriodId],
|
|
)
|
|
const result = await getPool().query<{ retention_expires_at: string }>(
|
|
`SELECT retention_expires_at::text
|
|
FROM public.fiscal_periods
|
|
WHERE id = $1`,
|
|
[fiscalPeriodId],
|
|
)
|
|
|
|
expect(result.rows[0].retention_expires_at).toBe('2035-01-01')
|
|
})
|
|
|
|
it('does not restore the old expiry calculation on unrelated updates', async () => {
|
|
const userId = await insertAuthUser()
|
|
const companyId = await insertCompany({ createdBy: userId })
|
|
const fiscalPeriodId = await insertFiscalPeriod({
|
|
userId,
|
|
companyId,
|
|
periodStart: '2025-07-01',
|
|
periodEnd: '2026-06-30',
|
|
name: '2025/2026',
|
|
})
|
|
|
|
await getPool().query(
|
|
`UPDATE public.fiscal_periods
|
|
SET name = 'Updated 2025/2026'
|
|
WHERE id = $1`,
|
|
[fiscalPeriodId],
|
|
)
|
|
|
|
const result = await getPool().query<{ retention_expires_at: string }>(
|
|
`SELECT retention_expires_at::text
|
|
FROM public.fiscal_periods
|
|
WHERE id = $1`,
|
|
[fiscalPeriodId],
|
|
)
|
|
|
|
expect(result.rows[0].retention_expires_at).toBe('2034-01-01')
|
|
})
|
|
|
|
it('allows retention backfills when a historical mid-month start is unchanged', async () => {
|
|
const userId = await insertAuthUser()
|
|
const companyId = await insertCompany({ createdBy: userId })
|
|
const historicalPeriodId = await insertFiscalPeriod({
|
|
userId,
|
|
companyId,
|
|
periodStart: '2025-10-20',
|
|
periodEnd: '2025-12-31',
|
|
name: 'Historical period',
|
|
})
|
|
|
|
// Adding an earlier period makes the existing mid-month row match the
|
|
// historical state that used to abort unrelated fiscal-period updates.
|
|
await insertFiscalPeriod({
|
|
userId,
|
|
companyId,
|
|
periodStart: '2024-01-01',
|
|
periodEnd: '2024-12-31',
|
|
name: 'Earlier period',
|
|
})
|
|
|
|
await expect(
|
|
getPool().query(
|
|
`UPDATE public.fiscal_periods
|
|
SET retention_expires_at = make_date(
|
|
extract(year FROM period_end)::integer + 8,
|
|
1,
|
|
1
|
|
)
|
|
WHERE id = $1`,
|
|
[historicalPeriodId],
|
|
),
|
|
).resolves.toBeDefined()
|
|
|
|
const result = await getPool().query<{ retention_expires_at: string }>(
|
|
`SELECT retention_expires_at::text
|
|
FROM public.fiscal_periods
|
|
WHERE id = $1`,
|
|
[historicalPeriodId],
|
|
)
|
|
|
|
expect(result.rows[0].retention_expires_at).toBe('2033-01-01')
|
|
})
|
|
})
|