182 lines
5.6 KiB
Go
182 lines
5.6 KiB
Go
package endpoints
|
|
|
|
import (
|
|
"arbeitszeitmessung/helper"
|
|
"arbeitszeitmessung/models"
|
|
"arbeitszeitmessung/templates"
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"log/slog"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/Dadido3/go-typst"
|
|
)
|
|
|
|
type typstMetadata struct {
|
|
TimeRange string `json:"time-range"`
|
|
EmployeeName string `json:"employee-name"`
|
|
WorkTime string `json:"worktime"`
|
|
Overtime string `json:"overtime"`
|
|
OvertimeTotal string `json:"overtime-total"`
|
|
CurrentTimestamp string `json:"current-timestamp"`
|
|
}
|
|
|
|
type typstDayPart struct {
|
|
BookingFrom string `json:"booking-from"`
|
|
BookingTo string `json:"booking-to"`
|
|
WorkType string `json:"worktype"`
|
|
IsWorkDay bool `json:"is-workday"`
|
|
}
|
|
|
|
type typstDay struct {
|
|
Date string `json:"date"`
|
|
DayParts []typstDayPart `json:"day-parts"`
|
|
Worktime string `json:"worktime"`
|
|
Pausetime string `json:"pausetime"`
|
|
Overtime string `json:"overtime"`
|
|
IsFriday bool `json:"is-weekend"`
|
|
}
|
|
|
|
func convertDaysToTypst(days []models.IWorkDay, u models.User) ([]typstDay, error) {
|
|
var typstDays []typstDay
|
|
for _, day := range days {
|
|
var thisTypstDay typstDay
|
|
work, pause, overtime := day.GetAllWorkTimesVirtual(u)
|
|
thisTypstDay.Date = day.Date().Format("02.01.2006")
|
|
thisTypstDay.Worktime = helper.FormatDurationFill(work, true)
|
|
thisTypstDay.Pausetime = helper.FormatDurationFill(pause, true)
|
|
thisTypstDay.Overtime = helper.FormatDurationFill(overtime, true)
|
|
thisTypstDay.IsFriday = day.Date().Weekday() == time.Friday
|
|
|
|
thisTypstDay.DayParts = convertDayToTypstDayParts(day, u)
|
|
typstDays = append(typstDays, thisTypstDay)
|
|
}
|
|
return typstDays, nil
|
|
}
|
|
|
|
func convertDayToTypstDayParts(day models.IWorkDay, user models.User) []typstDayPart {
|
|
var typstDayParts []typstDayPart
|
|
if day.IsWorkDay() {
|
|
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")
|
|
typstDayPart.BookingTo = workDay.Bookings[i+1].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)
|
|
typstDayParts = append(typstDayParts, typstDayPart{
|
|
BookingFrom: tsFrom.Format("15:04"),
|
|
BookingTo: tsTo.Format("15:04"),
|
|
WorkType: "Kurzarbeit",
|
|
IsWorkDay: true,
|
|
})
|
|
}
|
|
} else {
|
|
absentDay, _ := day.(*models.Absence)
|
|
typstDayParts = append(typstDayParts, typstDayPart{IsWorkDay: false, WorkType: absentDay.AbwesenheitTyp.Name})
|
|
}
|
|
return typstDayParts
|
|
}
|
|
|
|
func renderPDF(days []typstDay, metadata typstMetadata) (bytes.Buffer, error) {
|
|
var markup bytes.Buffer
|
|
var output bytes.Buffer
|
|
|
|
if err := typst.InjectValues(&markup, map[string]any{"meta": metadata, "days": days}); err != nil {
|
|
return output, err
|
|
}
|
|
|
|
// Import the template and invoke the template function with the custom data.
|
|
// Show is used to replace the current document with whatever content the template function in `template.typ` returns.
|
|
markup.WriteString(`
|
|
#import "template.typ": abrechnung
|
|
#show: doc => abrechnung(meta, days)
|
|
`)
|
|
|
|
// Compile the prepared markup with Typst and write the result it into `output.pdf`.
|
|
// f, err := os.Create("output.pdf")
|
|
// if err != nil {
|
|
// log.Panicf("Failed to create output file: %v.", err)
|
|
// }
|
|
// defer f.Close()
|
|
//
|
|
|
|
typstCLI := typst.CLI{}
|
|
if err := typstCLI.Compile(&markup, &output, nil); err != nil {
|
|
return output, err
|
|
}
|
|
return output, nil
|
|
}
|
|
|
|
func PDFFormHandler(w http.ResponseWriter, r *http.Request) {
|
|
helper.RequiresLogin(Session, w, r)
|
|
|
|
user, err := models.GetUserFromSession(Session, r.Context())
|
|
if err != nil {
|
|
slog.Warn("Error getting user!", slog.Any("Error", err))
|
|
// TODO add error handling
|
|
}
|
|
|
|
teamMembers, err := user.GetTeamMembers()
|
|
if err != nil {
|
|
slog.Warn("Error getting team members!", slog.Any("Error", err))
|
|
}
|
|
templates.PDFForm(teamMembers).Render(r.Context(), w)
|
|
}
|
|
|
|
func PDFHandler(w http.ResponseWriter, r *http.Request) {
|
|
helper.RequiresLogin(Session, w, r)
|
|
startDate, err := parseTimestamp(r, "start_date", time.Now().Format("2006-01-02"))
|
|
if err != nil {
|
|
log.Println("Error parsing 'start_date' time", err)
|
|
http.Error(w, "Timestamp 'start_date' cannot be parsed!", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if startDate.Day() > 1 {
|
|
startDate = startDate.AddDate(0, 0, -(startDate.Day() - 1))
|
|
}
|
|
endDate := startDate.AddDate(0, 1, -1)
|
|
|
|
user, err := models.GetUserFromSession(Session, r.Context())
|
|
if err != nil {
|
|
log.Println("Error getting user!")
|
|
}
|
|
|
|
//TODO: only accepted weeks
|
|
|
|
weeks := models.GetDays(user, startDate, endDate, false)
|
|
var aggregatedOvertime, aggregatedWorkTime time.Duration
|
|
for _, day := range weeks {
|
|
aggregatedOvertime += day.TimeOvertimeReal(user)
|
|
aggregatedWorkTime += day.TimeWorkVirtual(user)
|
|
}
|
|
|
|
typstDays, err := convertDaysToTypst(weeks, user)
|
|
if err != nil {
|
|
log.Panicf("Failed to convert days!")
|
|
}
|
|
metadata := typstMetadata{
|
|
EmployeeName: fmt.Sprintf("%s %s", user.Vorname, user.Name),
|
|
TimeRange: fmt.Sprintf("%s - %s", startDate.Format("02.01.2006"), endDate.Format("02.01.2006")),
|
|
Overtime: helper.FormatDurationFill(aggregatedOvertime, true),
|
|
WorkTime: helper.FormatDurationFill(aggregatedWorkTime, true),
|
|
OvertimeTotal: "",
|
|
CurrentTimestamp: time.Now().Format("02.01.2006 - 15:04 Uhr"),
|
|
}
|
|
|
|
output, err := renderPDF(typstDays, metadata)
|
|
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)
|
|
}
|