* refactor: optimize page loading and data fetching * fix: resolve recurring production runtime errors * feat: add MCP company and customer updates * fix: handle year-end tax adjustments * feat: harden annual report compliance * fix: expand invoice logo and font support * fix: sanitize API route error responses * fix: sanitize user-facing error messages * feat: persist onboarding and tax assessment notices * fix: reduce cloud backup audit churn * feat: refine invoice editor layout * fix: show saved tax adjustments in INK2 * fix: complete annual report API mappings * docs: record operational safeguards and decisions * fix: harden annual report review findings * fix: adjust column span for description based on VAT registration * New css class name
107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { NextResponse } from 'next/server'
|
|
import { createMockRequest, createQueuedMockSupabase, parseJsonResponse } from '@/tests/helpers'
|
|
|
|
vi.mock('@/lib/reports/opening-balances', () => ({
|
|
getOpeningBalances: vi.fn(),
|
|
}))
|
|
|
|
const requireAuthMock = vi.fn()
|
|
vi.mock('@/lib/auth/require-auth', () => ({
|
|
requireAuth: (...args: unknown[]) => requireAuthMock(...args),
|
|
}))
|
|
|
|
vi.mock('@/lib/company/context', () => ({
|
|
getActiveCompanyId: vi.fn().mockResolvedValue('company-1'),
|
|
}))
|
|
|
|
import { getOpeningBalances } from '@/lib/reports/opening-balances'
|
|
import { GET } from '../route'
|
|
|
|
function request(searchParams: Record<string, string>) {
|
|
return createMockRequest('/api/bookkeeping/account-balances', { searchParams })
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
vi.mocked(getOpeningBalances).mockResolvedValue({
|
|
balances: new Map(),
|
|
obEntryId: null,
|
|
})
|
|
})
|
|
|
|
describe('GET /api/bookkeeping/account-balances', () => {
|
|
it('returns 401 when not authenticated', async () => {
|
|
requireAuthMock.mockResolvedValue({
|
|
user: null,
|
|
supabase: {},
|
|
error: NextResponse.json({ error: 'Unauthorized' }, { status: 401 }),
|
|
})
|
|
|
|
expect((await GET(request({ accounts: '1930', as_of: '2026-06-30' }), {
|
|
params: Promise.resolve({}),
|
|
})).status).toBe(401)
|
|
})
|
|
|
|
it('returns 400 for an invalid account number', async () => {
|
|
const { supabase } = createQueuedMockSupabase()
|
|
requireAuthMock.mockResolvedValue({ user: { id: 'user-1' }, supabase, error: null })
|
|
|
|
expect((await GET(request({ accounts: 'not-an-account', as_of: '2026-06-30' }), {
|
|
params: Promise.resolve({}),
|
|
})).status).toBe(400)
|
|
})
|
|
|
|
it('returns zero balances when no fiscal period contains the date', async () => {
|
|
const { supabase, enqueue } = createQueuedMockSupabase()
|
|
enqueue({ data: null })
|
|
requireAuthMock.mockResolvedValue({ user: { id: 'user-1' }, supabase, error: null })
|
|
|
|
const { status, body } = await parseJsonResponse<{
|
|
data: Array<{ account_number: string; balance: number }>
|
|
}>(await GET(request({ accounts: '1930', as_of: '2026-06-30' }), {
|
|
params: Promise.resolve({}),
|
|
}))
|
|
|
|
expect(status).toBe(200)
|
|
expect(body.data).toEqual([{ account_number: '1930', balance: 0 }])
|
|
})
|
|
|
|
it('combines opening balances with aggregated period activity', async () => {
|
|
const { supabase, enqueueMany } = createQueuedMockSupabase()
|
|
enqueueMany([
|
|
{
|
|
data: {
|
|
id: 'period-1',
|
|
period_start: '2026-01-01',
|
|
period_end: '2026-12-31',
|
|
opening_balance_entry_id: 'opening-1',
|
|
},
|
|
},
|
|
{ data: [{ account_number: '1930', account_class: 1 }] },
|
|
{ data: [{ account_number: '1930', debit: 250, credit: 50 }] },
|
|
])
|
|
vi.mocked(getOpeningBalances).mockResolvedValue({
|
|
balances: new Map([['1930', { debit: 1_000, credit: 0 }]]),
|
|
obEntryId: 'opening-1',
|
|
})
|
|
requireAuthMock.mockResolvedValue({ user: { id: 'user-1' }, supabase, error: null })
|
|
|
|
const { status, body } = await parseJsonResponse<{
|
|
data: Array<{ account_number: string; balance: number }>
|
|
}>(await GET(request({ accounts: '1930', as_of: '2026-06-30' }), {
|
|
params: Promise.resolve({}),
|
|
}))
|
|
|
|
expect(status).toBe(200)
|
|
expect(body.data).toEqual([{ account_number: '1930', balance: 1_200 }])
|
|
expect(supabase.rpc).toHaveBeenCalledWith('get_account_period_activity', {
|
|
p_company_id: 'company-1',
|
|
p_start: '2026-01-01',
|
|
p_end: '2026-06-30',
|
|
p_accounts: ['1930'],
|
|
p_exclude_journal_entry_id: 'opening-1',
|
|
})
|
|
})
|
|
})
|