AB-PDF: Entwurfs-Kennzeichnung für die Vorschau und Hinweis auf die ersetzte AB

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-07 14:13:51 +00:00
1 parent 6f213dca25
commit 3464808a40
3 files changed
+77 -2

No files matched your search

+25 -2
View File
@@ -404,10 +404,25 @@ function zeichneFuss(doc, daten, nummer, gesamt) {
schreibe(doc, `${b.seite} ${nummer} ${b.von} ${gesamt}`, RECHTS - 70, y + 8, STIL.fussLabel, { width: 70, align: 'right', lineBreak: false })
}
function zeichneWasserzeichen(doc, daten) {
const stil = { schrift: 'semibold', groesse: 110, lineGap: 0, farbe: FARBEN.entwurf }
setzeStil(doc, stil)
const breite = doc.widthOfString(daten.beschriftung.entwurf)
const mitteX = SEITE.breite / 2
const mitteY = SEITE.hoehe / 2
doc.save()
doc.fillOpacity(0.07)
doc.rotate(-35, { origin: [mitteX, mitteY] })
doc.text(daten.beschriftung.entwurf, mitteX - breite / 2, mitteY - (stil.groesse * 1.2) / 2, { lineBreak: false })
doc.restore()
schreibe(doc, daten.beschriftung.entwurfHinweis, SEITE.links, 22, { schrift: 'semibold', groesse: 8, lineGap: 0, farbe: FARBEN.entwurf }, { width: INHALT_BREITE, align: 'center', lineBreak: false })
}
/** Zeichnet alle Seiten; die erste Seite legt pdfkit selbst an. */
function zeichneAuftrag(doc, daten, seiten, assets) {
seiten.forEach((seite, index) => {
if (index > 0) doc.addPage()
if (daten.vorschau) zeichneWasserzeichen(doc, daten)
if (index > 0) zeichneFolgeKopf(doc, daten, assets)
for (const posten of seite.posten) zeichneAtom(doc, daten, posten.atom, posten.y, assets)
zeichneFuss(doc, daten, seite.nummer, seiten.length)
@@ -416,9 +431,17 @@ function zeichneAuftrag(doc, daten, seiten, assets) {
// ===== 5. Ablauf =====
async function ladeErsetzte(id) {
const { getAuftrag } = await import('./auftragStore.js')
const alt = await getAuftrag(id)
if (!alt?.nummer || !alt.gesendetAm) throw new AuftragError(409, 'Die ersetzte Auftragsbestätigung wurde nicht gefunden.')
return { nummer: alt.nummer, datum: berlinDate(new Date(alt.gesendetAm)) }
}
/** Das AB-PDF (A4) als Buffer. */
export async function erzeugeAuftragPdf(auftrag, { settings, vorschau = false, jetzt = new Date() } = {}) {
const daten = bereinigeTexte(auftragPdfDaten(auftrag, { settings, vorschau, jetzt }))
export async function erzeugeAuftragPdf(auftrag, { settings, vorschau = false, jetzt = new Date(), ersetzt } = {}) {
const alt = ersetzt !== undefined ? ersetzt : auftrag.ersetztId ? await ladeErsetzte(auftrag.ersetztId) : null
const daten = bereinigeTexte(auftragPdfDaten(auftrag, { settings, vorschau, jetzt, ersetzt: alt }))
const assets = ladeSchriften()
const doc = new PDFDocument({
size: 'A4', margins: { top: 0, bottom: 0, left: 0, right: 0 }, lang: 'de-DE', displayTitle: true,
+17
View File
@@ -128,3 +128,20 @@ test('unvollständige AB: AuftragError 409 statt halbem PDF', async () => {
await assert.rejects(erzeugeAuftragPdf(beispielAuftrag({ titel: '' }), optionen()), (e) => e instanceof AuftragError && e.status === 409)
await assert.rejects(erzeugeAuftragPdf(beispielAuftrag({ kunde: null }), optionen({ vorschau: true })), (e) => e instanceof AuftragError && e.status === 409)
})
test('Vorschau: ENTWURF und Hinweis auf jeder Seite, Nummer „Entwurf“', async () => {
const pdf = await erzeugeAuftragPdf(beispielAuftrag({ nummer: '', gesendetAm: null }), optionen({ vorschau: true }))
const seiten = leseSeiten(pdf)
assert.ok(seiten.length >= 1)
for (const zeilen of seiten) {
const text = zeilen.join('\n')
assert.ok(text.includes('ENTWURF'))
assert.ok(text.includes('Entwurf – Vorschau, noch keine gültige Auftragsbestätigung'))
}
assert.ok(gesamtText(pdf).includes('Entwurf'))
})
test('ersetzte AB: Hinweis mit Nummer und Datum der alten AB im PDF', async () => {
const pdf = await erzeugeAuftragPdf(beispielAuftrag(), optionen({ ersetzt: { nummer: 'AB-2026-0003', datum: '2026-10-01' } }))
assert.ok(ohneLeerraum(gesamtText(pdf)).includes(ohneLeerraum(AB_TEXTE.ersetzt({ nummer: 'AB-2026-0003', datum: '01.10.2026' }))))
})
+35
View File
@@ -0,0 +1,35 @@
import { test, after } from 'node:test'
import assert from 'node:assert/strict'
import { startAppwriteMock } from './support/appwriteMock.js'
import { schemaKeys } from './support/schema.js'
import { useTestEnv } from './support/testEnv.js'
import { beispielAuftrag, beispielSettings, leseSeiten } from './auftraegePdfHilfe.js'
// Die alte AB liegt im Appwrite-Nachbau (Schema aus L0); erzeugeAuftragPdf liest sie über getAuftrag aus auftragStore.js
const aw = await startAppwriteMock({ schema: { auftraege: schemaKeys('auftraege') } })
after(() => aw.close())
useTestEnv({ appwriteUrl: aw.url, stripeUrl: 'http://127.0.0.1:1' })
const { erzeugeAuftragPdf } = await import('../server/services/auftraege/pdf.js')
const { AB_TEXTE } = await import('../server/services/auftraege/texte.js')
const { AuftragError } = await import('../server/services/auftraege/fehler.js')
const optionen = { settings: beispielSettings, jetzt: new Date('2026-10-06T10:00:00.000Z') }
const ohneLeerraum = (text) => text.replace(/\s+/g, '')
const text = (pdf) => ohneLeerraum(leseSeiten(pdf).flat().join(''))
const hinweis = ohneLeerraum(AB_TEXTE.ersetzt({ nummer: 'AB-2026-0003', datum: '01.10.2026' }))
aw.seed('auftraege', { $id: 'alt1', kundenId: 'k1', status: 'versendet', nummer: 'AB-2026-0003', gesendetAm: '2026-10-01T09:00:00.000+00:00' })
test('ersetztId ohne Option: die alte AB wird gelesen, ihre Nummer und ihr Berliner Datum stehen im PDF', async () => {
const pdf = await erzeugeAuftragPdf(beispielAuftrag({ ersetztId: 'alt1' }), optionen)
assert.ok(text(pdf).includes(hinweis))
})
test('ersetztId verweist auf eine unbekannte AB: AuftragError 409 statt PDF ohne den Hinweis', async () => {
await assert.rejects(erzeugeAuftragPdf(beispielAuftrag({ ersetztId: 'gibtsnicht' }), optionen), (e) => e instanceof AuftragError && e.status === 409)
})
test('ersetzt: null unterdrückt Hinweis und Lesen, auch bei gesetztem ersetztId', async () => {
const pdf = await erzeugeAuftragPdf(beispielAuftrag({ ersetztId: 'gibtsnicht' }), { ...optionen, ersetzt: null })
assert.equal(text(pdf).includes(hinweis), false)
})