AB-Kern: Erinnerung mit neuem Token und geprüfter PDF-Datei

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-07 14:14:46 +00:00
1 parent c74f7eeea7
commit 0da5b30119
2 files changed
+158 -1

No files matched your search

+37 -1
View File
@@ -5,12 +5,13 @@
* über `deps` (L4/L5, Standard: dynamischer Import beim Aufruf). Jede Änderung an einer bestehenden AB läuft unter
* sperreAuftrag(id) (konstanten.js), das Senden zusätzlich unter SPERRE_NUMMER; Mails gehen immer erst nach der Sperre.
*/
import { createHash } from 'node:crypto'
import { config } from '../../config.js'
import { getDocument, listDocuments, Query, updateDocument } from '../appwriteAdmin.js'
import { getSettings } from '../finance/settings.js'
import { withLocks } from '../keyedLock.js'
import { kundeSnapshot } from './auftragSicht.js'
import { entfernePdf, speicherePdf } from './ablage.js'
import { entfernePdf, lesePdf, speicherePdf } from './ablage.js'
import { createAuftrag, deleteAuftrag, getAuftrag, updateAuftrag } from './auftragStore.js'
import { AuftragError } from './fehler.js'
import { vergibNummer } from './nummern.js'
@@ -444,3 +445,38 @@ export async function sendeAuftrag(id, { von, deps, jetzt = new Date() } = {}) {
})
return versendeMail(d, gespeichert, { pdf, token })
}
// ---------------------------------------------------------------------------------------------------------------------
// Erinnern („Erinnerung senden“ und „Erneut senden“)
// ---------------------------------------------------------------------------------------------------------------------
/** Liest die gespeicherte PDF und prüft die SHA-256-Summe: eine veränderte Datei wird nie verschickt oder angezeigt. */
export async function ladeGespeichertesPdf(auftrag) {
if (!hatText(auftrag.pdfDatei)) throw new AuftragError(404, 'PDF nicht gefunden.')
const buffer = await lesePdf(auftrag.pdfDatei)
if (createHash('sha256').update(buffer).digest('hex') !== auftrag.pdfSha256) {
console.error('[auftraege] Prüfsumme der gespeicherten PDF stimmt nicht:', auftrag.id)
throw new AuftragError(500, 'Die gespeicherte PDF-Datei stimmt nicht mit der Prüfsumme überein.')
}
return buffer
}
/**
* Neuer Bestätigungslink für eine versendete AB (der alte gilt ab dann nicht mehr) und erneute Mail mit der gespeicherten
* PDF. Nach einem Mail-Fehler ist es eine gewöhnliche Mail ohne Erinnerungs-Hinweis; gesendetAn wird die aktuelle Adresse.
*/
export async function erinnereAuftrag(id, { deps, jetzt = new Date() } = {}) {
const d = await ladeDeps(deps, ['neuerToken', 'sendeAuftragMail'])
const { gespeichert, pdf, token, erinnerung } = await withLocks(sperreAuftrag(id), async () => {
const alt = await ladeAuftrag(id)
if (alt.status !== 'versendet') throw new AuftragError(409, 'Eine Erinnerung gibt es nur für versendete Auftragsbestätigungen.')
const kundeDoc = await ladeKunde(alt.kundenId)
if (!hatText(kundeDoc.email)) throw new AuftragError(400, 'Für den Versand fehlt die E-Mail-Adresse des Kunden.')
// vor dem Token-Tausch: eine beschädigte Datei darf den alten Link nicht entwerten
const pdfBytes = await ladeGespeichertesPdf(alt)
const neu = d.neuerToken(jetzt)
const dokument = await updateAuftrag(id, { tokenHash: neu.hash, tokenGueltigBis: neu.gueltigBis.toISOString(), gesendetAn: kundeDoc.email.trim() })
return { gespeichert: dokument, pdf: pdfBytes, token: neu.token, erinnerung: alt.mailStatus !== 'fehler' }
})
return versendeMail(d, gespeichert, { pdf, token, erinnerung })
}