- Treatment records supplier, purchase proof and prescribing vet, and keeps the apiary the colony stood at on the treatment day. - Holder name and address in site settings (never in public output; regression test in public.service.spec). - treatment-log.pdf: one sheet per apiary, identical treatments grouped, missing mandatory fields printed as FEHLT, uncertain locations marked. - Shared PDF table helpers extracted from the record card. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
108 lines
2.7 KiB
TypeScript
108 lines
2.7 KiB
TypeScript
/**
|
||
* Small pdfkit helpers shared by the printable records (Stockkarte,
|
||
* Bestandsbuch): dates, empty values and a table that repeats its header on
|
||
* every page.
|
||
*/
|
||
|
||
export const PDF_MARGIN = 36;
|
||
|
||
export interface PdfColumn {
|
||
header: string;
|
||
width: number;
|
||
}
|
||
|
||
export function formatDay(date: Date): string {
|
||
return date.toLocaleDateString('de-DE', {
|
||
day: '2-digit',
|
||
month: '2-digit',
|
||
year: 'numeric',
|
||
timeZone: 'UTC',
|
||
});
|
||
}
|
||
|
||
export function orDash(value: string | number | null | undefined): string {
|
||
return value === null || value === undefined || value === ''
|
||
? '–'
|
||
: String(value);
|
||
}
|
||
|
||
/** Widens the last column so the table fills the page width. */
|
||
export function fillWidth(
|
||
doc: PDFKit.PDFDocument,
|
||
columns: PdfColumn[],
|
||
): PdfColumn[] {
|
||
const fixed = columns.slice(0, -1).reduce((sum, c) => sum + c.width, 0);
|
||
const last = columns[columns.length - 1];
|
||
return [
|
||
...columns.slice(0, -1),
|
||
{ ...last, width: doc.page.width - 2 * PDF_MARGIN - fixed },
|
||
];
|
||
}
|
||
|
||
export function drawSection(doc: PDFKit.PDFDocument, title: string): void {
|
||
if (doc.y > doc.page.height - PDF_MARGIN - 80) doc.addPage();
|
||
doc.moveDown(0.6);
|
||
doc
|
||
.font('Helvetica-Bold')
|
||
.fontSize(11)
|
||
.fillColor('#000000')
|
||
.text(title, PDF_MARGIN);
|
||
doc.moveDown(0.2);
|
||
}
|
||
|
||
/** Draws a simple table, repeating the header on every new page. */
|
||
export function drawTable(
|
||
doc: PDFKit.PDFDocument,
|
||
columns: PdfColumn[],
|
||
rows: string[][],
|
||
): void {
|
||
const left = PDF_MARGIN;
|
||
const bottomLimit = doc.page.height - PDF_MARGIN - 20;
|
||
|
||
const drawRow = (cells: string[], bold: boolean) => {
|
||
doc.font(bold ? 'Helvetica-Bold' : 'Helvetica').fontSize(8);
|
||
const heights = cells.map((cell, index) =>
|
||
doc.heightOfString(cell, { width: columns[index].width - 4 }),
|
||
);
|
||
const rowHeight = Math.max(...heights, 10) + 4;
|
||
if (doc.y + rowHeight > bottomLimit) {
|
||
doc.addPage();
|
||
if (!bold) {
|
||
drawRow(
|
||
columns.map((c) => c.header),
|
||
true,
|
||
);
|
||
doc.font('Helvetica').fontSize(8);
|
||
}
|
||
}
|
||
const top = doc.y;
|
||
let x = left;
|
||
cells.forEach((cell, index) => {
|
||
doc.text(cell, x + 2, top + 2, { width: columns[index].width - 4 });
|
||
x += columns[index].width;
|
||
});
|
||
doc
|
||
.moveTo(left, top + rowHeight)
|
||
.lineTo(x, top + rowHeight)
|
||
.lineWidth(0.3)
|
||
.strokeColor('#999999')
|
||
.stroke();
|
||
doc.y = top + rowHeight;
|
||
};
|
||
|
||
drawRow(
|
||
columns.map((c) => c.header),
|
||
true,
|
||
);
|
||
if (rows.length === 0) {
|
||
doc
|
||
.font('Helvetica-Oblique')
|
||
.fontSize(8)
|
||
.text('Keine Einträge.', left + 2, doc.y + 2);
|
||
doc.moveDown(0.5);
|
||
return;
|
||
}
|
||
for (const row of rows) drawRow(row, false);
|
||
doc.moveDown(0.5);
|
||
}
|