Functions Time Eval and PDF
This commit is contained in:
128
src/modules/time/evaluation.service.ts
Normal file
128
src/modules/time/evaluation.service.ts
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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' })
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
226
src/utils/pdf.ts
226
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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user