Richtigstellung zuerst: der vorige Commit trägt die Nachricht "Nichts davon gebaut", enthält aber bereits die Schema-Änderung an CaseTemplate und CaseCounter. Ein abgebrochener Aufruf hatte die Datei schon geschrieben, und das nachfolgende `git add -A` hat sie mitgenommen. Die Nachricht war damit falsch. Hier ist die Änderung vollständig und geprüft. Das Muster des Aktenzeichens gehört zur Vorlage: Präfix und Stellenzahl je Fallart, ein Nummernkreis je Präfix und Jahr. Die Abteilung führt gewachsene Präfixe wie AKT-PA-2026-62843 neben dem AfiS-2026-0042 aus dem Glossar. Die Migration ist von Hand geschrieben, weil der Wechsel des Primärschlüssels auf CaseCounter den bestehenden Zähler erhalten muss: eine zurückgesetzte Nummer wäre ein doppeltes Aktenzeichen. Vorher und nachher geprüft. parseCaseNumber zerlegt von hinten, damit Präfixe mit Bindestrich funktionieren. Ein Test belegt, dass AKT-PA und AfiS getrennte Nummernkreise führen. Dazu start.sh für die lokale Entwicklung, solange der Dockge-Stack nicht läuft: Datenbank hochfahren, Migrationen, Grundzustand, Worker und Web. Mit --ohne-worker für Arbeit ohne Discord und --nur-db für die Datenbank allein.
66 lines
2.3 KiB
TypeScript
66 lines
2.3 KiB
TypeScript
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<string> {
|
|
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)
|
|
}
|