Time Migration

This commit is contained in:
2025-12-14 16:29:08 +01:00
parent 267a57c4ea
commit 1281976ec3

View File

@@ -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
}
}