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
20 lines
946 B
TypeScript
20 lines
946 B
TypeScript
import { z } from 'zod'
|
|
|
|
// Commit-boundary re-validation for the staged ignore_transaction operation
|
|
// (gnubok_ignore_transaction, issue #1661). Ignoring writes no verifikat: it
|
|
// flips transactions.is_ignored so a row that is not an affärshändelse (a
|
|
// PSD2 ghost row, a duplicate from a reconnect, a transfer that never
|
|
// executed) leaves the "to book" funnels. The DB CHECK
|
|
// transactions_is_ignored_no_journal_entry guarantees an ignored row is
|
|
// unbooked, and lib/transactions/ignore.ts refuses a booked row through the
|
|
// three anchors (journal_entry_id, payment allocations, voucher links), so
|
|
// this schema only has to bound the shape.
|
|
|
|
export const IgnoreTransactionParamsSchema = z.object({
|
|
transaction_id: z.string().uuid(),
|
|
// true = ignore (default), false = restore a previously ignored row.
|
|
ignored: z.boolean().default(true),
|
|
})
|
|
|
|
export type IgnoreTransactionParams = z.infer<typeof IgnoreTransactionParamsSchema>
|