Functions Time Eval and PDF

This commit is contained in:
2025-11-08 18:59:47 +01:00
parent 6d0b764ee2
commit 29bebe6149
3 changed files with 444 additions and 186 deletions

View File

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