print file with appendix

This commit is contained in:
Julian Krauser 2025-07-12 16:38:14 +02:00
parent 9ef82adef7
commit 98ce39efc5
7 changed files with 97 additions and 24 deletions

View file

@ -16,6 +16,8 @@ import { InspectionPointEnum } from "../../../enums/inspectionEnum";
import multer from "multer"; import multer from "multer";
import { FileSystemHelper } from "../../../helpers/fileSystemHelper"; import { FileSystemHelper } from "../../../helpers/fileSystemHelper";
import { PdfExport } from "../../../helpers/pdfExport"; import { PdfExport } from "../../../helpers/pdfExport";
import { PDFDocument } from "pdf-lib";
import sharp from "sharp";
/** /**
* @description get all inspections sorted by id not having newer inspection * @description get all inspections sorted by id not having newer inspection
@ -240,13 +242,37 @@ export async function finishInspection(req: Request, res: Response): Promise<any
formattedInspection.inspectionPlan.title formattedInspection.inspectionPlan.title
}_${new Date(formattedInspection.finished ?? "").toLocaleDateString("de-de")}`; }_${new Date(formattedInspection.finished ?? "").toLocaleDateString("de-de")}`;
await PdfExport.renderFile({ let inspectionPoints = [];
for (const ip of formattedInspection.inspectionVersionedPlan.inspectionPoints.sort(
(a, b) => (a.sort ?? 0) - (b.sort ?? 0)
)) {
let value = formattedInspection.checks.find((c) => c.inspectionPointId == ip.id).value;
let image = "";
if (ip.type == InspectionPointEnum.file && ip.others == "img") {
const imagePath = FileSystemHelper.formatPath("inspection", inspection.id, value);
let pngImageBytes = await sharp(imagePath).png().toBuffer();
image = `data:image/png;base64,${pngImageBytes.toString("base64")}`;
} else if (ip.type == InspectionPointEnum.oknok) {
value = value ? "OK" : "Nicht OK";
}
inspectionPoints.push({
title: ip.title,
description: ip.description,
type: ip.type,
min: ip.min,
max: ip.max,
others: ip.others,
value: value,
image: image,
});
}
let pdf = await PdfExport.renderFile({
template: "inspection", template: "inspection",
title, title,
filename: "printout", saveToDisk: false,
folder: `inspection/${inspection.id}`,
data: { data: {
inspector: `${req.userId}`, inspector: `${req.lastname}, ${req.firstname}`,
context: formattedInspection.context || "---", context: formattedInspection.context || "---",
createdAt: formattedInspection.created, createdAt: formattedInspection.created,
finishedAt: formattedInspection.finished ?? new Date(), finishedAt: formattedInspection.finished ?? new Date(),
@ -255,23 +281,55 @@ export async function finishInspection(req: Request, res: Response): Promise<any
plan: formattedInspection.inspectionPlan, plan: formattedInspection.inspectionPlan,
planVersion: formattedInspection.inspectionVersionedPlan.version, planVersion: formattedInspection.inspectionVersionedPlan.version,
planTitle: formattedInspection.inspectionPlan.title, planTitle: formattedInspection.inspectionPlan.title,
checks: formattedInspection.inspectionVersionedPlan.inspectionPoints checks: inspectionPoints,
.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0))
.map((ip) => ({
title: ip.title,
description: ip.description,
type: ip.type,
min: ip.min,
max: ip.max,
value:
ip.type == InspectionPointEnum.file
? "siehe Anhang"
: formattedInspection.checks.find((c) => c.inspectionPointId == ip.id).value,
})),
}, },
}); });
// TODO: Anhang hinzufügen const finalDocument = await PDFDocument.create();
const printout = await PDFDocument.load(pdf);
const copiedPages = await finalDocument.copyPages(printout, printout.getPageIndices());
copiedPages.forEach((page) => finalDocument.addPage(page));
let resultsForAppend = inspectionPoints.filter((ip) => ip.type == InspectionPointEnum.file && ip.others == "pdf");
if (resultsForAppend.length !== 0) {
const appendixPage = finalDocument.addPage();
const { width, height } = appendixPage.getSize();
appendixPage.drawText("Anhang:", {
x: 50,
y: height - 50,
size: 24,
});
}
for (const appendix of resultsForAppend) {
const appendixPdfBytes = FileSystemHelper.readFileAsBase64("inspection", inspection.id, appendix.value);
const appendixPdf = await PDFDocument.load(appendixPdfBytes);
const appendixPages = await finalDocument.copyPages(appendixPdf, appendixPdf.getPageIndices());
appendixPages.forEach((page) => finalDocument.addPage(page));
/** print image
const imagePath = FileSystemHelper.formatPath("inspection", inspection.id, checkValue);
let pngImageBytes = await sharp(imagePath).png().toBuffer();
let image = await finalDocument.embedPng(pngImageBytes);
let dims = image.scale(1);
if (image) {
const page = finalDocument.addPage();
const { width, height } = page.getSize();
const x = (width - dims.width) / 2;
const y = (height - dims.height) / 2;
page.drawImage(image, {
x,
y,
width: dims.width,
height: dims.height,
});
}
*/
}
const mergedPdfBytes = await finalDocument.save();
FileSystemHelper.writeFile(`inspection/${inspection.id}`, `printout.pdf`, mergedPdfBytes);
let finish: FinishInspectionCommand = { let finish: FinishInspectionCommand = {
id: inspectionId, id: inspectionId,

View file

@ -59,3 +59,7 @@ Handlebars.registerHelper("longdatetimeWithWeekday", function (aString) {
Handlebars.registerHelper("json", function (context) { Handlebars.registerHelper("json", function (context) {
return JSON.stringify(context); return JSON.stringify(context);
}); });
Handlebars.registerHelper("eq", function (p, q, options) {
return p == q ? options.fn(this) : options.inverse(this);
});

View file

@ -15,7 +15,7 @@ export abstract class FileSystemHelper {
return readFileSync(this.formatPath(...filePath), "utf8"); return readFileSync(this.formatPath(...filePath), "utf8");
} }
static readFileasBase64(...filePath: string[]) { static readFileAsBase64(...filePath: string[]) {
this.createFolder(...filePath); this.createFolder(...filePath);
return readFileSync(this.formatPath(...filePath), "base64"); return readFileSync(this.formatPath(...filePath), "base64");
} }

View file

@ -127,7 +127,7 @@ export abstract class PdfExport {
const mergedPdf = await PDFDocument.create(); const mergedPdf = await PDFDocument.create();
for (const pdfPath of pdfFilePaths) { for (const pdfPath of pdfFilePaths) {
const pdfBytes = FileSystemHelper.readFileasBase64(inputFolder, pdfPath); const pdfBytes = FileSystemHelper.readFileAsBase64(inputFolder, pdfPath);
const pdf = await PDFDocument.load(pdfBytes); const pdf = await PDFDocument.load(pdfBytes);
const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices()); const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices());
copiedPages.forEach((page) => mergedPdf.addPage(page)); copiedPages.forEach((page) => mergedPdf.addPage(page));

View file

@ -11,6 +11,8 @@ declare global {
export interface Request { export interface Request {
userId: string; userId: string;
username: string; username: string;
firstname: string;
lastname: string;
isOwner: boolean; isOwner: boolean;
permissions: PermissionObject; permissions: PermissionObject;
isPWA: boolean; isPWA: boolean;

View file

@ -35,6 +35,8 @@ export default async function authenticate(req: Request, res: Response, next: Ne
req.userId = decoded.userId; req.userId = decoded.userId;
req.username = decoded.username; req.username = decoded.username;
req.firstname = decoded.firstname;
req.lastname = decoded.lastname;
req.isOwner = decoded.isOwner; req.isOwner = decoded.isOwner;
req.permissions = decoded.permissions; req.permissions = decoded.permissions;
req.isWebApiRequest = decoded?.sub == "webapi_access_token"; req.isWebApiRequest = decoded?.sub == "webapi_access_token";

View file

@ -17,16 +17,23 @@
<p>Kontext: {{context}}</p> <p>Kontext: {{context}}</p>
<br /> <br />
<p>Prüfpunkte:</p> <h3>Prüfpunkte:</h3>
<table style="width: 100%"> <table style="width: 100%">
{{#each checks}} {{#each checks}}
<tr> <tr>
<td style="width: 50%; padding: 10px 5px"> <td style="width: 30%; padding: 10px 5px; word-break: break-word">
{{this.title}} {{this.title}}
<br /> <br />
{{this.description}} {{#if this.description}} > {{this.description}}
<br />
{{/if}}
<small>(Typ: {{this.type}})</small>
</td>
<td style="max-width: 70%; width: 70%; padding: 10px 5px; word-break: break-word">
{{#eq this.type "file"}} {{#eq this.others "img"}}
<img style="width: 100%; height: auto" src="{{ this.image }}" />
{{else}} siehe Anhang {{/eq}} {{else}} {{this.value}} {{/eq}}
</td> </td>
<td style="width: 50%; padding: 10px 5px">{{this.value}}</td>
</tr> </tr>
{{/each}} {{/each}}
</table> </table>