import "server-only";
import { htmlZuPdf, html } from "@/lib/pdf";
import { alsDatum, alsDatumZeit, alsEuro } from "@/lib/utils";
import { ERGEBNIS_BEZEICHNUNG, type Ergebnis, type PunktTyp } from "@/lib/wartung/bewertung";
/**
* Das Prüfprotokoll als PDF.
*
* Es ist der Nachweis, den die Wehr bei einer Prüfung durch die Unfallkasse
* vorlegt. Danach richtet sich der Aufbau:
*
* Kopf wer geprüft hat, welche Wehr, welches Gerät, welche Vorschrift
* Mitte jeder Checklistenpunkt mit Soll, Ist und Befund
* Fotos eingebettet, nicht verlinkt — ein Link ins Nichts ist kein Beleg
* Mängel was aufgefallen ist
* Fuß Unterschriften, Prüfvorschrift, Prüfdatum, Seitenzahl
*
* Alles ist eingebettet: keine externen Schriften, keine nachgeladenen Bilder.
* Ein Protokoll muss in zehn Jahren noch genauso aussehen wie heute, auch wenn
* es dann keinen Server mehr gibt, der irgendetwas nachliefert.
*/
/** Bild als data-URI, damit es fest im PDF steckt. */
export function alsDataUri(inhalt: Buffer, mimeTyp: string): string {
return `data:${mimeTyp};base64,${inhalt.toString("base64")}`;
}
export interface ProtokollPunkt {
text: string;
typ: PunktTyp;
pflicht: boolean;
sollwert?: string | null;
einheit?: string | null;
/** Die Antwort, schon für die Anzeige aufbereitet. */
istwert: string;
inOrdnung: boolean;
bemerkung?: string | null;
/** Foto zum Punkt, bereits als data-URI. */
bild?: string | null;
}
export interface ProtokollSignatur {
name: string;
rolle: string;
unterzeichnetAm: Date;
/** PNG des Zeichenfelds als data-URI. */
bild?: string | null;
}
export interface ProtokollDaten {
organisation: {
name: string;
strasse?: string | null;
plz?: string | null;
ort?: string | null;
};
geraet: {
inventarnummer: string;
bezeichnung: string;
hersteller?: string | null;
modell?: string | null;
seriennummer?: string | null;
typName?: string | null;
lagerung?: string | null;
baujahr?: Date | null;
indienststellung?: Date | null;
};
pruefung: {
protokollNummer?: string | null;
durchgefuehrtAm: Date;
art: "INTERN" | "EXTERN";
ergebnis: Ergebnis;
dauerMinuten?: number | null;
prueferName?: string | null;
/** Mitgliedsnummer oder Kennung des Prüfers — die "Prüfer-ID" im Kopf. */
prueferKennung?: string | null;
externeFirma?: string | null;
kosten?: number | null;
naechstePruefungAm?: Date | null;
bemerkung?: string | null;
/** Bei einer Korrektur: Nummer des ersetzten Protokolls. */
ersetztProtokollNummer?: string | null;
korrekturGrund?: string | null;
};
vorlage?: {
name: string;
pruefvorschrift?: string | null;
intervallMonate: number;
} | null;
punkte: ProtokollPunkt[];
maengel: Array<{ beschreibung: string; prioritaet: string }>;
signaturen: ProtokollSignatur[];
erzeugtAm: Date;
}
const ERGEBNIS_FARBE: Record = {
BESTANDEN: "#15803d",
BESTANDEN_MIT_MAENGELN: "#b45309",
NICHT_BESTANDEN: "#b91c1c",
};
function zeile(bezeichnung: string, wert: string | null | undefined): string {
return `
${html(bezeichnung)}
${html(wert && wert !== "" ? wert : "—")}
`;
}
function punktZeile(punkt: ProtokollPunkt, nummer: number): string {
const soll = [punkt.sollwert, punkt.einheit].filter(Boolean).join(" ");
return `
Erstellt mit FLORIAN am ${html(alsDatumZeit(daten.erzeugtAm))}. Dieses
Protokoll ist nach dem Abschluss unveränderbar gespeichert. Korrekturen
erscheinen als eigenes Protokoll mit Verweis auf dieses.
`;
}
/** Kopfzeile des PDF — sie wiederholt sich auf jeder Seite. */
export function kopfzeileHtml(daten: ProtokollDaten): string {
return `