diff --git a/README.md b/README.md index 6be9c2c..857a160 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,12 @@ Serverseitig erzeugt, alle mit Kopf/Fuß aus *Einstellungen → Export* (Logo, A - **Stundennachweis je Monat** (PDF, Excel mit Summenformeln, CSV mit UTF-8-BOM/Semikolon): eigener Nachweis, eines Teammitglieds oder aller; filterbar nach Kunde/Projekt; optional mit Sätzen und Beträgen für die Rechnung. - **Zeitreport** nach Kunden/Projekten/Tasks/Mitarbeitern oder als Detailliste. +Im PDF darf die Beschriftung der Summenzeile in die leeren Spalten dahinter laufen +(`merge` in `drawRow`, `server/src/export/render.js`). Ohne das stand „Summe" in der +schmalen Spalte *Tag* und brach dort in drei Zeilen um, während rechts daneben die +Zahlen standen. Gefüllte Spalten werden nicht einverleibt, die Zahlen bleiben also, +wo sie sind. + ## Listenansichten `src/components/booking/EntryList.tsx` zeigt einzelne Buchungen als sortierbare Tabelle: Datum, optional Mitarbeiter, diff --git a/server/src/export/render.js b/server/src/export/render.js index b5c3f4e..fd543b0 100644 --- a/server/src/export/render.js +++ b/server/src/export/render.js @@ -392,10 +392,23 @@ function drawTable(pdf, table, { left, width, accent }) { } header(); - const drawRow = (data, { zebra = false, bold = false } = {}) => { + const drawRow = (data, { zebra = false, bold = false, merge = false } = {}) => { const texts = table.columns.map((c) => formatCell(data[c.key], c.type)); + /* + * In der Summenzeile darf eine Beschriftung in die leeren Spalten dahinter + * laufen. Ohne das stand "Summe" in der schmalen Spalte "Tag" und brach + * dort in drei Zeilen um ("Su/mm/e"), waehrend rechts daneben die Zahlen + * standen. Die Zahlenspalten selbst bleiben unberuehrt, weil sie gefuellt + * sind und deshalb nicht einverleibt werden. + */ + const spans = cols.map((w, i) => { + if (!merge || !texts[i]) return w; + let total = w; + for (let j = i + 1; j < texts.length && !texts[j]; j++) total += cols[j]; + return total; + }); pdf.font(bold ? 'Helvetica-Bold' : 'Helvetica').fontSize(8.5); - const height = Math.max(16, ...texts.map((t, i) => pdf.heightOfString(t || ' ', { width: cols[i] - 10 }) + 8)); + const height = Math.max(16, ...texts.map((t, i) => pdf.heightOfString(t || ' ', { width: spans[i] - 10 }) + 8)); if (pdf.y + height > bottomLimit()) { pdf.addPage(); header(); @@ -408,8 +421,10 @@ function drawTable(pdf, table, { left, width, accent }) { let x = left; texts.forEach((text, i) => { pdf.font(bold ? 'Helvetica-Bold' : 'Helvetica').fontSize(8.5).fillColor('#1F1F1F').text(text, x + 5, y + 4, { - width: cols[i] - 10, + width: spans[i] - 10, align: table.columns[i].align || 'left', + // Eine Summenzeile bleibt einzeilig; lieber gekuerzt als gequetscht. + ...(merge ? { lineBreak: false, ellipsis: true } : {}), }); x += cols[i]; }); @@ -422,7 +437,7 @@ function drawTable(pdf, table, { left, width, accent }) { pdf.y += 10; } table.rows.forEach((row, index) => drawRow(row, { zebra: index % 2 === 1 })); - if (table.totals) drawRow(table.totals, { bold: true }); + if (table.totals) drawRow(table.totals, { bold: true, merge: true }); pdf.x = left; pdf.moveDown(0.8); }