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