diff --git a/Backend/endpoints/pdf-create.go b/Backend/endpoints/pdf-create.go index 5184c70..335672d 100644 --- a/Backend/endpoints/pdf-create.go +++ b/Backend/endpoints/pdf-create.go @@ -89,10 +89,71 @@ func renderPDF(days []typstDay, metadata typstMetadata) (bytes.Buffer, error) { return output, nil } +func PDFCreateController(w http.ResponseWriter, r *http.Request) { + helper.RequiresLogin(Session, w, r) + switch r.Method { + case http.MethodGet: + user, err := models.GetUserFromSession(Session, r.Context()) + if err != nil { + log.Println("Error getting user!") + return + } + pp := paramParser.New(r.URL.Query()) + startDate := pp.ParseTimestampFallback("start_date", time.DateOnly, time.Now()) + var members []models.User = make([]models.User, 0) + output, err := createReports(user, members, startDate) + if err != nil { + slog.Warn("Could not create pdf report", slog.Any("Error", err)) + } + + w.Header().Set("Content-type", "application/pdf") + output.WriteTo(w) + w.WriteHeader(200) + + default: + http.Error(w, "Method not allowed!", http.StatusMethodNotAllowed) + } +} + +func createReports(user models.User, employes []models.User, startDate time.Time) (bytes.Buffer, error) { + if startDate.Day() > 1 { + startDate = startDate.AddDate(0, 0, -(startDate.Day() - 1)) + } + endDate := startDate.AddDate(0, 1, -1) + return createEmployeReport(user, startDate, endDate) +} + +func createEmployeReport(employee models.User, startDate, endDate time.Time) (bytes.Buffer, error) { + targetHours := (employee.ArbeitszeitProWoche() / 5) * time.Duration(helper.GetWorkingDays(startDate, endDate)) + workingDays := models.GetDays(employee, startDate, endDate, false) + + var actualHours time.Duration + for _, day := range workingDays { + actualHours += day.TimeWorkVirtual(employee) + } + worktimeBalance := actualHours - targetHours + + typstDays, err := convertDaysToTypst(workingDays, employee) + if err != nil { + log.Panicf("Failed to convert days!") + } + + metadata := typstMetadata{ + EmployeeName: fmt.Sprintf("%s %s", employee.Vorname, employee.Name), + TimeRange: fmt.Sprintf("%s - %s", startDate.Format("02.01.2006"), endDate.Format("02.01.2006")), + Overtime: helper.FormatDurationFill(worktimeBalance, true), + WorkTime: helper.FormatDurationFill(actualHours, true), + OvertimeTotal: "", + CurrentTimestamp: time.Now().Format("02.01.2006 - 15:04 Uhr"), + } + + return renderPDF(typstDays, metadata) +} + func PDFHandler(w http.ResponseWriter, r *http.Request) { helper.RequiresLogin(Session, w, r) - pp := paramParser.New(r.URL.Query()) - startDate := pp.ParseTimestamp("start_date", time.DateOnly, time.Now()) + startDate := time.Now() + if startDate.Day() > 1 { startDate = startDate.AddDate(0, 0, -(startDate.Day() - 1)) } diff --git a/Backend/helper/time.go b/Backend/helper/time.go index 046ad64..30198fc 100644 --- a/Backend/helper/time.go +++ b/Backend/helper/time.go @@ -55,3 +55,16 @@ func FormatDurationFill(d time.Duration, fill bool) string { func IsSameDate(a, b time.Time) bool { return a.Truncate(24 * time.Hour).Equal(b.Truncate(24 * time.Hour)) } + +func GetWorkingDays(startDate, endDate time.Time) int { + if endDate.Before(startDate) { + return 0 + } + var count int = 0 + for d := startDate.Truncate(24 * time.Hour); !d.After(endDate); d = d.Add(24 * time.Hour) { + if !IsWeekend(d) { + count++ + } + } + return count +} diff --git a/Backend/helper/time_test.go b/Backend/helper/time_test.go index e09cbd3..8e56834 100644 --- a/Backend/helper/time_test.go +++ b/Backend/helper/time_test.go @@ -1,6 +1,7 @@ package helper import ( + "fmt" "testing" "time" ) @@ -35,3 +36,25 @@ func TestFormatDuration(t *testing.T) { }) } } + +func TestGetWorkingDays(t *testing.T) { + testCases := []struct { + start string + end string + days int + }{ + {"2025-10-01", "2025-10-02", 2}, + {"2025-10-02", "2025-10-01", 0}, + {"2025-10-01", "2025-10-31", 23}, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("WorkingDayTest: %d days", tc.days), func(t *testing.T) { + startDate, _ := time.Parse(time.DateOnly, tc.start) + endDate, _ := time.Parse(time.DateOnly, tc.end) + if GetWorkingDays(startDate, endDate) != tc.days { + t.Error("Calculated workdays do not match target") + } + }) + } +} diff --git a/Backend/main.go b/Backend/main.go index 98fde52..dbb6d87 100644 --- a/Backend/main.go +++ b/Backend/main.go @@ -53,7 +53,7 @@ func main() { server.HandleFunc("/team", endpoints.TeamHandler) server.HandleFunc("/presence", endpoints.TeamPresenceHandler) server.Handle("/pdf", ParamsMiddleware(endpoints.PDFFormHandler)) - server.HandleFunc("/pdf/generate", endpoints.PDFHandler) + server.HandleFunc("/pdf/generate", endpoints.PDFCreateController) server.Handle("/", http.RedirectHandler("/time", http.StatusPermanentRedirect)) server.Handle("/static/", http.StripPrefix("/static/", fs)) diff --git a/Backend/templates/pdf.templ b/Backend/templates/pdf.templ index 3b4220a..c00d786 100644 --- a/Backend/templates/pdf.templ +++ b/Backend/templates/pdf.templ @@ -36,10 +36,10 @@ templ PDFForm(teamMembers []models.User) {
-
Direktvorschau oder Download
+
PDFs Bündeln
- - + +
diff --git a/Backend/templates/pdf_templ.go b/Backend/templates/pdf_templ.go index 3af3360..a15eca1 100644 --- a/Backend/templates/pdf_templ.go +++ b/Backend/templates/pdf_templ.go @@ -88,7 +88,7 @@ func PDFForm(teamMembers []models.User) templ.Component { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
Direktvorschau oder Download
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
PDFs Bündeln
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }