import type { Prisma } from '@afis/db' /** * Aktenzeichen. Das Muster gehört zur Vorlage, nicht ins Programm: die * Abteilung führt gewachsene Präfixe wie `AKT-PA-2026-62843` neben dem * `AfiS-2026-0042` aus dem Glossar. Siehe docs/13-entscheidungen.md. */ export type NumberPattern = { prefix: string; padding: number } export const DEFAULT_PATTERN: NumberPattern = { prefix: 'AfiS', padding: 4 } export function formatCaseNumber( year: number, number: number, pattern: NumberPattern = DEFAULT_PATTERN, ): string { return `${pattern.prefix}-${year}-${String(number).padStart(pattern.padding, '0')}` } /** * Ein Präfix darf Bindestriche enthalten (`AKT-PA`). Zerlegt wird deshalb von * hinten: die letzten beiden Abschnitte sind Jahr und laufende Nummer, alles * davor ist das Präfix. */ const PATTERN = /^([A-Za-z][A-Za-z0-9-]*)-(\d{4})-(\d+)$/ export function parseCaseNumber( value: string, ): { prefix: string; year: number; number: number } | null { const match = PATTERN.exec(value.trim()) if (!match) return null return { prefix: match[1]!, year: Number(match[2]), number: Number(match[3]) } } /** Erkennt Aktenzeichen im Fliesstext, für die Vorschaukarten im Chat. */ export const CASE_NUMBER_GLOBAL = /\b[A-Za-z][A-Za-z0-9-]*-\d{4}-\d{3,8}\b/g /** * Vergibt das nächste Aktenzeichen im Nummernkreis dieses Präfixes und Jahres. * * `INSERT … ON CONFLICT DO UPDATE` sperrt die Zeile für die Dauer der * Transaktion. Zwei gleichzeitige Anlagen warten dadurch aufeinander, statt * dieselbe Nummer zu ziehen. Läuft in **derselben** Transaktion wie das * Anlegen des Falls, siehe docs/04-datenmodell.md. */ export async function nextCaseNumber( tx: Prisma.TransactionClient, pattern: NumberPattern = DEFAULT_PATTERN, year = new Date().getFullYear(), ): Promise { const rows = await tx.$queryRaw<{ lastNumber: number }[]>` INSERT INTO "CaseCounter" ("prefix", "year", "lastNumber") VALUES (${pattern.prefix}, ${year}, 1) ON CONFLICT ("prefix", "year") DO UPDATE SET "lastNumber" = "CaseCounter"."lastNumber" + 1 RETURNING "lastNumber" ` const lastNumber = rows[0]?.lastNumber if (lastNumber === undefined) { throw new Error('Aktenzeichen konnte nicht vergeben werden') } return formatCaseNumber(year, lastNumber, pattern) }