reworked time Calculations

This commit is contained in:
2025-09-13 14:11:26 +02:00
parent 3d76778d4f
commit ccded6d76b
3 changed files with 178 additions and 37 deletions

View File

@@ -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,
}}

View File

@@ -19,7 +19,7 @@ type WorkDay struct {
Absence Absence 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 workDays []WorkDay
var workSec, pauseSec float64 var workSec, pauseSec float64
@@ -97,7 +97,7 @@ ORDER BY d.work_date ASC;`)
} }
defer qStr.Close() defer qStr.Close()
rows, err := qStr.Query(card_uid, tsFrom, tsTo) rows, err := qStr.Query(user.CardUID, tsFrom, tsTo)
if err != nil { if err != nil {
log.Println("Error getting rows!") log.Println("Error getting rows!")
return workDays return workDays
@@ -125,21 +125,20 @@ ORDER BY d.work_date ASC;`)
} }
if absenceType.Valid { if absenceType.Valid {
workDay.Absence, err = NewAbsence(card_uid, int(absenceType.Int16), workDay.Day) workDay.Absence, err = NewAbsence(user.CardUID, int(absenceType.Int16), workDay.Day)
// log.Println("Found absence", workDay.Absence) workDay.CalcRealWorkTime(user)
} }
if workDay.Day.Equal(time.Now().Truncate(24 * time.Hour)) { if workDay.Day.Equal(time.Now().Truncate(24 * time.Hour)) {
workDay.getWorkTime() workDay.CalcRealWorkTime(user)
workDay.CalcWorkPauseDiff(user)
} else { } else {
workDay.calcPauseTime() workDay.CalcWorkPauseDiff(user)
} }
if emptyDays && workDay.Day.Weekday() >= 1 && workDay.Day.Weekday() <= 5 { if emptyDays && workDay.Day.Weekday() >= 1 && workDay.Day.Weekday() <= 5 {
workDays = append(workDays, workDay) workDays = append(workDays, workDay)
} else if len(workDay.Bookings) > 0 || (workDay.Absence != Absence{}) { } else if len(workDay.Bookings) > 0 || (workDay.Absence != Absence{}) {
workDays = append(workDays, workDay) workDays = append(workDays, workDay)
// } else {
// log.Println("no booking on day", workDay.Day.Format("02.01.2006"))
} }
} }
if err = rows.Err(); err != nil { if err = rows.Err(); err != nil {
@@ -148,45 +147,56 @@ ORDER BY d.work_date ASC;`)
return workDays return workDays
} }
func (d *WorkDay) calcPauseTime() { func (d *WorkDay) CalcWorkPauseDiff(user User) (work, pause time.Duration) {
if d.workTime > 6*time.Hour && d.pauseTime < 45*time.Minute { if d.workTime == 0 {
if d.workTime <= (9*time.Hour) && d.pauseTime < 30*time.Minute { d.CalcRealWorkTime(user)
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
}
} }
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) CalcRealWorkTime(user User) time.Duration {
func (d *WorkDay) getWorkTime() { if (len(d.Bookings) < 1 && d.Absence == Absence{}) {
if len(d.Bookings) < 1 { return 0
return
} }
var workTime, pauseTime time.Duration var realWorkTime, realPauseTime time.Duration
var lastBooking Booking var lastBooking Booking
for _, booking := range d.Bookings { for _, booking := range d.Bookings {
if booking.CheckInOut%2 == 1 { if booking.CheckInOut%2 == 1 {
if !lastBooking.Timestamp.IsZero() { if !lastBooking.Timestamp.IsZero() {
pauseTime += booking.Timestamp.Sub(lastBooking.Timestamp) realPauseTime += booking.Timestamp.Sub(lastBooking.Timestamp)
} }
} else { } else {
workTime += booking.Timestamp.Sub(lastBooking.Timestamp) realWorkTime += booking.Timestamp.Sub(lastBooking.Timestamp)
} }
lastBooking = booking lastBooking = booking
} }
// checks if booking is today and has no gehen yet, so the time since last kommen booking is added to workTime if helper.IsSameDate(d.Day, time.Now()) && len(d.Bookings)%2 == 1 {
if d.Day.Day() == time.Now().Day() && len(d.Bookings)%2 == 1 { realWorkTime += time.Since(lastBooking.Timestamp.Local())
workTime += time.Since(lastBooking.Timestamp.Local())
} }
d.workTime = workTime if d.Absence.AbwesenheitTyp.WorkTime > 0 {
d.pauseTime = pauseTime 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) { func (d *WorkDay) GetWorkTimeString() (work string, pause string) {
@@ -195,6 +205,10 @@ func (d *WorkDay) GetWorkTimeString() (work string, pause string) {
return workString, pauseString 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 // returns bool wheter the workday was ended with an automatic logout
func (d *WorkDay) RequiresAction() bool { func (d *WorkDay) RequiresAction() bool {
if len(d.Bookings) == 0 { if len(d.Bookings) == 0 {
@@ -211,15 +225,13 @@ func (d *WorkDay) GetWorkDayProgress(user User) uint8 {
} }
func (d *WorkDay) CalcOvertime(user User) time.Duration { func (d *WorkDay) CalcOvertime(user User) time.Duration {
if d.workTime == 0 {
d.CalcWorkPauseDiff(user)
}
if helper.IsWeekend(d.Day) && len(d.Bookings) == 0 { if helper.IsWeekend(d.Day) && len(d.Bookings) == 0 {
return 0 return 0
} }
var overtime time.Duration var overtime time.Duration
overtime = d.workTime - time.Duration(user.ArbeitszeitPerTag*float32(time.Hour)).Round(time.Minute) overtime = d.workTime - time.Duration(user.ArbeitszeitPerTag*float32(time.Hour)).Round(time.Minute)
// weekday is WE
if (d.Absence != Absence{}) {
overtime = 0
}
return overtime return overtime
} }

View File

@@ -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))
}
})
}
}