Time Migration
This commit is contained in:
198
src/utils/pdf.ts
198
src/utils/pdf.ts
@@ -878,11 +878,10 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da
|
||||
let pages = []
|
||||
let pageCounter = 1
|
||||
|
||||
|
||||
|
||||
const backgroudPdf = await PDFDocument.load(backgroundSourceBuffer)
|
||||
|
||||
const firstPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[0])
|
||||
// Fallback für einseitige Hintergründe
|
||||
const secondPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[backgroudPdf.getPages().length > 1 ? 1 : 0])
|
||||
|
||||
const page1 = pdfDoc.addPage()
|
||||
@@ -894,121 +893,145 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da
|
||||
|
||||
pages.push(page1)
|
||||
|
||||
console.log("PDF Input Data:", input)
|
||||
|
||||
//Falzmarke 1
|
||||
/*pages[pageCounter - 1].drawLine({
|
||||
start: getCoordinatesForPDFLib(0,105,page1),
|
||||
end: getCoordinatesForPDFLib(7,105,page1),
|
||||
thickness: 0.25,
|
||||
color: rgb(0,0,0),
|
||||
opacity: 1
|
||||
})*/
|
||||
// ---------------------------------------------------------
|
||||
// DATEN-EXTRAKTION MIT FALLBACKS (Calculated vs. Standard)
|
||||
// ---------------------------------------------------------
|
||||
|
||||
//Lochmarke
|
||||
/*pages[pageCounter - 1].drawLine({
|
||||
start: getCoordinatesForPDFLib(0,148.5,page1),
|
||||
end: getCoordinatesForPDFLib(7,148.5,page1),
|
||||
thickness: 0.25,
|
||||
color: rgb(0,0,0),
|
||||
opacity: 1
|
||||
})*/
|
||||
// Summen: Bevorzuge calculated..., falls vorhanden
|
||||
const sumSubmitted = input.calculatedSumWorkingMinutesSubmitted ?? input.sumWorkingMinutesSubmitted ?? 0;
|
||||
const sumApproved = input.calculatedSumWorkingMinutesApproved ?? input.sumWorkingMinutesApproved ?? 0;
|
||||
|
||||
// Saldi: Bevorzuge calculated...
|
||||
const saldoSubmitted = input.calculatedSaldoSubmitted ?? input.saldoSubmitted ?? input.saldoInOfficial ?? 0;
|
||||
const saldoApproved = input.calculatedSaldoApproved ?? input.saldoApproved ?? input.saldo ?? 0;
|
||||
|
||||
// Andere Summen (diese sind meist korrekt vom Backend)
|
||||
const sumRecreation = input.sumWorkingMinutesRecreationDays ?? 0;
|
||||
const sumVacation = input.sumWorkingMinutesVacationDays ?? 0;
|
||||
const sumSick = input.sumWorkingMinutesSickDays ?? 0;
|
||||
const sumTarget = input.timeSpanWorkingMinutes ?? 0;
|
||||
|
||||
// Hilfsfunktion zur Formatierung von Minuten -> HH:MM
|
||||
const fmtTime = (mins) => {
|
||||
const m = Math.floor(Math.abs(mins));
|
||||
return `${Math.floor(m / 60)}:${String(m % 60).padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
const fmtSaldo = (mins) => {
|
||||
const sign = Math.sign(mins) >= 0 ? "+" : "-";
|
||||
return `${sign} ${fmtTime(mins)}`;
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// HEADER TEXTE ZEICHNEN
|
||||
// ---------------------------------------------------------
|
||||
|
||||
//Falzmarke 2
|
||||
/*pages[pageCounter - 1].drawLine({
|
||||
start: getCoordinatesForPDFLib(0,210,page1),
|
||||
end: getCoordinatesForPDFLib(7,210,page1),
|
||||
thickness: 0.25,
|
||||
color: rgb(0,0,0),
|
||||
opacity: 1
|
||||
})*/
|
||||
console.log(input)
|
||||
pages[pageCounter - 1].drawText(`Anwesenheitsauswertung`,{
|
||||
x: getCoordinatesForPDFLib(20,40,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,40,pages[pageCounter -1]).y,
|
||||
x: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).y,
|
||||
size: 15,
|
||||
font: fontBold
|
||||
})
|
||||
|
||||
pages[pageCounter - 1].drawText(`Mitarbeiter: ${input.full_name}`,{
|
||||
x: getCoordinatesForPDFLib(20,50,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,50,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
pages[pageCounter - 1].drawText(`Nummer: ${input.employee_number}`,{
|
||||
x: getCoordinatesForPDFLib(20,55,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,55,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
pages[pageCounter - 1].drawText(`Eingereicht: ${Math.floor(input.sumWorkingMinutesEingereicht/60)}:${String(input.sumWorkingMinutesEingereicht % 60).padStart(2,"0")} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,60,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
pages[pageCounter - 1].drawText(`Genehmigt: ${Math.floor(input.sumWorkingMinutesApproved/60)}:${String(input.sumWorkingMinutesApproved % 60).padStart(2,"0")} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,65,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,65,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
pages[pageCounter - 1].drawText(`Feiertagsausgleich: ${Math.floor(input.sumWorkingMinutesRecreationDays/60)}:${String(input.sumWorkingMinutesRecreationDays % 60).padStart(2,"0")} Std`,{
|
||||
pages[pageCounter - 1].drawText(`Mitarbeiter: ${input.full_name || ''}`,{
|
||||
x: getCoordinatesForPDFLib(20,70,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,70,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
pages[pageCounter - 1].drawText(`Urlaubsausgleich: ${Math.floor(input.sumWorkingMinutesVacationDays/60)}:${String(input.sumWorkingMinutesVacationDays % 60).padStart(2,"0")} Std`,{
|
||||
pages[pageCounter - 1].drawText(`Nummer: ${input.employee_number || '-'}`,{
|
||||
x: getCoordinatesForPDFLib(20,75,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,75,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
pages[pageCounter - 1].drawText(`Krankheitsausgleich: ${Math.floor(input.sumWorkingMinutesSickDays/60)}:${String(input.sumWorkingMinutesSickDays % 60).padStart(2,"0")} Std`,{
|
||||
|
||||
// Zeile 1: Eingereicht & Genehmigt
|
||||
pages[pageCounter - 1].drawText(`Eingereicht: ${fmtTime(sumSubmitted)} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,80,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,80,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
pages[pageCounter - 1].drawText(`Soll Stunden: ${Math.floor(input.timeSpanWorkingMinutes/60)}:${Math.floor(Number(String(input.timeSpanWorkingMinutes % 60).padStart(2,"0")))} Std`,{
|
||||
pages[pageCounter - 1].drawText(`Genehmigt: ${fmtTime(sumApproved)} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,85,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,85,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
pages[pageCounter - 1].drawText(`Inoffizielles Saldo: ${Math.sign(input.saldoInOfficial) === 1 ? "+" : "-"} ${Math.floor(Math.abs(input.saldoInOfficial/60))}:${Math.floor(Number(String(Math.abs(input.saldoInOfficial) % 60).padStart(2,"0")))} Std`,{
|
||||
|
||||
// Zeile 2: Ausgleichstage
|
||||
pages[pageCounter - 1].drawText(`Feiertagsausgleich: ${fmtTime(sumRecreation)} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,90,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,90,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
pages[pageCounter - 1].drawText(`Saldo: ${Math.sign(input.saldo) === 1 ? "+" : "-"} ${Math.floor(Math.abs(input.saldo/60))}:${Math.floor(Number(String(Math.abs(input.saldo) % 60).padStart(2,"0")))} Std`,{
|
||||
pages[pageCounter - 1].drawText(`Urlaubsausgleich: ${fmtTime(sumVacation)} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
pages[pageCounter - 1].drawText(`Start:`,{
|
||||
pages[pageCounter - 1].drawText(`Krankheitsausgleich: ${fmtTime(sumSick)} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,100,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,100,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
// Zeile 3: Soll & Saldo
|
||||
pages[pageCounter - 1].drawText(`Soll Stunden: ${fmtTime(sumTarget)} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,105,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,105,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
// Wir nutzen hier die Begriffe "Inoffiziell" (Submitted Saldo) und "Saldo" (Approved Saldo)
|
||||
pages[pageCounter - 1].drawText(`Inoffizielles Saldo: ${fmtSaldo(saldoSubmitted)} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,110,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,110,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
pages[pageCounter - 1].drawText(`Saldo: ${fmtSaldo(saldoApproved)} Std`,{
|
||||
x: getCoordinatesForPDFLib(20,115,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,115,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
// Tabellen-Header
|
||||
pages[pageCounter - 1].drawText(`Start:`,{
|
||||
x: getCoordinatesForPDFLib(20,125,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,125,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
pages[pageCounter - 1].drawText(`Ende:`,{
|
||||
x: getCoordinatesForPDFLib(60,100,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(60,100,pages[pageCounter -1]).y,
|
||||
x: getCoordinatesForPDFLib(60,125,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(60,125,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
pages[pageCounter - 1].drawText(`Dauer:`,{
|
||||
x: getCoordinatesForPDFLib(100,100,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(100,100,pages[pageCounter -1]).y,
|
||||
x: getCoordinatesForPDFLib(100,125,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(100,125,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// TABELLE GENERIEREN (Spans verarbeiten)
|
||||
// ---------------------------------------------------------
|
||||
|
||||
let rowHeight = 115
|
||||
let rowHeight = 130
|
||||
|
||||
// WICHTIG: input.spans verwenden, fallback auf input.times (altes Format)
|
||||
// Wir filtern leere Einträge raus
|
||||
const rawItems = (input.spans || input.times || []).filter(t => t);
|
||||
|
||||
// Sortierung umkehren (neueste zuletzt für den Druck? Oder wie gewünscht)
|
||||
// Im Original war es .reverse().
|
||||
let reversedInput = rawItems.slice().reverse();
|
||||
|
||||
let splitted = []
|
||||
|
||||
let reversedInput = input.times.slice().reverse()
|
||||
|
||||
const splittedLength = Math.floor((reversedInput.length - 25) / 40)
|
||||
|
||||
// Erste Seite hat weniger Platz wegen Header (25 Zeilen)
|
||||
splitted.push(reversedInput.slice(0,25))
|
||||
|
||||
let lastIndex = 25
|
||||
@@ -1017,10 +1040,11 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da
|
||||
lastIndex = lastIndex + (i + 1) * 40 + 1
|
||||
}
|
||||
|
||||
if(reversedInput.slice(lastIndex, reversedInput.length).length > 0) splitted.push(reversedInput.slice(lastIndex, reversedInput.length))
|
||||
|
||||
console.log(splitted )
|
||||
if(reversedInput.slice(lastIndex, reversedInput.length).length > 0) {
|
||||
splitted.push(reversedInput.slice(lastIndex, reversedInput.length))
|
||||
}
|
||||
|
||||
console.log("PDF Pages Chunks:", splitted.length)
|
||||
|
||||
splitted.forEach((chunk,index) => {
|
||||
if(index > 0) {
|
||||
@@ -1034,30 +1058,50 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da
|
||||
pages.push(page)
|
||||
pageCounter++
|
||||
rowHeight = 20
|
||||
|
||||
}
|
||||
|
||||
chunk.forEach(time => {
|
||||
pages[pageCounter - 1].drawText(`${dayjs(time.started_at).format("HH:mm DD.MM.YY")}`,{
|
||||
chunk.forEach(span => {
|
||||
// Mapping für Felder: spans nutzen 'startedAt', times nutzten 'started_at'
|
||||
const startStr = span.startedAt || span.started_at;
|
||||
const endStr = span.endedAt || span.stopped_at; // endedAt oder stopped_at
|
||||
|
||||
// Dauer berechnen (da Spans keine duration_minutes haben)
|
||||
let durationStr = "";
|
||||
if (startStr && endStr) {
|
||||
const diffMins = dayjs(endStr).diff(dayjs(startStr), 'minute');
|
||||
durationStr = fmtTime(diffMins);
|
||||
} else if (span.duration_minutes) {
|
||||
durationStr = fmtTime(span.duration_minutes);
|
||||
} else if (span.duration) { // Falls schon formatiert übergeben
|
||||
durationStr = span.duration;
|
||||
}
|
||||
|
||||
pages[pageCounter - 1].drawText(`${dayjs(startStr).format("HH:mm DD.MM.YY")}`,{
|
||||
x: getCoordinatesForPDFLib(20,rowHeight,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(20,rowHeight,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
pages[pageCounter - 1].drawText(`${dayjs(time.stopped_at).format("HH:mm DD.MM.YY")}`,{
|
||||
pages[pageCounter - 1].drawText(`${endStr ? dayjs(endStr).format("HH:mm DD.MM.YY") : 'läuft...'}`,{
|
||||
x: getCoordinatesForPDFLib(60,rowHeight,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(60,rowHeight,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
pages[pageCounter - 1].drawText(`${getDuration(time).composed}`,{
|
||||
pages[pageCounter - 1].drawText(`${durationStr}`,{
|
||||
x: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).y,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
rowHeight += 6
|
||||
// Optional: Status anzeigen?
|
||||
/*pages[pageCounter - 1].drawText(`${span.status || span.state || ''}`,{
|
||||
x: getCoordinatesForPDFLib(130,rowHeight,pages[pageCounter -1]).x,
|
||||
y: getCoordinatesForPDFLib(130,rowHeight,pages[pageCounter -1]).y,
|
||||
size: 8,
|
||||
})*/
|
||||
|
||||
rowHeight += 6
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1077,8 +1121,6 @@ export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, da
|
||||
}
|
||||
} catch(error) {
|
||||
console.log(error)
|
||||
throw error; // Fehler weiterwerfen, damit er oben ankommt
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user