fix: weekbased calculation pdf report
with this change the time calculations for pdf reports should be better line with the reports send as "week_report"
This commit is contained in:
@@ -21,66 +21,7 @@ import (
|
||||
const DE_DATE string = "02.01.2006"
|
||||
const FILE_YEAR_MONTH string = "2006_01"
|
||||
|
||||
func convertDaysToTypst(days []models.IWorkDay, u models.User) ([]typstDay, error) {
|
||||
var typstDays []typstDay
|
||||
for _, day := range days {
|
||||
var thisTypstDay typstDay
|
||||
work, pause, overtime := day.GetTimes(u, models.WorktimeBaseDay, false)
|
||||
workVirtual := day.GetWorktime(u, models.WorktimeBaseDay, true)
|
||||
overtime = workVirtual - u.ArbeitszeitProWocheFrac(0.2)
|
||||
thisTypstDay.Date = day.Date().Format(DE_DATE)
|
||||
thisTypstDay.Worktime = helper.FormatDurationFill(workVirtual, true)
|
||||
thisTypstDay.Pausetime = helper.FormatDurationFill(pause, true)
|
||||
thisTypstDay.Overtime = helper.FormatDurationFill(overtime, true)
|
||||
thisTypstDay.IsFriday = day.Date().Weekday() == time.Friday
|
||||
|
||||
if workVirtual > work {
|
||||
thisTypstDay.Kurzarbeit = helper.FormatDurationFill(workVirtual-work, true)
|
||||
} else {
|
||||
thisTypstDay.Kurzarbeit = helper.FormatDurationFill(0, true)
|
||||
}
|
||||
|
||||
thisTypstDay.DayParts = convertDayToTypstDayParts(day, u)
|
||||
typstDays = append(typstDays, thisTypstDay)
|
||||
}
|
||||
return typstDays, nil
|
||||
}
|
||||
|
||||
func convertDayToTypstDayParts(day models.IWorkDay, user models.User) []typstDayPart {
|
||||
var typstDayParts []typstDayPart
|
||||
switch day.Type() {
|
||||
case models.DayTypeWorkday:
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
for i := 0; i < len(workDay.Bookings); i += 2 {
|
||||
var typstDayPart typstDayPart
|
||||
typstDayPart.BookingFrom = workDay.Bookings[i].Timestamp.Format("15:04")
|
||||
if i+1 < len(workDay.Bookings) {
|
||||
typstDayPart.BookingTo = workDay.Bookings[i+1].Timestamp.Format("15:04")
|
||||
} else {
|
||||
typstDayPart.BookingTo = workDay.Bookings[i].Timestamp.Format("15:04")
|
||||
}
|
||||
typstDayPart.WorkType = workDay.Bookings[i].BookingType.Name
|
||||
typstDayPart.IsWorkDay = true
|
||||
typstDayParts = append(typstDayParts, typstDayPart)
|
||||
}
|
||||
if day.IsKurzArbeit() && len(workDay.Bookings) > 0 {
|
||||
tsFrom, tsTo := workDay.GenerateKurzArbeitBookings(user)
|
||||
typstDayParts = append(typstDayParts, typstDayPart{
|
||||
BookingFrom: tsFrom.Format("15:04"),
|
||||
BookingTo: tsTo.Format("15:04"),
|
||||
WorkType: "Kurzarbeit",
|
||||
IsWorkDay: true,
|
||||
})
|
||||
}
|
||||
case models.DayTypeCompound:
|
||||
for _, c := range day.(*models.CompoundDay).DayParts {
|
||||
typstDayParts = append(typstDayParts, convertDayToTypstDayParts(c, user)...)
|
||||
}
|
||||
default:
|
||||
typstDayParts = append(typstDayParts, typstDayPart{IsWorkDay: false, WorkType: day.ToString()})
|
||||
}
|
||||
return typstDayParts
|
||||
}
|
||||
const PDF_DIRECTORY = "/home/tom/Code/arbeitszeitmessung/Backend/doc"
|
||||
|
||||
func PDFCreateController(w http.ResponseWriter, r *http.Request) {
|
||||
helper.RequiresLogin(Session, w, r)
|
||||
@@ -101,14 +42,16 @@ func PDFCreateController(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
n := 0
|
||||
for _, e := range employes {
|
||||
if user.IsSuperior(e) {
|
||||
employes[n] = e
|
||||
n++
|
||||
if !helper.IsDebug() {
|
||||
n := 0
|
||||
for _, e := range employes {
|
||||
if user.IsSuperior(e) {
|
||||
employes[n] = e
|
||||
n++
|
||||
}
|
||||
}
|
||||
employes = employes[:n]
|
||||
}
|
||||
employes = employes[:n]
|
||||
|
||||
reportData := createReports(employes, startDate)
|
||||
|
||||
@@ -119,8 +62,9 @@ func PDFCreateController(w http.ResponseWriter, r *http.Request) {
|
||||
slog.Warn("Could not create pdf report", slog.Any("Error", err))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-type", "application/pdf")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=Monatsabrechnung_%s", startDate.Format(FILE_YEAR_MONTH)))
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=Monatsabrechnung_%s.pdf", startDate.Format(FILE_YEAR_MONTH)))
|
||||
output.WriteTo(w)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
case "download":
|
||||
@@ -131,11 +75,12 @@ func PDFCreateController(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
output, err := zipPfd(pdfReports, &reportData)
|
||||
if err != nil {
|
||||
slog.Warn("Could not create pdf report", slog.Any("Error", err))
|
||||
slog.Warn("Could not zip pdf reports", slog.Any("Error", err))
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-type", "application/zip")
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachement; filename=Monatsabrechnung_%s", startDate.Format(FILE_YEAR_MONTH)))
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachement; filename=Monatsabrechnung_%s.zip", startDate.Format(FILE_YEAR_MONTH)))
|
||||
output.WriteTo(w)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
@@ -145,6 +90,73 @@ func PDFCreateController(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func convertDaysToTypst(days []models.IWorkDay, u models.User, weekbase models.WorktimeBase) ([]typstDay, error) {
|
||||
var typstDays []typstDay
|
||||
for i, day := range days {
|
||||
if !day.IsSubmittedAndAccepted() && !helper.IsDebug() {
|
||||
continue
|
||||
}
|
||||
|
||||
var thisTypstDay typstDay
|
||||
workVirtual, pause, overtime := day.GetTimes(u, weekbase, true)
|
||||
|
||||
if day.Type() != models.DayTypeHoliday {
|
||||
overtime = workVirtual - u.ArbeitszeitProWocheFrac(0.2)
|
||||
}
|
||||
thisTypstDay.Date = day.Date().Format(DE_DATE)
|
||||
thisTypstDay.Worktime = helper.FormatDurationFill(workVirtual, true)
|
||||
thisTypstDay.Pausetime = helper.FormatDurationFill(pause, true)
|
||||
thisTypstDay.Overtime = helper.FormatDurationFill(overtime, true)
|
||||
thisTypstDay.IsFriday = i == len(days)-1
|
||||
|
||||
if work := day.GetWorktime(u, weekbase, false); workVirtual > work {
|
||||
thisTypstDay.Kurzarbeit = helper.FormatDurationFill(workVirtual-work, true)
|
||||
} else {
|
||||
thisTypstDay.Kurzarbeit = helper.FormatDurationFill(0, true)
|
||||
}
|
||||
|
||||
thisTypstDay.DayParts = convertDayToTypstDayParts(day, u, weekbase)
|
||||
typstDays = append(typstDays, thisTypstDay)
|
||||
}
|
||||
return typstDays, nil
|
||||
}
|
||||
|
||||
func convertDayToTypstDayParts(day models.IWorkDay, user models.User, weekBase models.WorktimeBase) []typstDayPart {
|
||||
var typstDayParts []typstDayPart
|
||||
switch day.Type() {
|
||||
case models.DayTypeWorkday:
|
||||
workDay, _ := day.(*models.WorkDay)
|
||||
for i := 0; i < len(workDay.Bookings); i += 2 {
|
||||
var typstDayPart typstDayPart
|
||||
typstDayPart.BookingFrom = workDay.Bookings[i].Timestamp.Format("15:04")
|
||||
if i+1 < len(workDay.Bookings) {
|
||||
typstDayPart.BookingTo = workDay.Bookings[i+1].Timestamp.Format("15:04")
|
||||
} else {
|
||||
typstDayPart.BookingTo = workDay.Bookings[i].Timestamp.Format("15:04")
|
||||
}
|
||||
typstDayPart.WorkType = workDay.Bookings[i].BookingType.Name
|
||||
typstDayPart.IsWorkDay = true
|
||||
typstDayParts = append(typstDayParts, typstDayPart)
|
||||
}
|
||||
if day.IsKurzArbeit() {
|
||||
tsFrom, tsTo := workDay.GenerateKurzArbeitBookings(user, weekBase)
|
||||
typstDayParts = append(typstDayParts, typstDayPart{
|
||||
BookingFrom: tsFrom.Format("15:04"),
|
||||
BookingTo: tsTo.Format("15:04"),
|
||||
WorkType: "Kurzarbeit",
|
||||
IsWorkDay: true,
|
||||
})
|
||||
}
|
||||
case models.DayTypeCompound:
|
||||
for _, c := range day.(*models.CompoundDay).DayParts {
|
||||
typstDayParts = append(typstDayParts, convertDayToTypstDayParts(c, user, weekBase)...)
|
||||
}
|
||||
default:
|
||||
typstDayParts = append(typstDayParts, typstDayPart{IsWorkDay: false, WorkType: day.ToString()})
|
||||
}
|
||||
return typstDayParts
|
||||
}
|
||||
|
||||
func createReports(employes []models.User, startDate time.Time) []typstData {
|
||||
startDate = helper.GetFirstOfMonth(startDate)
|
||||
endDate := startDate.AddDate(0, 1, -1)
|
||||
@@ -161,28 +173,67 @@ func createReports(employes []models.User, startDate time.Time) []typstData {
|
||||
}
|
||||
|
||||
func createEmployeReport(employee models.User, startDate, endDate time.Time) (typstData, error) {
|
||||
publicHolidays, err := models.GetHolidaysFromTo(startDate, endDate)
|
||||
publicHolidays, _ := models.GetHolidaysFromTo(startDate, endDate)
|
||||
targetHoursThisMonth := employee.ArbeitszeitProWocheFrac(.2) * time.Duration(helper.GetWorkingDays(startDate, endDate)-len(publicHolidays))
|
||||
workDaysThisMonth := models.GetDays(employee, startDate, endDate.AddDate(0, 0, 1), false)
|
||||
|
||||
slog.Debug("Baseline Working hours", "targetHours", targetHoursThisMonth.Hours())
|
||||
daysThisMonth := helper.GenerateDateRange(startDate, endDate)
|
||||
mondaysThisMonth := helper.GetMondays(daysThisMonth, false)
|
||||
|
||||
var weeks []models.WorkWeek
|
||||
var workHours, kurzarbeitHours time.Duration
|
||||
for _, day := range workDaysThisMonth {
|
||||
tmpvirtualHours := day.GetWorktime(employee, models.WorktimeBaseDay, true)
|
||||
tmpactualHours := day.GetWorktime(employee, models.WorktimeBaseDay, false)
|
||||
if day.IsKurzArbeit() && tmpvirtualHours > tmpactualHours {
|
||||
slog.Debug("Adding kurzarbeit to workday", "day", day.Date())
|
||||
kurzarbeitHours += tmpvirtualHours - tmpactualHours
|
||||
for _, monday := range mondaysThisMonth {
|
||||
var week models.WorkWeek
|
||||
if monday.After(startDate) {
|
||||
week = models.NewWorkWeekSimple(employee, monday, true)
|
||||
} else if startDate.Sub(monday) < time.Hour*24*6 {
|
||||
week = models.NewWorkWeek(employee, startDate, monday.Add(6*24*time.Hour), true)
|
||||
}
|
||||
workHours += tmpvirtualHours
|
||||
workHours += week.WorktimeVirtual
|
||||
kurzarbeitHours += week.WorktimeVirtual - week.Worktime
|
||||
weeks = append(weeks, week)
|
||||
}
|
||||
var typstDays []typstDay
|
||||
for _, week := range weeks {
|
||||
weekTypstDays, err := convertDaysToTypst(week.Days, employee, week.WeekBase)
|
||||
if err != nil {
|
||||
slog.Error("Error converting days into typst", "error", err)
|
||||
continue
|
||||
}
|
||||
typstDays = append(typstDays, weekTypstDays...)
|
||||
}
|
||||
|
||||
slog.Info("Weeks for the month", "week len", len(weeks), "week", weeks)
|
||||
// workDaysThisMonth := models.GetDays(employee, startDate, endDate.AddDate(0, 0, 1), false)
|
||||
|
||||
// var weekbase models.WorktimeBase
|
||||
// if lenWorkDays(workDaysThisMonth) == helper.GetWorkingDays(startDate, endDate) {
|
||||
// weekbase = models.WorktimeBaseWeek
|
||||
// } else {
|
||||
// weekbase = models.WorktimeBaseDay
|
||||
// }
|
||||
|
||||
// slog.Debug("Baseline Working hours", "targetHours", targetHoursThisMonth.Hours(), "days", helper.GetWorkingDays(startDate, endDate), "workdays", lenWorkDays(workDaysThisMonth))
|
||||
|
||||
// var workHours, kurzarbeitHours time.Duration
|
||||
// for _, day := range workDaysThisMonth {
|
||||
// tmpvirtualHours := day.GetWorktime(employee, weekbase, true)
|
||||
// tmpactualHours := day.GetWorktime(employee, weekbase, false)
|
||||
// if day.IsKurzArbeit() && tmpvirtualHours > tmpactualHours {
|
||||
// slog.Debug("Adding kurzarbeit to workday", "day", day.Date())
|
||||
// kurzarbeitHours += tmpvirtualHours - tmpactualHours
|
||||
// }
|
||||
// workHours += tmpvirtualHours
|
||||
// }
|
||||
worktimeBalance := workHours - targetHoursThisMonth
|
||||
|
||||
typstDays, err := convertDaysToTypst(workDaysThisMonth, employee)
|
||||
// typstDays, err := convertDaysToTypst(workDaysThisMonth, employee, weekbase)
|
||||
// if err != nil {
|
||||
// slog.Warn("Failed to convert to days", slog.Any("error", err))
|
||||
// return typstData{}, err
|
||||
// }
|
||||
|
||||
totalOvertime, err := employee.GetReportedOvertime(endDate)
|
||||
if err != nil {
|
||||
slog.Warn("Failed to convert to days", slog.Any("error", err))
|
||||
return typstData{}, err
|
||||
slog.Error("Cannot retrieve total Overtime", "Error", err)
|
||||
}
|
||||
|
||||
metadata := typstMetadata{
|
||||
@@ -191,7 +242,7 @@ func createEmployeReport(employee models.User, startDate, endDate time.Time) (ty
|
||||
Overtime: helper.FormatDurationFill(worktimeBalance, true),
|
||||
WorkTime: helper.FormatDurationFill(workHours, true),
|
||||
Kurzarbeit: helper.FormatDurationFill(kurzarbeitHours, true),
|
||||
OvertimeTotal: "",
|
||||
OvertimeTotal: helper.FormatDurationFill(totalOvertime+worktimeBalance, true),
|
||||
CurrentTimestamp: time.Now().Format("02.01.2006 - 15:04 Uhr"),
|
||||
}
|
||||
return typstData{Meta: metadata, Days: typstDays, FileName: fmt.Sprintf("%s_%s.pdf", startDate.Format(FILE_YEAR_MONTH), employee.Name)}, nil
|
||||
@@ -202,8 +253,7 @@ func renderPDFSingle(data []typstData) (bytes.Buffer, error) {
|
||||
var output bytes.Buffer
|
||||
|
||||
typstCLI := typst.CLI{
|
||||
WorkingDirectory: "/doc/",
|
||||
// ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
||||
WorkingDirectory: PDF_DIRECTORY,
|
||||
}
|
||||
|
||||
if err := typst.InjectValues(&markup, map[string]any{"data": data}); err != nil {
|
||||
@@ -230,8 +280,7 @@ func renderPDFMulti(data []typstData) ([]bytes.Buffer, error) {
|
||||
var outputMulti []bytes.Buffer
|
||||
|
||||
typstRender := typst.CLI{
|
||||
WorkingDirectory: "/doc/",
|
||||
// ContainerName: helper.GetEnv("TYPST_CONTAINER", "arbeitszeitmessung-doc-creator"),
|
||||
WorkingDirectory: PDF_DIRECTORY,
|
||||
}
|
||||
|
||||
for _, d := range data {
|
||||
@@ -273,6 +322,16 @@ func zipPfd(pdfReports []bytes.Buffer, reportData *[]typstData) (bytes.Buffer, e
|
||||
return zipOutput, err
|
||||
}
|
||||
|
||||
func lenWorkDays(workDays []models.IWorkDay) int {
|
||||
var lenght int
|
||||
for _, day := range workDays {
|
||||
if !day.IsEmpty() || day.IsKurzArbeit() {
|
||||
lenght += 1
|
||||
}
|
||||
}
|
||||
return lenght
|
||||
}
|
||||
|
||||
type typstMetadata struct {
|
||||
TimeRange string `json:"time-range"`
|
||||
EmployeeName string `json:"employee-name"`
|
||||
|
||||
Reference in New Issue
Block a user