From ccded6d76bd0b0412775ed841e4846cbb2a62666 Mon Sep 17 00:00:00 2001 From: tom Date: Sat, 13 Sep 2025 14:11:26 +0200 Subject: [PATCH] reworked time Calculations --- Backend/models/booking_test.go | 47 +++++++++++++++++++ Backend/models/workDay.go | 86 +++++++++++++++++++--------------- Backend/models/workDay_test.go | 82 ++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 37 deletions(-) create mode 100644 Backend/models/booking_test.go create mode 100644 Backend/models/workDay_test.go diff --git a/Backend/models/booking_test.go b/Backend/models/booking_test.go new file mode 100644 index 0000000..032c59e --- /dev/null +++ b/Backend/models/booking_test.go @@ -0,0 +1,47 @@ +package models_test + +import ( + "arbeitszeitmessung/models" + "time" +) + +var testBookingType = models.BookingType{ + Id: 1, + Name: "Büro", +} + +var testBookings8hrs = []models.Booking{models.Booking{ + CardUID: "aaaa-aaaa", + CheckInOut: 1, + Timestamp: CatchError(time.Parse("2006-01-02 15:04", "2025-01-01 08:00")), + BookingType: testBookingType, +}, models.Booking{ + CardUID: "aaaa-aaaa", + CheckInOut: 2, + Timestamp: CatchError(time.Parse("2006-01-02 15:04", "2025-01-01 16:00")), + BookingType: testBookingType, +}} + +var testBookings6hrs = []models.Booking{models.Booking{ + CardUID: "aaaa-aaaa", + CheckInOut: 1, + Timestamp: CatchError(time.Parse("2006-01-02 15:04", "2025-01-01 08:00")), + BookingType: testBookingType, +}, models.Booking{ + CardUID: "aaaa-aaaa", + CheckInOut: 2, + Timestamp: CatchError(time.Parse("2006-01-02 15:04", "2025-01-01 14:00")), + BookingType: testBookingType, +}} + +var testBookings10hrs = []models.Booking{models.Booking{ + CardUID: "aaaa-aaaa", + CheckInOut: 1, + Timestamp: CatchError(time.Parse("2006-01-02 15:04", "2025-01-01 08:00")), + BookingType: testBookingType, +}, models.Booking{ + CardUID: "aaaa-aaaa", + CheckInOut: 2, + Timestamp: CatchError(time.Parse("2006-01-02 15:04", "2025-01-01 18:00")), + BookingType: testBookingType, +}} diff --git a/Backend/models/workDay.go b/Backend/models/workDay.go index cf8df46..fda0704 100644 --- a/Backend/models/workDay.go +++ b/Backend/models/workDay.go @@ -19,7 +19,7 @@ type WorkDay struct { Absence Absence } -func GetWorkDays(card_uid string, tsFrom, tsTo time.Time) []WorkDay { +func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay { var workDays []WorkDay var workSec, pauseSec float64 @@ -97,7 +97,7 @@ ORDER BY d.work_date ASC;`) } defer qStr.Close() - rows, err := qStr.Query(card_uid, tsFrom, tsTo) + rows, err := qStr.Query(user.CardUID, tsFrom, tsTo) if err != nil { log.Println("Error getting rows!") return workDays @@ -125,21 +125,20 @@ ORDER BY d.work_date ASC;`) } if absenceType.Valid { - workDay.Absence, err = NewAbsence(card_uid, int(absenceType.Int16), workDay.Day) - // log.Println("Found absence", workDay.Absence) + workDay.Absence, err = NewAbsence(user.CardUID, int(absenceType.Int16), workDay.Day) + workDay.CalcRealWorkTime(user) } if workDay.Day.Equal(time.Now().Truncate(24 * time.Hour)) { - workDay.getWorkTime() + workDay.CalcRealWorkTime(user) + workDay.CalcWorkPauseDiff(user) } else { - workDay.calcPauseTime() + workDay.CalcWorkPauseDiff(user) } if emptyDays && workDay.Day.Weekday() >= 1 && workDay.Day.Weekday() <= 5 { workDays = append(workDays, workDay) } else if len(workDay.Bookings) > 0 || (workDay.Absence != Absence{}) { workDays = append(workDays, workDay) - // } else { - // log.Println("no booking on day", workDay.Day.Format("02.01.2006")) } } if err = rows.Err(); err != nil { @@ -148,45 +147,56 @@ ORDER BY d.work_date ASC;`) return workDays } -func (d *WorkDay) calcPauseTime() { - if d.workTime > 6*time.Hour && d.pauseTime < 45*time.Minute { - if d.workTime <= (9*time.Hour) && d.pauseTime < 30*time.Minute { - diff := 30*time.Minute - d.pauseTime - d.workTime -= diff - d.pauseTime += diff - } else if d.pauseTime < 45*time.Minute { - diff := 45*time.Minute - d.pauseTime - d.workTime -= diff - d.pauseTime += diff - } +func (d *WorkDay) CalcWorkPauseDiff(user User) (work, pause time.Duration) { + if d.workTime == 0 { + d.CalcRealWorkTime(user) } + if d.Absence.AbwesenheitTyp.WorkTime > 0 { + return d.workTime, d.pauseTime + } + if d.workTime <= 6*time.Hour || d.pauseTime > 45*time.Minute { + return d.workTime, d.pauseTime + } + if d.workTime <= (9*time.Hour) && d.pauseTime < 30*time.Minute { + diff := 30*time.Minute - d.pauseTime + d.workTime -= diff + d.pauseTime += diff + } else if d.pauseTime < 45*time.Minute { + diff := 45*time.Minute - d.pauseTime + d.workTime -= diff + d.pauseTime += diff + } + + return d.workTime, d.pauseTime } -// Gets the duration someone worked that day -func (d *WorkDay) getWorkTime() { - if len(d.Bookings) < 1 { - return +func (d *WorkDay) CalcRealWorkTime(user User) time.Duration { + if (len(d.Bookings) < 1 && d.Absence == Absence{}) { + return 0 } - var workTime, pauseTime time.Duration + var realWorkTime, realPauseTime time.Duration var lastBooking Booking for _, booking := range d.Bookings { if booking.CheckInOut%2 == 1 { if !lastBooking.Timestamp.IsZero() { - pauseTime += booking.Timestamp.Sub(lastBooking.Timestamp) + realPauseTime += booking.Timestamp.Sub(lastBooking.Timestamp) } } else { - workTime += booking.Timestamp.Sub(lastBooking.Timestamp) + realWorkTime += booking.Timestamp.Sub(lastBooking.Timestamp) } lastBooking = booking } - // checks if booking is today and has no gehen yet, so the time since last kommen booking is added to workTime - if d.Day.Day() == time.Now().Day() && len(d.Bookings)%2 == 1 { - workTime += time.Since(lastBooking.Timestamp.Local()) + if helper.IsSameDate(d.Day, time.Now()) && len(d.Bookings)%2 == 1 { + realWorkTime += time.Since(lastBooking.Timestamp.Local()) } - d.workTime = workTime - d.pauseTime = pauseTime + if d.Absence.AbwesenheitTyp.WorkTime > 0 { + realWorkTime = time.Duration(user.ArbeitszeitPerTag * float32(time.Hour)).Round(time.Minute) + log.Println("Rewriting worktime", realWorkTime) + } + d.workTime = realWorkTime + d.pauseTime = realPauseTime - d.calcPauseTime() + return realWorkTime } func (d *WorkDay) GetWorkTimeString() (work string, pause string) { @@ -195,6 +205,10 @@ func (d *WorkDay) GetWorkTimeString() (work string, pause string) { return workString, pauseString } +func (d *WorkDay) GetAllWorkTimes(user User) (work, pause, overtime time.Duration) { + return d.workTime.Round(time.Minute), d.pauseTime.Round(time.Minute), d.CalcOvertime(user) +} + // returns bool wheter the workday was ended with an automatic logout func (d *WorkDay) RequiresAction() bool { if len(d.Bookings) == 0 { @@ -211,15 +225,13 @@ func (d *WorkDay) GetWorkDayProgress(user User) uint8 { } func (d *WorkDay) CalcOvertime(user User) time.Duration { + if d.workTime == 0 { + d.CalcWorkPauseDiff(user) + } if helper.IsWeekend(d.Day) && len(d.Bookings) == 0 { return 0 } var overtime time.Duration overtime = d.workTime - time.Duration(user.ArbeitszeitPerTag*float32(time.Hour)).Round(time.Minute) - // weekday is WE - if (d.Absence != Absence{}) { - overtime = 0 - } - return overtime } diff --git a/Backend/models/workDay_test.go b/Backend/models/workDay_test.go new file mode 100644 index 0000000..5b26ea7 --- /dev/null +++ b/Backend/models/workDay_test.go @@ -0,0 +1,82 @@ +package models_test + +import ( + "arbeitszeitmessung/helper" + "arbeitszeitmessung/models" + "log" + "testing" + "time" +) + +func CatchError[T any](val T, err error) T { + if err != nil { + log.Fatalln(err) + } + return val +} + +var testWorkDay = models.WorkDay{ + Day: CatchError(time.Parse("2006-01-02", "2025-01-01")), + Bookings: testBookings8hrs, + TimeFrom: CatchError(time.Parse("2006-01-02 15:04", "2025-01-01 08:00")), + TimeTo: CatchError(time.Parse("2006-01-02 15:04", "2025-01-01 16:30")), + Absence: models.Absence{}, +} + +func TestCalcRealWorkTime(t *testing.T) { + workTime := testWorkDay.CalcRealWorkTime(testUser) + if workTime != time.Hour*8 { + t.Errorf("Calc Worktime Default not working, time should be 8h, but was %s", helper.FormatDuration(workTime)) + } +} + +func TestCalcWorkPauseDiff(t *testing.T) { + type testCase struct { + Name string + bookings []models.Booking + expectedWorkTime time.Duration + expectedPauseTime time.Duration + expectedOvertime time.Duration + } + + testCases := []testCase{testCase{ + Name: "6hrs no pause", + bookings: testBookings6hrs, + expectedWorkTime: 6 * time.Hour, + expectedPauseTime: 0, + expectedOvertime: -2 * time.Hour, + }, + testCase{ + Name: "8hrs - 30min pause", + bookings: testBookings8hrs, + expectedWorkTime: 7*time.Hour + 30*time.Minute, + expectedPauseTime: 30 * time.Minute, + expectedOvertime: -30 * time.Minute, + }, + testCase{ + Name: "10hrs - 45min pause", + bookings: testBookings10hrs, + expectedWorkTime: 9*time.Hour + 15*time.Minute, + expectedPauseTime: 45 * time.Minute, + expectedOvertime: 1*time.Hour + 15*time.Minute, + }} + + for _, test := range testCases { + t.Run(test.Name, func(t *testing.T) { + testWorkDay.Bookings = test.bookings + testWorkDay.CalcRealWorkTime(testUser) + testWorkDay.CalcWorkPauseDiff(testUser) + testWorkDay.CalcOvertime(testUser) + workTime, pauseTime, overTime := testWorkDay.GetAllWorkTimes(testUser) + if workTime != test.expectedWorkTime { + t.Errorf("Calculated wrong workTime: should be %s, but was %s", helper.FormatDuration(test.expectedWorkTime), helper.FormatDuration(workTime)) + } + if pauseTime != test.expectedPauseTime { + t.Errorf("Calculated wrong pauseTime: should be %s, but was %s", helper.FormatDuration(test.expectedPauseTime), helper.FormatDuration(pauseTime)) + } + if overTime != test.expectedOvertime { + t.Errorf("Calculated wrong overtime: should be %s, but was %s", helper.FormatDuration(test.expectedOvertime), helper.FormatDuration(overTime)) + } + }) + } +}