feat(mcp): Vorschau von Ausgangsbelegen mit Seitenbildern und Umbruchprüfung
- accounting.outgoing_documents.preview rendert den Beleg wie der Editor und liefert Seiten als PNG-Bilder im MCP-Ergebnis - Layout je Seite in mm mit Warnungen zu übervollen Seiten, allein stehenden Überschriften, leeren Seiten und vom Überweisungs-QR-Code überdeckten Beträgen samt Vorschlag für den Seitenumbruch - rows testet geänderte Positionen samt Seitenumbrüchen, ohne zu speichern - Belegdaten für das PDF als Backend-Port von getDocumentData (Titelsummen, Überträge, Abschläge, Optional-/Alternativpositionen) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -117,7 +117,17 @@ const getDuration = (time) => {
|
||||
}
|
||||
|
||||
|
||||
export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoiceData, backgroundPath:string) => {
|
||||
/** Positions in mm from the top edge, recorded while rendering for layout checks (e.g. page breaks). */
|
||||
export type InvoicePdfLayout = {
|
||||
pageHeightMm: number
|
||||
rows: Array<{ index: number, id?: string, pos?: string, mode: string, text: string, page: number, topMm: number, bottomMm: number }>
|
||||
totals?: { page: number, topMm: number, bottomMm: number }
|
||||
endText?: { page: number, topMm: number, bottomMm: number }
|
||||
paymentQr?: { page: number, topMm: number, leftMm: number }
|
||||
pageCount: number
|
||||
}
|
||||
|
||||
export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoiceData, backgroundPath:string, layout?: InvoicePdfLayout) => {
|
||||
const deliveryNoteLikeDocumentTypes = ["deliveryNotes", "packingSlips"]
|
||||
const isPackingSlip = invoiceData?.type === "packingSlips"
|
||||
|
||||
@@ -496,6 +506,7 @@ export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoi
|
||||
}
|
||||
|
||||
invoiceData.rows.forEach((row, index) => {
|
||||
const layoutTop = rowHeight
|
||||
|
||||
if (!["pagebreak", "title", "text"].includes(row.mode)) {
|
||||
|
||||
@@ -881,10 +892,16 @@ export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoi
|
||||
}
|
||||
}
|
||||
console.log(rowHeight)
|
||||
layout?.rows.push({
|
||||
index, id: row.id, pos: row.pos, mode: row.mode, text: String(row.text || "").slice(0, 80),
|
||||
// rowHeight is the baseline of the first text line; shift to the visible cap height.
|
||||
page: pageCounter, topMm: row.mode === "pagebreak" ? 30 : layoutTop - 3, bottomMm: row.mode === "pagebreak" ? 30 : rowHeight - 3,
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
let endTextDiff = 35
|
||||
const layoutContentEnd = rowHeight
|
||||
|
||||
if (!deliveryNoteLikeDocumentTypes.includes(invoiceData.type)) {
|
||||
pages[pageCounter - 1].drawLine({
|
||||
@@ -1001,6 +1018,18 @@ export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoi
|
||||
|
||||
const endTextPosition = getCoordinatesForPDFLib(21, rowHeight + endTextDiff + (invoiceData.totalArray.length - 3) * 8, page1)
|
||||
|
||||
if (layout) {
|
||||
const maxWidth = getTextWidthBeforePaymentQr(pages[pageCounter - 1], endTextPosition.x)
|
||||
const lines = String(invoiceData.endText || "").split("\n")
|
||||
.reduce((sum, line) => sum + Math.max(1, Math.ceil(font.widthOfTextAtSize(line, 10) / maxWidth)), 0)
|
||||
const topMm = rowHeight + endTextDiff + (invoiceData.totalArray.length - 3) * 8
|
||||
// Totals are baselines 8 mm apart; tax notes (13b/19/12.3) add one more line below.
|
||||
const totalLines = invoiceData.totalArray.length + (["13b UStG", "19 UStG", "12.3 UStG"].includes(invoiceData.taxType) ? 1 : 0)
|
||||
layout.totals = { page: pageCounter, topMm: layoutContentEnd, bottomMm: rowHeight + (totalLines - 1) * 8 + 1 }
|
||||
// drawText places the baseline of the first line; 10 pt line height ≈ 3.53 mm.
|
||||
layout.endText = { page: pageCounter, topMm: topMm - 3.53, bottomMm: topMm + (lines - 1) * 3.53 + 1 }
|
||||
}
|
||||
|
||||
pages[pageCounter - 1].drawText(invoiceData.endText, {
|
||||
...endTextPosition,
|
||||
size: 10,
|
||||
@@ -1030,6 +1059,12 @@ export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoi
|
||||
font: fontBold,
|
||||
})
|
||||
|
||||
if (layout) layout.paymentQr = {
|
||||
page: pages.length,
|
||||
topMm: (lastPage.getHeight() - (qrY + qrSize + labelGap + 8)) / mm,
|
||||
leftMm: qrX / mm,
|
||||
}
|
||||
|
||||
lastPage.drawImage(paymentQrCode, {
|
||||
x: qrX,
|
||||
y: qrY,
|
||||
@@ -1038,6 +1073,11 @@ export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoi
|
||||
})
|
||||
}
|
||||
|
||||
if (layout) {
|
||||
layout.pageCount = pages.length
|
||||
layout.pageHeightMm = page1.getHeight() / mm
|
||||
}
|
||||
|
||||
return await pdfDoc.saveAsBase64()
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user