dev/actions feature: overtime #27

Merged
tom_trgr merged 53 commits from dev/actions into main 2025-09-04 00:58:25 +02:00
4 changed files with 64 additions and 33 deletions
Showing only changes of commit 492216b160 - Show all commits

View File

@@ -34,7 +34,7 @@ func submitReport(w http.ResponseWriter, r *http.Request) {
_weekTs := r.FormValue("week") _weekTs := r.FormValue("week")
weekTs, err := time.Parse(time.DateOnly, _weekTs) weekTs, err := time.Parse(time.DateOnly, _weekTs)
user, err := models.GetUserByPersonalNr(userPN) user, err := models.GetUserByPersonalNr(userPN)
workWeek := (*models.WorkWeek).GetWeek(nil, user, weekTs, false) workWeek := models.NewWorkWeek(user, weekTs, true)
if err != nil { if err != nil {
log.Println("Could not get user!") log.Println("Could not get user!")
@@ -70,7 +70,7 @@ func showWeeks(w http.ResponseWriter, r *http.Request) {
lastSub = helper.GetMonday(submissionDate) lastSub = helper.GetMonday(submissionDate)
} }
} }
userWeek := (*models.WorkWeek).GetWeek(nil, user, lastSub, true) userWeek := models.NewWorkWeek(user, lastSub, true)
var workWeeks []models.WorkWeek var workWeeks []models.WorkWeek
teamMembers, err := user.GetTeamMembers() teamMembers, err := user.GetTeamMembers()

View File

