Buchungskalender: Abschluss – S2 Rechnungsnummer unter einer Sperre, unique-Index; S3 Storno mit Ursprungs-Aussteller
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
435dd0b746
commit
727613d0e2
4 files changed
+87
-19
No files matched your search
@@ -65,6 +65,55 @@ test('stelleAus storno negiert Posten und Summen, traegt bezugNummer', async ()
|
||||
assert.equal((await re.ladeRechnung(s.$id)).nummer, 'RE-2026-0002')
|
||||
})
|
||||
|
||||
const ANF = {
|
||||
$id: 'a9', unterkunftId: 'u1', nummer: 'A-WXYZ', von: '2026-11-10', bis: '2026-11-12', personen: 2, zahlart: 'vorort',
|
||||
rechnungsadresse: { name: 'Gast Neu' },
|
||||
posten: [{ text: 'Nacht', menge: 2, einzelCent: 10000, ustProzent: 7 }],
|
||||
summen: { bruttoCent: 20000, nettoCent: 18692, ust: [{ satz: 7, nettoCent: 18692, steuerCent: 1308 }] },
|
||||
}
|
||||
|
||||
test('Rechnungsnummer: scheitert die Dokument-Anlage, bleibt keine Luecke (Zaehler zurueck)', async () => {
|
||||
const ctx = { featureId: 'f1', einstellungen: { firma: 'Haus GmbH' } }
|
||||
const r1 = await re.stelleAus(ctx, ANF, { art: 'rechnung', now: NOW })
|
||||
assert.equal(r1.nummer, 'RE-2026-0001')
|
||||
aw.failNext({ collection: 'ukRechnungen', method: 'POST', status: 500 })
|
||||
await assert.rejects(re.stelleAus(ctx, ANF, { art: 'rechnung', now: NOW }))
|
||||
assert.deepEqual((await sp.ladeEinstellungen('f1')).rechnungZaehler, { jahr: 2026, n: 1 })
|
||||
const r2 = await re.stelleAus(ctx, ANF, { art: 'rechnung', now: NOW })
|
||||
assert.equal(r2.nummer, 'RE-2026-0002')
|
||||
// erste Rechnung ueberhaupt scheitert: Zaehler bleibt leer
|
||||
const ctx2 = { featureId: 'f2', einstellungen: {} }
|
||||
aw.failNext({ collection: 'ukRechnungen', method: 'POST', status: 500 })
|
||||
await assert.rejects(re.stelleAus(ctx2, ANF, { art: 'rechnung', now: NOW }))
|
||||
assert.ok(!((await sp.ladeEinstellungen('f2'))?.rechnungZaehler?.n > 0))
|
||||
assert.equal((await re.stelleAus(ctx2, ANF, { art: 'rechnung', now: NOW })).nummer, 'RE-2026-0001')
|
||||
})
|
||||
|
||||
test('Rechnungsnummer: parallele Ausstellung mit einem Fehler dazwischen bleibt lueckenlos', async () => {
|
||||
const ctx = { featureId: 'f1', einstellungen: {} }
|
||||
aw.failNext({ collection: 'ukRechnungen', method: 'POST', status: 500 })
|
||||
const res = await Promise.allSettled(Array.from({ length: 4 }, () => re.stelleAus(ctx, ANF, { art: 'rechnung', now: NOW })))
|
||||
const nr = res.filter((x) => x.status === 'fulfilled').map((x) => x.value.nummer).sort()
|
||||
assert.deepEqual(nr, ['RE-2026-0001', 'RE-2026-0002', 'RE-2026-0003'])
|
||||
})
|
||||
|
||||
test('Stornorechnung traegt Aussteller und Empfaenger der Ursprungsrechnung, nicht die heutigen', async () => {
|
||||
const alt = { featureId: 'f1', einstellungen: { firma: 'Alte Firma', iban: 'DE-ALT', steuernummer: '1/1' } }
|
||||
const r = await re.stelleAus(alt, ANF, { art: 'rechnung', now: NOW })
|
||||
const neu = { featureId: 'f1', einstellungen: { firma: 'Neue Firma', iban: 'DE-NEU' } }
|
||||
const s = await re.stelleAus(neu, { ...ANF, rechnungsadresse: { name: 'Anderer Name' } }, { art: 'storno', bezug: r, now: NOW })
|
||||
assert.equal(s.inhalt.aussteller.firma, 'Alte Firma')
|
||||
assert.equal(s.inhalt.aussteller.iban, 'DE-ALT')
|
||||
assert.deepEqual(s.inhalt.empfaenger, { name: 'Gast Neu' })
|
||||
assert.equal(s.inhalt.bezugNummer, r.nummer)
|
||||
})
|
||||
|
||||
test('Schema: Rechnungsnummer ist je Feature eindeutig (unique-Index)', async () => {
|
||||
const { UNTERKUNFT_COLLECTIONS } = await import('../server/services/unterkunft/schema.js')
|
||||
const i = UNTERKUNFT_COLLECTIONS.ukRechnungen.indexes.find((x) => x.type === 'unique')
|
||||
assert.deepEqual(i.attributes, ['featureId', 'nummer'])
|
||||
})
|
||||
|
||||
test('Token: Hash passt nur zum Token; Formtoken', () => {
|
||||
const { token, hash } = tk.neuesToken()
|
||||
assert.equal(tk.tokenPasst(hash, token), true)
|
||||
|
||||
Reference in new issue
Block a user