seperated pdf-generate endpoint + added new helper in Time
This commit is contained in:
@@ -89,10 +89,71 @@ func renderPDF(days []typstDay, metadata typstMetadata) (bytes.Buffer, error) {
|
|||||||
return output, nil
|
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) {
|
func PDFHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
helper.RequiresLogin(Session, w, r)
|
helper.RequiresLogin(Session, w, r)
|
||||||
pp := paramParser.New(r.URL.Query())
|
startDate := time.Now()
|
||||||
startDate := pp.ParseTimestamp("start_date", time.DateOnly, time.Now())
|
|
||||||
if startDate.Day() > 1 {
|
if startDate.Day() > 1 {
|
||||||
startDate = startDate.AddDate(0, 0, -(startDate.Day() - 1))
|
startDate = startDate.AddDate(0, 0, -(startDate.Day() - 1))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,3 +55,16 @@ func FormatDurationFill(d time.Duration, fill bool) string {
|
|||||||
func IsSameDate(a, b time.Time) bool {
|
func IsSameDate(a, b time.Time) bool {
|
||||||
return a.Truncate(24 * time.Hour).Equal(b.Truncate(24 * time.Hour))
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package helper
|
package helper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func main() {
|
|||||||
server.HandleFunc("/team", endpoints.TeamHandler)
|
server.HandleFunc("/team", endpoints.TeamHandler)
|
||||||
server.HandleFunc("/presence", endpoints.TeamPresenceHandler)
|
server.HandleFunc("/presence", endpoints.TeamPresenceHandler)
|
||||||
server.Handle("/pdf", ParamsMiddleware(endpoints.PDFFormHandler))
|
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("/", http.RedirectHandler("/time", http.StatusPermanentRedirect))
|
||||||
server.Handle("/static/", http.StripPrefix("/static/", fs))
|
server.Handle("/static/", http.StripPrefix("/static/", fs))
|
||||||
|
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ templ PDFForm(teamMembers []models.User) {
|
|||||||
<div></div>
|
<div></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-sub divide-x-1 responsive">
|
<div class="grid-sub divide-x-1 responsive">
|
||||||
<div class="grid-cell">Direktvorschau oder Download</div>
|
<div class="grid-cell">PDFs Bündeln</div>
|
||||||
<div class="grid-cell col-span-3 flex gap-2 flex-col md:flex-row">
|
<div class="grid-cell col-span-3 flex gap-2 flex-col md:flex-row">
|
||||||
<button class="btn" type="button" name="action" value="download">Download</button>
|
<button class="btn" type="button" name="action" value="download">Einzeln</button>
|
||||||
<button class="btn" type="button" name="action" value="preview" onclick="">Vorschau</button>
|
<button class="btn" type="button" name="action" value="preview" onclick="">Bündel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func PDFForm(teamMembers []models.User) templ.Component {
|
|||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div><div></div></div><div class=\"grid-sub divide-x-1 responsive\"><div class=\"grid-cell\">Direktvorschau oder Download</div><div class=\"grid-cell col-span-3 flex gap-2 flex-col md:flex-row\"><button class=\"btn\" type=\"button\" name=\"action\" value=\"download\">Download</button> <button class=\"btn\" type=\"button\" name=\"action\" value=\"preview\" onclick=\"\">Vorschau</button></div></div></div>")
|
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "</div><div></div></div><div class=\"grid-sub divide-x-1 responsive\"><div class=\"grid-cell\">PDFs Bündeln</div><div class=\"grid-cell col-span-3 flex gap-2 flex-col md:flex-row\"><button class=\"btn\" type=\"button\" name=\"action\" value=\"download\">Einzeln</button> <button class=\"btn\" type=\"button\" name=\"action\" value=\"preview\" onclick=\"\">Bündel</button></div></div></div>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user