AB-Kern: Nummernvergabe AB-JJJJ-NNNN nach Berliner Jahr
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
68ffac24c4
commit
e3df5383d1
2 files changed
+76
No files matched your search
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Nummernvergabe der Auftragsbestätigungen: AB-JJJJ-NNNN, lückenlos je Berliner Kalenderjahr (Übersicht V4, L3).
|
||||
* vergibNummer schreibt nichts und sperrt nicht selbst: der Aufrufer hält SPERRE_NUMMER (konstanten.js), und die Nummer
|
||||
* ist erst mit dem Speichern der AB vergeben. Eine Nummer, die schon in Gebrauch ist, wird übersprungen.
|
||||
*/
|
||||
import { berlinDate } from '../finance/dates.js'
|
||||
import { findAuftragByNummer, listAuftraege } from './auftragStore.js'
|
||||
|
||||
export function formatNummer(jahr, laufnummer) {
|
||||
if (!Number.isInteger(jahr) || jahr < 2000 || jahr > 9999 || !Number.isInteger(laufnummer) || laufnummer < 1 || laufnummer > 9999) {
|
||||
throw new RangeError('Ungültige Auftragsnummer.')
|
||||
}
|
||||
return `AB-${jahr}-${String(laufnummer).padStart(4, '0')}`
|
||||
}
|
||||
|
||||
export async function vergibNummer(jetzt = new Date()) {
|
||||
const jahr = Number(berlinDate(jetzt).slice(0, 4))
|
||||
const vorhandene = await listAuftraege({ jahr })
|
||||
let laufnummer = vorhandene.reduce((groesste, a) => Math.max(groesste, Number.isSafeInteger(a.laufnummer) ? a.laufnummer : 0), 0) + 1
|
||||
while (await findAuftragByNummer(formatNummer(jahr, laufnummer))) laufnummer += 1
|
||||
return { nummer: formatNummer(jahr, laufnummer), jahr, laufnummer }
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { test, after, beforeEach } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { starteUmgebung, entwurfsDaten } from './helpers/auftraegeAb.js'
|
||||
|
||||
const umg = await starteUmgebung()
|
||||
after(() => umg.schliesse())
|
||||
beforeEach(umg.zuruecksetzen)
|
||||
const { createAuftrag, updateAuftrag } = await import('../server/services/auftraege/auftragStore.js')
|
||||
const { formatNummer, vergibNummer } = await import('../server/services/auftraege/nummern.js')
|
||||
|
||||
/** Eine versendete AB mit Nummer anlegen (über den Speicher von L0). */
|
||||
async function legeNummerierteAn(jahr, laufnummer, patch = {}) {
|
||||
const entwurf = await createAuftrag(entwurfsDaten())
|
||||
return updateAuftrag(entwurf.id, { status: 'versendet', nummer: formatNummer(jahr, laufnummer), jahr, laufnummer, ...patch })
|
||||
}
|
||||
|
||||
test('formatNummer: AB-JJJJ-NNNN mit vier Ziffern; ungültige Werte werfen RangeError', () => {
|
||||
assert.equal(formatNummer(2026, 1), 'AB-2026-0001')
|
||||
assert.equal(formatNummer(2026, 42), 'AB-2026-0042')
|
||||
assert.equal(formatNummer(2027, 9999), 'AB-2027-9999')
|
||||
for (const [jahr, lauf] of [[2026, 0], [2026, 10000], [2026, 1.5], [2026, '1'], [1999, 1], ['2026', 1], [2026, -3]]) {
|
||||
assert.throws(() => formatNummer(jahr, lauf), RangeError, `${jahr}/${lauf}`)
|
||||
}
|
||||
})
|
||||
|
||||
test('vergibNummer: die erste Nummer des Jahres ist 0001', async () => {
|
||||
assert.deepEqual(await vergibNummer(new Date('2026-10-06T10:00:00Z')), { nummer: 'AB-2026-0001', jahr: 2026, laufnummer: 1 })
|
||||
})
|
||||
|
||||
test('vergibNummer: größte Laufnummer des Jahres plus 1 – Lücken egal, andere Jahre und Entwürfe zählen nicht', async () => {
|
||||
for (const nr of [1, 2, 7]) await legeNummerierteAn(2026, nr)
|
||||
await legeNummerierteAn(2025, 40)
|
||||
await createAuftrag(entwurfsDaten()) // Entwurf ohne Nummer
|
||||
assert.deepEqual(await vergibNummer(new Date('2026-10-06T10:00:00Z')), { nummer: 'AB-2026-0008', jahr: 2026, laufnummer: 8 })
|
||||
assert.deepEqual(await vergibNummer(new Date('2025-03-01T10:00:00Z')), { nummer: 'AB-2025-0041', jahr: 2025, laufnummer: 41 })
|
||||
})
|
||||
|
||||
test('vergibNummer: Jahreswechsel nach Berliner Zeit (31.12. 23:00 UTC ist in Berlin schon der 01.01.)', async () => {
|
||||
for (const nr of [1, 2, 3]) await legeNummerierteAn(2026, nr)
|
||||
assert.equal((await vergibNummer(new Date('2026-12-31T22:59:59Z'))).nummer, 'AB-2026-0004')
|
||||
assert.deepEqual(await vergibNummer(new Date('2026-12-31T23:00:00Z')), { nummer: 'AB-2027-0001', jahr: 2027, laufnummer: 1 })
|
||||
assert.equal((await vergibNummer(new Date('2027-01-01T08:00:00Z'))).nummer, 'AB-2027-0001')
|
||||
})
|
||||
|
||||
test('vergibNummer: zählt auch bei mehr als 25 ABs im Jahr richtig (Seitengröße 25 wie bei Appwrite ohne limit)', async () => {
|
||||
for (let i = 1; i <= 30; i += 1) await legeNummerierteAn(2026, i)
|
||||
assert.equal((await vergibNummer(new Date('2026-10-06T10:00:00Z'))).nummer, 'AB-2026-0031')
|
||||
})
|
||||
|
||||
test('vergibNummer: überspringt eine Nummer, die es schon gibt (Dokument mit nummer, aber ohne laufnummer)', async () => {
|
||||
const entwurf = await createAuftrag(entwurfsDaten())
|
||||
await updateAuftrag(entwurf.id, { status: 'versendet', nummer: 'AB-2026-0001' })
|
||||
assert.deepEqual(await vergibNummer(new Date('2026-10-06T10:00:00Z')), { nummer: 'AB-2026-0002', jahr: 2026, laufnummer: 2 })
|
||||
})
|
||||
Reference in new issue
Block a user