@@ -13,11 +13,12 @@ import (
) )
type User struct { type User struct {
CardUID string `json:"card_uid"` CardUID string `json:"card_uid"`
Name string `json:"name"` Name string `json:"name"`
Vorname string `json:"vorname"` Vorname string `json:"vorname"`
PersonalNummer int `json:"personal_nummer"` PersonalNummer int `json:"personal_nummer"`
ArbeitszeitPerTag float32 `json:"arbeitszeit"` ArbeitszeitPerTag float32 `json:"arbeitszeit_per_tag"`
ArbeitszeitPerWoche float32 `json:"arbeitszeit_per_woche"`
} }
func (u *User) GetUserFromSession(Session *scs.SessionManager, ctx context.Context) (User, error) { func (u *User) GetUserFromSession(Session *scs.SessionManager, ctx context.Context) (User, error) {
@@ -98,11 +99,11 @@ func (u *User) CheckOut() error {
func GetUserByPersonalNr(personalNummer int) (User, error) { func GetUserByPersonalNr(personalNummer int) (User, error) {
var user User var user User
qStr, err := DB.Prepare((`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag FROM s_personal_daten WHERE personal_nummer = $1;`)) qStr, err := DB.Prepare((`SELECT personal_nummer, card_uid, vorname, nachname, arbeitszeit_per_tag, arbeitszeit_per_woche FROM s_personal_daten WHERE personal_nummer = $1;`))
if err != nil { if err != nil {
return user, err return user, err
} }
err = qStr.QueryRow(personalNummer).Scan(&user.PersonalNummer, &user.CardUID, &user.Vorname, &user.Name, &user.ArbeitszeitPerTag) err = qStr.QueryRow(personalNummer).Scan(&user.PersonalNummer, &user.CardUID, &user.Vorname, &user.Name, &user.ArbeitszeitPerTag, &user.ArbeitszeitPerWoche)
if err != nil { if err != nil {
return user, err return user, err
@@ -223,10 +224,8 @@ func (u *User) GetLastSubmission() time.Time {
log.Println("Error executing query!", err) log.Println("Error executing query!", err)
return lastSub return lastSub
} }
log.Println("From DB: ", lastSub)
lastSub = getMonday(lastSub) lastSub = getMonday(lastSub)
lastSub = lastSub.Round(24 * time.Hour) lastSub = lastSub.Round(24 * time.Hour)
log.Println("After truncate: ", lastSub)
return lastSub return lastSub
} }

View File

@@ -201,6 +201,11 @@ func (d *WorkDay) GetWorkDayProgress(user User) uint8 {
} }
func (d *WorkDay) CalcOvertime(user User) time.Duration { func (d *WorkDay) CalcOvertime(user User) time.Duration {
overtime := d.workTime - time.Duration(user.ArbeitszeitPerTag*float32(time.Hour)).Round(time.Minute) var overtime time.Duration
// weekday is WE
overtime = d.workTime - time.Duration(user.ArbeitszeitPerTag*float32(time.Hour)).Round(time.Minute)
if (d.Day.Weekday() == 6 || d.Day.Weekday() == 0) && len(d.Bookings) == 0 {
overtime = 0
}
return overtime return overtime
} }

View File

@@ -8,12 +8,16 @@ import (
"time" "time"
) )
// Workweeks are
type WorkWeek struct { type WorkWeek struct {
Id int Id int
WorkDays []WorkDay WorkDays []WorkDay
Absences []Absence
User User User User
WeekStart time.Time WeekStart time.Time
WorkHours time.Duration WorkHours time.Duration
Status WeekStatus
} }
type WeekStatus int8 type WeekStatus int8
@@ -24,51 +28,68 @@ const (
WeekStatusAccepted WeekStatusAccepted
) )
func (w *WorkWeek) GetWeek(user User, tsMonday time.Time, populateDays bool) WorkWeek { func NewWorkWeek(user User, tsMonday time.Time, populate bool) WorkWeek {
var week WorkWeek var week WorkWeek = WorkWeek{
if populateDays { User: user,
week.WorkDays = (*WorkDay).GetWorkDays(nil, user.CardUID, tsMonday, tsMonday.Add(7*24*time.Hour)) WeekStart: tsMonday,
week.WorkHours = aggregateWorkTime(week.WorkDays) Status: WeekStatusNone,
}
if populate {
week.WorkDays = (*WorkDay).GetWorkDays(nil, user.CardUID, tsMonday, tsMonday.Add(7*24*time.Hour))
if absences, err := GetAbsenceByCardUID(user.CardUID, tsMonday, tsMonday.Add(7*24*time.Hour)); err == nil {
week.Absences = absences
} else {
log.Printf("Error populating absences in workWeek (%s): %v", tsMonday, err)
}
week.WorkHours = week.aggregateWorkTime()
} }
week.User = user
week.WeekStart = tsMonday
return week return week
} }
func (w *WorkWeek) CheckStatus() WeekStatus { func (w *WorkWeek) CheckStatus() WeekStatus {
weekStatus := WeekStatusNone if w.Status != WeekStatusNone {
return w.Status
}
if DB == nil {
log.Println("Cannot access Database!")
return w.Status
}
qStr, err := DB.Prepare(`SELECT bestaetigt FROM wochen_report WHERE woche_start = $1::DATE AND personal_nummer = $2;`) qStr, err := DB.Prepare(`SELECT bestaetigt FROM wochen_report WHERE woche_start = $1::DATE AND personal_nummer = $2;`)
if err != nil { if err != nil {
log.Println("Error preparing SQL statement", err) log.Println("Error preparing SQL statement", err)
return weekStatus return w.Status
} }
defer qStr.Close() defer qStr.Close()
var beastatigt bool var beastatigt bool
err = qStr.QueryRow(w.WeekStart, w.User.PersonalNummer).Scan(&beastatigt) err = qStr.QueryRow(w.WeekStart, w.User.PersonalNummer).Scan(&beastatigt)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return weekStatus return w.Status
} }
if err != nil { if err != nil {
log.Println("Error querying database", err) log.Println("Error querying database", err)
return weekStatus return w.Status
} }
if beastatigt { if beastatigt {
weekStatus = WeekStatusAccepted w.Status = WeekStatusAccepted
} else { } else {
weekStatus = WeekStatusSent w.Status = WeekStatusSent
} }
return weekStatus return w.Status
} }
func (w *WorkWeek) GetWorkHourString() string { func (w *WorkWeek) GetWorkHourString() string {
return helper.FormatDuration(w.WorkHours) return helper.FormatDuration(w.WorkHours)
} }
func aggregateWorkTime(days []WorkDay) time.Duration { func (w *WorkWeek) aggregateWorkTime() time.Duration {
var workTime time.Duration var workTime time.Duration
for _, day := range days { for _, day := range w.WorkDays {
workTime += day.workTime workTime += day.workTime
} }
for _, absences := range w.Absences {
absenceWorkTime := absences.AbwesenheitTyp.WorkTime - (absences.AbwesenheitTyp.WorkTime - w.User.ArbeitszeitPerTag) // workTime Equivalent of Absence is capped at user Worktime per Day
workTime += time.Duration(absenceWorkTime * float32(time.Hour)).Round(time.Minute)
}
return workTime return workTime
} }
@@ -95,7 +116,7 @@ func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
return weeks return weeks
} }
week.WorkDays = (*WorkDay).GetWorkDays(nil, user.CardUID, week.WeekStart, week.WeekStart.Add(7*24*time.Hour)) week.WorkDays = (*WorkDay).GetWorkDays(nil, user.CardUID, week.WeekStart, week.WeekStart.Add(7*24*time.Hour))
week.WorkHours = aggregateWorkTime(week.WorkDays) week.WorkHours = week.aggregateWorkTime()
weeks = append(weeks, week) weeks = append(weeks, week)
} }
if err = rows.Err(); err != nil { if err = rows.Err(); err != nil {
@@ -107,34 +128,40 @@ func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
var ErrRunningWeek = errors.New("Week is in running week") var ErrRunningWeek = errors.New("Week is in running week")
func (w *WorkWeek) GetOvertime() time.Duration {
var weekOvertime time.Duration = w.WorkHours - time.Duration(w.User.ArbeitszeitPerWoche*float32(time.Hour)).Round(time.Minute)
return weekOvertime
}
// creates a new entry in the woche_report table with the given workweek // creates a new entry in the woche_report table with the given workweek
func (w *WorkWeek) Send() error { func (w *WorkWeek) Send() error {
var qStr *sql.Stmt var qStr *sql.Stmt
var err error var err error
if time.Since(w.WeekStart) < 5*24*time.Hour { if time.Since(w.WeekStart) < 5*24*time.Hour {
log.Println("Cannot send week, because it's the running week!") log.Println("Cannot send week, because it's the running week!")
return ErrRunningWeek return ErrRunningWeek
} }
if w.CheckStatus() != WeekStatusNone { if w.CheckStatus() != WeekStatusNone {
qStr, err = DB.Prepare(`UPDATE "wochen_report" SET bestaetigt = FALSE WHERE personal_nummer = $1 AND woche_start = $2;`) qStr, err = DB.Prepare(`UPDATE "wochen_report" SET bestaetigt = FALSE, ueberstunden = $3 WHERE personal_nummer = $1 AND woche_start = $2;`)
if err != nil { if err != nil {
log.Println("Error preparing SQL statement", err) log.Println("Error preparing SQL statement", err)
return err return err
} }
} else { } else {
qStr, err = DB.Prepare(`INSERT INTO wochen_report (personal_nummer, woche_start) VALUES ($1, $2);`) qStr, err = DB.Prepare(`INSERT INTO wochen_report (personal_nummer, woche_start, ueberstunden) VALUES ($1, $2, $3);`)
if err != nil { if err != nil {
log.Println("Error preparing SQL statement", err) log.Println("Error preparing SQL statement", err)
return err return err
} }
} }
_, err = qStr.Exec(w.User.PersonalNummer, w.WeekStart) _, err = qStr.Exec(w.User.PersonalNummer, w.WeekStart, w.GetOvertime().Hours())
if err != nil { if err != nil {
log.Println("Error executing query!", err) log.Println("Error executing query!", err)
return err return err
} }
return nil return nil
} }
func (w *WorkWeek) Accept() error { func (w *WorkWeek) Accept() error {