import type { Prisma } from '@afis/db' /** `AfiS-2026-0042`, siehe Glossar in docs/01-produkt.md. */ export function formatCaseNumber(year: number, number: number): string { return `AfiS-${year}-${String(number).padStart(4, '0')}` } const PATTERN = /^AfiS-(\d{4})-(\d{4})$/ export function parseCaseNumber(value: string): { year: number; number: number } | null { const match = PATTERN.exec(value.trim()) if (!match) return null return { year: Number(match[1]), number: Number(match[2]) } } /** Erkennt Aktenzeichen im Fliesstext, für die Vorschaukarten im Chat. */ export const CASE_NUMBER_GLOBAL = /AfiS-\d{4}-\d{4}/g /** * Vergibt das nächste Aktenzeichen des Jahres. * * Läuft mit `SELECT … FOR UPDATE` in **derselben** Transaktion wie das Anlegen * des Falls. Zwei gleichzeitige Anlagen warten dadurch aufeinander, statt * dieselbe Nummer zu ziehen oder eine Lücke zu hinterlassen. * Siehe docs/04-datenmodell.md. * * Eine Sequenz pro Jahr wäre einfacher, müsste aber jedes Jahr von Hand * angelegt werden — und der Abteilungsleiter ist ausdrücklich nicht technisch. */ export async function nextCaseNumber( tx: Prisma.TransactionClient, year = new Date().getFullYear(), ): Promise { const rows = await tx.$queryRaw<{ lastNumber: number }[]>` INSERT INTO "CaseCounter" ("year", "lastNumber") VALUES (${year}, 1) ON CONFLICT ("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) }