import type { Prisma } from '@afis/db' /** * Geschäftsnummern: Aktenzeichen und Dokumentnummern. * * 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. * * Fallakten und Dokumente teilen sich denselben Zähler. Sie kollidieren nicht, * weil das Präfix Teil des Schlüssels ist: eine Fallart und eine Dokumentart * mit demselben Präfix laufen bewusst in einem Nummernkreis. */ export type NumberPattern = { prefix: string; padding: number } export const DEFAULT_PATTERN: NumberPattern = { prefix: 'AfiS', padding: 4 } export function formatNumber( 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 parseNumber( 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 die nächste Nummer 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 nextNumber( tx: Prisma.TransactionClient, pattern: NumberPattern = DEFAULT_PATTERN, year = new Date().getFullYear(), ): Promise { const rows = await tx.$queryRaw<{ lastNumber: number }[]>` INSERT INTO "NumberSeries" ("prefix", "year", "lastNumber") VALUES (${pattern.prefix}, ${year}, 1) ON CONFLICT ("prefix", "year") DO UPDATE SET "lastNumber" = "NumberSeries"."lastNumber" + 1 RETURNING "lastNumber" ` const lastNumber = rows[0]?.lastNumber if (lastNumber === undefined) { throw new Error('Nummer konnte nicht vergeben werden') } return formatNumber(year, lastNumber, pattern) }