diff --git a/src/modules/time/evaluation.service.ts b/src/modules/time/evaluation.service.ts new file mode 100644 index 0000000..745711d --- /dev/null +++ b/src/modules/time/evaluation.service.ts @@ -0,0 +1,128 @@ +import {FastifyInstance} from "fastify"; + + +export async function generateTimesEvaluation( + server: FastifyInstance, + user_id: string, + tenant_id: number, + startDateInput: string, + endDateInput: string +) { + const startDate = server.dayjs(startDateInput) + const endDate = server.dayjs(endDateInput) + + // 🧾 Profil laden (Arbeitszeiten + Bundesland) + const { data: profile, error: profileError } = await server.supabase + .from("auth_profiles") + .select("*") + .eq("user_id", user_id) + .eq("tenant_id", tenant_id) + .maybeSingle() + + if (profileError || !profile) throw new Error("Profil konnte nicht geladen werden.") + + // 🕒 Arbeitszeiten abrufen + const { data: times, error: timeError } = await server.supabase + .from("staff_time_entries") + .select("*") + .eq("tenant_id", tenant_id) + .eq("user_id", user_id) + .gte("started_at", startDate.toISOString()) + .lte("started_at", endDate.toISOString()) + .order("started_at", { ascending: true }) + + if (timeError) throw new Error("Fehler beim Laden der Arbeitszeiten: " + timeError.message) + + // 📅 Feiertage aus Tabelle für Bundesland + DE + const { data: holidays, error: holidaysError } = await server.supabase + .from("holidays") + .select("date") + .in("state_code", [profile.state_code, "DE"]) + .gte("date", startDate.format("YYYY-MM-DD")) + .lte("date", endDate.format("YYYY-MM-DD")) + + if (holidaysError) throw new Error("Fehler beim Laden der Feiertage: " + holidaysError.message) + + // 🗓️ Sollzeit berechnen + let timeSpanWorkingMinutes = 0 + const totalDays = endDate.add(1, "day").diff(startDate, "days") + + for (let i = 0; i < totalDays; i++) { + const date = startDate.add(i, "days") + const weekday = date.day() + timeSpanWorkingMinutes += (profile.weekly_regular_working_hours?.[weekday] || 0) * 60 + } + + // 🧮 Eingereicht & genehmigt + const calcMinutes = (start: string, end: string | null) => + server.dayjs(end || new Date()).diff(server.dayjs(start), "minutes") + + let sumWorkingMinutesEingereicht = 0 + let sumWorkingMinutesApproved = 0 + + for (const t of times) { + const minutes = calcMinutes(t.started_at, t.stopped_at) + if(["submitted","approved"].includes(t.state))sumWorkingMinutesEingereicht += minutes + if (t.state === "approved") sumWorkingMinutesApproved += minutes + } + + // 🎉 Feiertagsausgleich + let sumWorkingMinutesRecreationDays = 0 + let sumRecreationDays = 0 + + if (profile.recreationDaysCompensation && holidays?.length) { + holidays.forEach(({ date }) => { + const weekday = server.dayjs(date).day() + const hours = profile.weekly_regular_working_hours?.[weekday] || 0 + sumWorkingMinutesRecreationDays += hours * 60 + sumRecreationDays++ + }) + } + + // 🏖️ Urlaub & Krankheit (über Typ) + const sumWorkingMinutesVacationDays = times + .filter((t) => t.type === "vacation") + .reduce((sum, t) => sum + calcMinutes(t.started_at, t.stopped_at), 0) + + const sumWorkingMinutesSickDays = times + .filter((t) => t.type === "sick") + .reduce((sum, t) => sum + calcMinutes(t.started_at, t.stopped_at), 0) + + const sumVacationDays = times.filter((t) => t.type === "vacation").length + const sumSickDays = times.filter((t) => t.type === "sick").length + + // 💰 Salden + const saldo = + sumWorkingMinutesApproved + + sumWorkingMinutesRecreationDays + + sumWorkingMinutesVacationDays + + sumWorkingMinutesSickDays - + timeSpanWorkingMinutes + + const saldoInOfficial = + sumWorkingMinutesEingereicht + + sumWorkingMinutesRecreationDays + + sumWorkingMinutesVacationDays + + sumWorkingMinutesSickDays - + timeSpanWorkingMinutes + + // 📦 Rückgabe (kompatibel zur alten Struktur) + return { + user_id, + tenant_id, + from: startDate.format("YYYY-MM-DD"), + to: endDate.format("YYYY-MM-DD"), + timeSpanWorkingMinutes, + sumWorkingMinutesEingereicht, + sumWorkingMinutesApproved, + sumWorkingMinutesRecreationDays, + sumRecreationDays, + sumWorkingMinutesVacationDays, + sumVacationDays, + sumWorkingMinutesSickDays, + sumSickDays, + saldo, + saldoInOfficial, + times + } +} \ No newline at end of file diff --git a/src/routes/functions.ts b/src/routes/functions.ts index 512bb01..bcd49f9 100644 --- a/src/routes/functions.ts +++ b/src/routes/functions.ts @@ -1,5 +1,5 @@ import { FastifyInstance } from "fastify"; -import {createInvoicePDF} from "../utils/pdf"; +import {createInvoicePDF, createTimeSheetPDF} from "../utils/pdf"; import {useNextNumberRangeNumber} from "../utils/functions"; import dayjs from "dayjs"; @@ -10,6 +10,7 @@ import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js" import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js" import duration from "dayjs/plugin/duration.js"; import timezone from "dayjs/plugin/timezone.js"; +import {generateTimesEvaluation} from "../modules/time/evaluation.service"; dayjs.extend(customParseFormat) dayjs.extend(isoWeek) dayjs.extend(isBetween) @@ -19,19 +20,35 @@ dayjs.extend(duration) dayjs.extend(timezone) export default async function functionRoutes(server: FastifyInstance) { - server.post("/functions/createinvoicepdf", async (req, reply) => { + server.post("/functions/pdf/:type", async (req, reply) => { const body = req.body as { - invoiceData: any + data: any backgroundPath?: string } + const {type} = req.params as {type:string} + try { - const pdf = await createInvoicePDF( - server, - "base64", - body.invoiceData, - body.backgroundPath - ) + + let pdf = null + + if(type === "createdDocument") { + pdf = await createInvoicePDF( + server, + "base64", + body.data, + body.backgroundPath + ) + } else if(type === "timesheet") { + pdf = await createTimeSheetPDF( + server, + "base64", + body.data, + body.backgroundPath + ) + } + + console.log(pdf) reply.send(pdf) // Fastify wandelt automatisch in JSON } catch (err) { @@ -53,186 +70,83 @@ export default async function functionRoutes(server: FastifyInstance) { } }) - server.get("/functions/workingtimeevaluation/:profile_id", async (req, reply) => { + /** + * @route GET /functions/workingtimeevaluation/:user_id + * @query start_date=YYYY-MM-DD + * @query end_date=YYYY-MM-DD + */ + server.get("/functions/timeevaluation/:user_id", async (req, reply) => { + const { user_id } = req.params as { user_id: string } + const { start_date, end_date } = req.query as { start_date: string; end_date: string } + const { tenant_id } = req.user - const { profile_id } = req.params as { profile_id: string }; - const { start_date, end_date } = req.query as { start_date: string, end_date: string }; - - - async function generateWorkingTimesEvaluationValues(profile_id,startDateInput,endDateInput) { - - console.log(dayjs(startDateInput)) - console.log(dayjs(endDateInput)) - - let startDate = dayjs(startDateInput) - let endDate = dayjs(endDateInput) - //console.log(startDate) - //console.log(endDate) - - const {data:profile} = await server.supabase.from("auth_profiles").select().eq("old_profile_id",profile_id).single() - - console.log(profile) - - let {data:times,error:timesError} = await server.supabase.from("workingtimes").select().eq("profile", profile.old_profile_id).order("startDate",{ascending: false}) - const {data:absencerequests, error: timesAbsenceRequestsError} = await server.supabase.from("absencerequests").select().eq("profile",profile.old_profile_id).order("startDate",{ascending: false}) - - times = times.filter(i => dayjs(i.startDate).isSameOrAfter(startDate) && dayjs(i.endDate).subtract(1,"day").isSameOrBefore(endDate)) - - console.log(times) - - let weekFactor = 4.33 - //let monthlyWorkingMinutes = profile.weeklyWorkingHours * weekFactor * 60 - - //Get Every Day to Calc - let days = [] - let minuteSum = 0 - let dayCount = dayjs(endDate).add(1,"day").diff(startDate,"days") - for (let count = 0; count < dayCount; count++) { - let date = dayjs(startDate).add(count,"days") - minuteSum += (profile.weekly_regular_working_hours[date.day()] || 0)*60 - } - let timeSpanWorkingMinutes = minuteSum - - - - - let workingMinutesTarget = Math.ceil(Number((Number(dayjs(endDate).add(1,"days").diff(dayjs(startDate),'month',true).toFixed(2)) * 4.33 * profile.weeklyWorkingHours * 60).toFixed(2))) - - //Eingreicht - let sumWorkingMinutesEingereicht = 0 - times.forEach(time => { - const minutes = dayjs(time.endDate).diff(dayjs(time.startDate),'minutes') - sumWorkingMinutesEingereicht = sumWorkingMinutesEingereicht + minutes - }) - - //Bestätigt - let sumWorkingMinutesApproved = 0 - times.filter(i => i.approved).forEach(time => { - const minutes = dayjs(time.endDate).diff(dayjs(time.startDate),'minutes') - sumWorkingMinutesApproved = sumWorkingMinutesApproved + minutes - }) - - let recreationDays = ["2025-01-01","2025-04-18","2025-04-21","2025-05-01","2025-05-29","2025-06-09","2024-10-03","2024-10-31","2024-12-25","2024-12-26"] - - //Feiertagsausgleich - let sumWorkingMinutesRecreationDays = 0 - let sumRecreationDays = 0 - if(profile.recreationDaysCompensation) { - recreationDays.filter(i => dayjs(i).isSameOrAfter(startDate) && dayjs(i).isSameOrBefore(endDate)).forEach(day => { - let compensationTime = profile.weekly_regular_working_hours[dayjs(day).day()] || 0 - sumWorkingMinutesRecreationDays += compensationTime * 60 - sumRecreationDays++ - }) - } - - - let isBetween = (date,start,end) => { - return dayjs(date).isSameOrAfter(start) && dayjs(date).isSameOrBefore(end) - } - - //Urlaubsausgleich - let sumWorkingMinutesVacationDays = 0 - let sumVacationDays = 0 - - absencerequests.filter(i => (dayjs(i.startDate).isBetween(dayjs(startDate),dayjs(endDate),"day","[]") || dayjs(i.endDate).isBetween(dayjs(startDate),dayjs(endDate),"day","[]")) && (i.reason === "Urlaub" || i.reason === "Berufsschule") && i.approved === "Genehmigt").forEach(absenceRequest => { - let durationInDays = 0 - - let durationStartDate = null - - if(isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { - //Full in Range - durationInDays = dayjs(absenceRequest.endDate).diff(absenceRequest.startDate, "days") + 1 - durationStartDate = absenceRequest.startDate - } else if(isBetween(absenceRequest.startDate,startDate,endDate) && !isBetween(absenceRequest.endDate,startDate,endDate)) { - //Start in Range - durationInDays = dayjs(endDate).diff(absenceRequest.startDate, "days") + 1 - durationStartDate = absenceRequest.startDate - } else if(!isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { - //End in Range - durationInDays = dayjs(absenceRequest.endDate).diff(startDate, "days") + 1 - durationStartDate = startDate - - } - - let minuteSum = 0 - - for (let count = 0; count < durationInDays; count++) { - let date = dayjs(durationStartDate).add(count,"days") - minuteSum += (profile.weekly_regular_working_hours[date.day()] || 0)*60 - } - sumVacationDays += durationInDays - sumWorkingMinutesVacationDays += minuteSum - }) - - //Krankheitsausgleich - let sumWorkingMinutesSickDays = 0 - let sumSickDays = 0 - - absencerequests.filter(i => (dayjs(i.startDate).isBetween(dayjs(startDate),dayjs(endDate)) || dayjs(i.endDate).isBetween(dayjs(startDate),dayjs(endDate)) ) && (i.reason === "Krankheit" || i.reason === "Krankheit ab 1 Tag (mit Attest)" || i.reason === "Krankheit ab 2. Tag (mit Attest)") && i.approved === "Genehmigt").forEach(absenceRequest => { - let durationInDays = 0 - - let durationStartDate = null - - if(isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { - //Full in Range - console.log("FULL") - durationInDays = dayjs(absenceRequest.endDate).diff(absenceRequest.startDate, "days") + 1 - durationStartDate = absenceRequest.startDate - } else if(isBetween(absenceRequest.startDate,startDate,endDate) && !isBetween(absenceRequest.endDate,startDate,endDate)) { - //Start in Range - console.log("Start") - durationInDays = dayjs(endDate).diff(absenceRequest.startDate, "days") + 1 - durationStartDate = absenceRequest.startDate - } else if(!isBetween(absenceRequest.startDate,startDate,endDate) && isBetween(absenceRequest.endDate,startDate,endDate)) { - //End in Range - console.log("End") - durationInDays = dayjs(absenceRequest.endDate).diff(startDate, "days") + 1 - durationStartDate = startDate - - } - - let minuteSum = 0 - - for (let count = 0; count < durationInDays; count++) { - let date = dayjs(durationStartDate).add(count,"days") - minuteSum += (profile.weekly_regular_working_hours[date.day()] || 0)*60 - } - - sumSickDays += durationInDays - sumWorkingMinutesSickDays += minuteSum - }) - - //Saldo - let saldo = (sumWorkingMinutesApproved + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - timeSpanWorkingMinutes).toFixed(2) - let saldoInOfficial = (sumWorkingMinutesEingereicht + sumWorkingMinutesRecreationDays + sumWorkingMinutesVacationDays + sumWorkingMinutesSickDays - timeSpanWorkingMinutes).toFixed(2) - - return { - timeSpanWorkingMinutes, - workingMinutesTarget, - sumWorkingMinutesEingereicht, - sumWorkingMinutesApproved, - sumWorkingMinutesRecreationDays, - sumRecreationDays, - sumWorkingMinutesVacationDays, - sumVacationDays, - sumWorkingMinutesSickDays, - sumSickDays, - saldo, - saldoInOfficial, - times - } + // 🔒 Sicherheitscheck: andere User nur bei Berechtigung + if (user_id !== req.user.user_id && !req.hasPermission("staff.time.read_all")) { + return reply.code(403).send({ error: "Not allowed to view other users." }) } - - try { - reply.send(await generateWorkingTimesEvaluationValues(profile_id,start_date,end_date)) - } catch(error) { - console.log(error) + const result = await generateTimesEvaluation(server, user_id, tenant_id, start_date, end_date) + reply.send(result) + } catch (error) { + console.error(error) + reply.code(500).send({ error: error.message }) + } + }) + + server.get('/functions/check-zip/:zip', async (req, reply) => { + const { zip } = req.params as { zip: string } + + if (!zip) { + return reply.code(400).send({ error: 'ZIP is required' }) } + try { + const { data, error } = await server.supabase + .from('citys') + .select() + .eq('zip', zip) + .maybeSingle() + + if (error) { + console.log(error) + return reply.code(500).send({ error: 'Database error' }) + } + + if (!data) { + return reply.code(404).send({ error: 'ZIP not found' }) + } + + //districtMap + const bundeslaender = [ + { code: 'DE-BW', name: 'Baden-Württemberg' }, + { code: 'DE-BY', name: 'Bayern' }, + { code: 'DE-BE', name: 'Berlin' }, + { code: 'DE-BB', name: 'Brandenburg' }, + { code: 'DE-HB', name: 'Bremen' }, + { code: 'DE-HH', name: 'Hamburg' }, + { code: 'DE-HE', name: 'Hessen' }, + { code: 'DE-MV', name: 'Mecklenburg-Vorpommern' }, + { code: 'DE-NI', name: 'Niedersachsen' }, + { code: 'DE-NW', name: 'Nordrhein-Westfalen' }, + { code: 'DE-RP', name: 'Rheinland-Pfalz' }, + { code: 'DE-SL', name: 'Saarland' }, + { code: 'DE-SN', name: 'Sachsen' }, + { code: 'DE-ST', name: 'Sachsen-Anhalt' }, + { code: 'DE-SH', name: 'Schleswig-Holstein' }, + { code: 'DE-TH', name: 'Thüringen' } + ] + + return reply.send({ + ...data, + state_code: bundeslaender.find(i => i.name === data.countryName) + }) + } catch (err) { + console.log(err) + return reply.code(500).send({ error: 'Internal server error' }) + } }) } \ No newline at end of file diff --git a/src/utils/pdf.ts b/src/utils/pdf.ts index 904bbbe..c5eae47 100644 --- a/src/utils/pdf.ts +++ b/src/utils/pdf.ts @@ -23,13 +23,27 @@ const getCoordinatesForPDFLib = (x:number ,y:number, page:any) => { } +const getBackgroundSourceBuffer = async (server:FastifyInstance, path:string) => { + const {data:backgroundPDFData,error:backgroundPDFError} = await server.supabase.storage.from("files").download(path) + + return backgroundPDFData.arrayBuffer() +} + +const getDuration = (time) => { + const minutes = Math.floor(dayjs(time.stopped_at).diff(dayjs(time.started_at),'minutes',true)) + const hours = Math.floor(minutes/60) + return { + //dezimal: dez, + hours: hours, + minutes: minutes, + composed: `${hours}:${String(minutes % 60).padStart(2,"0")} Std` + } +} export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoiceData, backgroundPath:string) => { - console.log(returnMode, invoiceData, backgroundPath) - const genPDF = async (invoiceData, backgroundSourceBuffer) => { const pdfDoc = await PDFDocument.create() @@ -841,14 +855,216 @@ export const createInvoicePDF = async (server:FastifyInstance, returnMode, invoi } - const {data:backgroundPDFData,error:backgroundPDFError} = await server.supabase.storage.from("files").download(backgroundPath) - - const pdfBytes = await genPDF(invoiceData, await backgroundPDFData.arrayBuffer()) + const pdfBytes = await genPDF(invoiceData, await getBackgroundSourceBuffer(server,backgroundPath)) if(returnMode === "base64"){ return { mimeType: 'application/pdf', base64: pdfBytes } + } else { + return null } +} + +export const createTimeSheetPDF = async (server: FastifyInstance, returnMode, data, backgroundPath: string) => { + + const genPDF = async (input, backgroundSourceBuffer) => { + const pdfDoc = await PDFDocument.create() + + const font = await pdfDoc.embedFont(StandardFonts.Helvetica) + const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold) + + let pages = [] + let pageCounter = 1 + + + + const backgroudPdf = await PDFDocument.load(backgroundSourceBuffer) + + const firstPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[0]) + const secondPageBackground = await pdfDoc.embedPage(backgroudPdf.getPages()[backgroudPdf.getPages().length > 1 ? 1 : 0]) + + const page1 = pdfDoc.addPage() + + page1.drawPage(firstPageBackground, { + x: 0, + y: 0, + }) + + pages.push(page1) + + + //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 + })*/ + + //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 + })*/ + + //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(`Mitarbeiter: ${input.full_name}`,{ + 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`,{ + 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`,{ + 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`,{ + 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`,{ + 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`,{ + 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`,{ + x: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,95,pages[pageCounter -1]).y, + size: 10, + }) + + pages[pageCounter - 1].drawText(`Start:`,{ + x: getCoordinatesForPDFLib(20,100,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(20,100,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, + size: 10, + }) + + pages[pageCounter - 1].drawText(`Dauer:`,{ + x: getCoordinatesForPDFLib(100,100,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(100,100,pages[pageCounter -1]).y, + size: 10, + }) + + + let rowHeight = 115 + + + let splitted = [] + + let reversedInput = input.times.slice().reverse() + + const splittedLength = Math.floor((reversedInput.length - 25) / 40) + + splitted.push(reversedInput.slice(0,25)) + + let lastIndex = 25 + for (let i = 0; i < splittedLength; ++i ) { + splitted.push(reversedInput.slice(lastIndex, lastIndex + (i + 1) * 40)) + lastIndex = lastIndex + (i + 1) * 40 + 1 + } + + splitted.push(reversedInput.slice(lastIndex, reversedInput.length)) + + + splitted.forEach((chunk,index) => { + if(index > 0) { + const page = pdfDoc.addPage() + + page.drawPage(secondPageBackground, { + x: 0, + y: 0, + }) + + pages.push(page) + pageCounter++ + rowHeight = 20 + + } + + chunk.forEach(time => { + pages[pageCounter - 1].drawText(`${dayjs(time.started_at).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")}`,{ + 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}`,{ + x: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).x, + y: getCoordinatesForPDFLib(100,rowHeight,pages[pageCounter -1]).y, + size: 10, + }) + + rowHeight += 6 + + }) + }) + + return await pdfDoc.saveAsBase64() + } + + try { + const pdfBytes = await genPDF(data, await getBackgroundSourceBuffer(server,backgroundPath)) + + if(returnMode === "base64"){ + return { + mimeType: 'application/pdf', + base64: pdfBytes + } + } else { + return "test" + } + } catch(error) { + console.log(error) + } + + + } \ No newline at end of file