Files
accounted/tests/pg/fiscal-period-tax-adjustments.pg.test.ts
T
Mattsson e11f70b347 Bug/gh issues fiz (#1103)
* 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
2026-07-21 23:00:15 +02:00

175 lines
6.4 KiB
TypeScript

import { randomUUID } from 'node:crypto'
import { describe, expect, it } from 'vitest'
import { getPool, withUserContext } from './setup'
import {
insertAuthUser,
insertCompany,
insertCompanyMember,
insertFiscalPeriod,
seedCompany,
} from './fixtures'
async function setActiveCompany(userId: string, companyId: string): Promise<void> {
await getPool().query(
`INSERT INTO public.user_preferences (user_id, active_company_id)
VALUES ($1, $2)
ON CONFLICT (user_id) DO UPDATE SET active_company_id = EXCLUDED.active_company_id`,
[userId, companyId],
)
}
async function insertAdjustment(params: {
companyId: string
userId: string
fiscalPeriodId: string
}): Promise<string> {
const id = randomUUID()
await getPool().query(
`INSERT INTO public.fiscal_period_tax_adjustments
(id, company_id, user_id, fiscal_period_id, adjustment_type, source,
source_key, description, account_number, amount, included)
VALUES ($1, $2, $3, $4, 'non_deductible_expense', 'detected',
'account:6992', 'Ej avdragsgill kostnad', '6992', 2994, true)`,
[id, params.companyId, params.userId, params.fiscalPeriodId],
)
return id
}
describe('fiscal_period_tax_adjustments constraints and RLS', () => {
it('uses the standard UUID default and locks the fiscal period in the guard', async () => {
const result = await getPool().query<{ default_expression: string; function_definition: string }>(
`SELECT
pg_get_expr(d.adbin, d.adrelid) AS default_expression,
pg_get_functiondef('public.guard_fiscal_period_tax_adjustment()'::regprocedure)
AS function_definition
FROM pg_attrdef d
JOIN pg_attribute a
ON a.attrelid = d.adrelid AND a.attnum = d.adnum
WHERE d.adrelid = 'public.fiscal_period_tax_adjustments'::regclass
AND a.attname = 'id'`,
)
expect(result.rows[0]?.default_expression).toContain('uuid_generate_v4')
expect(result.rows[0]?.function_definition).toMatch(/FOR UPDATE/i)
})
it('isolates adjustments by company membership', async () => {
const owner = await seedCompany()
const adjustmentId = await insertAdjustment(owner)
const strangerId = await insertAuthUser()
const ownerView = await withUserContext(owner.userId, (client) =>
client.query(
'SELECT id FROM public.fiscal_period_tax_adjustments WHERE id = $1',
[adjustmentId],
),
)
expect(ownerView.rows).toHaveLength(1)
const strangerView = await withUserContext(strangerId, (client) =>
client.query(
'SELECT id FROM public.fiscal_period_tax_adjustments WHERE id = $1',
[adjustmentId],
),
)
expect(strangerView.rows).toHaveLength(0)
})
it('rejects a company that does not own the fiscal period', async () => {
const first = await seedCompany()
const second = await seedCompany()
await expect(
insertAdjustment({
companyId: first.companyId,
userId: first.userId,
fiscalPeriodId: second.fiscalPeriodId,
}),
).rejects.toThrow(/does not match fiscal period company/i)
})
it('allows an owner to insert an adjustment for the active company', async () => {
const owner = await seedCompany()
await setActiveCompany(owner.userId, owner.companyId)
await withUserContext(owner.userId, async (client) => {
const result = await client.query(
`INSERT INTO public.fiscal_period_tax_adjustments
(company_id, user_id, fiscal_period_id, adjustment_type, source,
source_key, description, account_number, amount, included)
VALUES ($1, $2, $3, 'non_deductible_expense', 'detected',
'account:6992', 'Ej avdragsgill kostnad', '6992', 2994, true)
RETURNING id`,
[owner.companyId, owner.userId, owner.fiscalPeriodId],
)
expect(result.rows).toHaveLength(1)
})
})
it('blocks a viewer from inserting an adjustment directly under RLS', async () => {
const owner = await seedCompany()
const viewerId = await insertAuthUser()
await insertCompanyMember({ companyId: owner.companyId, userId: viewerId, role: 'viewer' })
await setActiveCompany(viewerId, owner.companyId)
await withUserContext(viewerId, async (client) => {
await expect(
client.query(
`INSERT INTO public.fiscal_period_tax_adjustments
(company_id, user_id, fiscal_period_id, adjustment_type, source,
source_key, description, account_number, amount, included)
VALUES ($1, $2, $3, 'non_deductible_expense', 'detected',
'account:6992', 'Ej avdragsgill kostnad', '6992', 2994, true)`,
[owner.companyId, viewerId, owner.fiscalPeriodId],
),
).rejects.toThrow()
})
})
it('blocks writes to a member company that is not the active company', async () => {
const owner = await seedCompany()
const otherCompanyId = await insertCompany({ createdBy: owner.userId })
await insertCompanyMember({ companyId: otherCompanyId, userId: owner.userId, role: 'owner' })
const otherPeriodId = await insertFiscalPeriod({
userId: owner.userId,
companyId: otherCompanyId,
})
await setActiveCompany(owner.userId, owner.companyId)
await withUserContext(owner.userId, async (client) => {
await expect(
client.query(
`INSERT INTO public.fiscal_period_tax_adjustments
(company_id, user_id, fiscal_period_id, adjustment_type, source,
source_key, description, account_number, amount, included)
VALUES ($1, $2, $3, 'non_deductible_expense', 'detected',
'account:6992', 'Ej avdragsgill kostnad', '6992', 2994, true)`,
[otherCompanyId, owner.userId, otherPeriodId],
),
).rejects.toThrow()
})
})
it('blocks adjustment changes after the fiscal period is locked', async () => {
const owner = await seedCompany()
const adjustmentId = await insertAdjustment(owner)
await getPool().query(
'UPDATE public.fiscal_periods SET locked_at = now() WHERE id = $1',
[owner.fiscalPeriodId],
)
await expect(
getPool().query(
'UPDATE public.fiscal_period_tax_adjustments SET amount = 3000 WHERE id = $1',
[adjustmentId],
),
).rejects.toThrow(/locked for tax adjustments/i)
await expect(
getPool().query(
'DELETE FROM public.fiscal_period_tax_adjustments WHERE id = $1',
[adjustmentId],
),
).rejects.toThrow(/locked for tax adjustments/i)
})
})