dc07ca8872
Decision (option a): a private marking stays a real booking (eget uttag/insattning), so it remains blocked in a locked or closed period; the legal escape for rows that are not affarshandelser is ignore. Private + locked now returns TX_CATEGORIZE_PRIVATE_PERIOD_LOCKED with remediation naming the ignore paths instead of a bare PERIOD_LOCKED, on all four categorize surfaces and the bulk driver. New v1 POST/DELETE /transactions/{id}/ignore (isTransactionBooked-based 409, idempotent) and a staged MCP gnubok_ignore_transaction (+ accounted_ alias, search visibility to respect the tools/list payload ceiling) with operation_type ignore_transaction; the CHECK pair 20260831070000/070001 rebuilds the constraint from main's newest list plus the new value. Dashboard toast gains an Ignorera i stallet action. Closes #1661
56 lines
1.9 KiB
TypeScript
56 lines
1.9 KiB
TypeScript
import { beforeAll, describe, expect, it } from 'vitest'
|
|
import { getPool } from './setup'
|
|
import { seedCompany } from './fixtures'
|
|
|
|
/**
|
|
* The pending_operations_operation_type_check constraint must accept the
|
|
* 'ignore_transaction' op type added by migration 20260831070000 (validated
|
|
* in 20260831070001, issue #1661). Without the constraint expansion,
|
|
* gnubok_ignore_transaction's staging INSERT fails with check_violation on
|
|
* every real call while dry_run (which skips the INSERT) previews clean: the
|
|
* exact bug class the op-type audit test documents. The second case pins that
|
|
* the re-created constraint is still a closed list.
|
|
*/
|
|
describe('pending_operations operation_type CHECK: ignore_transaction', () => {
|
|
let userId: string
|
|
let companyId: string
|
|
|
|
beforeAll(async () => {
|
|
const seeded = await seedCompany()
|
|
userId = seeded.userId
|
|
companyId = seeded.companyId
|
|
})
|
|
|
|
it('accepts ignore_transaction (constraint expanded in 20260831070000)', async () => {
|
|
const client = await getPool().connect()
|
|
try {
|
|
await client.query('BEGIN')
|
|
await client.query(
|
|
`INSERT INTO public.pending_operations (user_id, company_id, operation_type, title)
|
|
VALUES ($1, $2, 'ignore_transaction', 'regression: ignorera transaktion')`,
|
|
[userId, companyId],
|
|
)
|
|
await client.query('ROLLBACK')
|
|
} finally {
|
|
client.release()
|
|
}
|
|
})
|
|
|
|
it('still rejects an unknown op type (the list stays closed)', async () => {
|
|
const client = await getPool().connect()
|
|
try {
|
|
await client.query('BEGIN')
|
|
await expect(
|
|
client.query(
|
|
`INSERT INTO public.pending_operations (user_id, company_id, operation_type, title)
|
|
VALUES ($1, $2, 'no_such_operation', 'must fail')`,
|
|
[userId, companyId],
|
|
),
|
|
).rejects.toMatchObject({ code: '23514' })
|
|
await client.query('ROLLBACK')
|
|
} finally {
|
|
client.release()
|
|
}
|
|
})
|
|
})
|