Aufträge L5: Auftrag bestätigen (idempotent, unter Sperre, Frühbeginn nur bei Privatpersonen)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
19abac0373
commit
335ac60e54
2 files changed
+230
No files matched your search
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Bestätigen einer Auftragsbestätigung (Übersicht V2/V4, Strang L5): idempotent, unter der Sperre `sperreAuftrag(id)`,
|
||||
* Stand innerhalb der Sperre neu gelesen. K32 und T22 gehen erst nach der Freigabe der Sperre raus; Mailfehler stehen
|
||||
* nur im Protokoll. Die erste Bestätigung zählt, jede weitere ändert nichts.
|
||||
*/
|
||||
import { withLocks } from '../keyedLock.js'
|
||||
import { getAuftrag, updateAuftrag } from './auftragStore.js'
|
||||
import { AuftragError } from './fehler.js'
|
||||
import { sperreAuftrag } from './konstanten.js'
|
||||
import { sendeBestaetigtMails } from './mails.js'
|
||||
import { FRUEHBEGINN_FASSUNG } from './texte.js'
|
||||
|
||||
export const GESPERRT_MELDUNG = {
|
||||
ersetzt: 'Diese Auftragsbestätigung wurde ersetzt.',
|
||||
storniert: 'Diese Auftragsbestätigung wurde storniert.',
|
||||
widerrufen: 'Diese Auftragsbestätigung wurde widerrufen.',
|
||||
}
|
||||
const NICHT_BESTAETIGBAR = 'Diese Auftragsbestätigung kann nicht bestätigt werden.'
|
||||
const NICHT_GEFUNDEN = 'Auftragsbestätigung nicht gefunden.'
|
||||
const GESPERRT_STATUS = ['entwurf', 'ersetzt', 'storniert', 'widerrufen']
|
||||
|
||||
/** 'offen' (bestätigbar), 'bestaetigt' (schon bestätigt) oder 'gesperrt' (Entwurf, ersetzt, storniert, widerrufen). */
|
||||
export function bestaetigungsLage(auftrag) {
|
||||
if (GESPERRT_STATUS.includes(auftrag.status)) return 'gesperrt'
|
||||
return auftrag.bestaetigtAm || auftrag.status === 'bestaetigt' ? 'bestaetigt' : 'offen'
|
||||
}
|
||||
|
||||
function pruefeZeitpunkt(jetzt) {
|
||||
if (!(jetzt instanceof Date) || Number.isNaN(jetzt.getTime())) throw new TypeError('jetzt muss ein gültiges Date sein')
|
||||
}
|
||||
|
||||
export async function bestaetigeAuftrag(auftrag, { ueber, fruehbeginn, jetzt = new Date() } = {}) {
|
||||
// synchron vor der Sperre: der zuerst gestartete Aufruf bekommt die Sperre zuerst
|
||||
if (ueber !== 'link' && ueber !== 'portal') throw new TypeError('bestaetigeAuftrag: ueber muss „link“ oder „portal“ sein')
|
||||
pruefeZeitpunkt(jetzt)
|
||||
const { aktuell, bestaetigtJetzt } = await withLocks(sperreAuftrag(auftrag.id), async () => {
|
||||
const frisch = await getAuftrag(auftrag.id)
|
||||
if (!frisch) throw new AuftragError(404, NICHT_GEFUNDEN)
|
||||
const lage = bestaetigungsLage(frisch)
|
||||
if (lage === 'gesperrt') throw new AuftragError(409, GESPERRT_MELDUNG[frisch.status] ?? NICHT_BESTAETIGBAR)
|
||||
if (lage === 'bestaetigt') return { aktuell: frisch, bestaetigtJetzt: false }
|
||||
const privat = frisch.kunde?.kundenart === 'privat'
|
||||
const patch = {
|
||||
bestaetigtAm: jetzt.toISOString(),
|
||||
bestaetigtUeber: ueber,
|
||||
bestaetigtPdfSha256: frisch.pdfSha256,
|
||||
fruehbeginn: privat && fruehbeginn === true,
|
||||
fruehbeginnFassung: privat ? FRUEHBEGINN_FASSUNG : '',
|
||||
}
|
||||
if (frisch.status === 'versendet') patch.status = 'bestaetigt'
|
||||
return { aktuell: await updateAuftrag(frisch.id, patch), bestaetigtJetzt: true }
|
||||
})
|
||||
if (bestaetigtJetzt) await sendeBestaetigtMails(aktuell)
|
||||
return aktuell
|
||||
}
|
||||
Reference in new issue
Block a user