This commit is contained in:
@@ -1,22 +1,25 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AbsenceType struct {
|
||||
Id int8
|
||||
Name string
|
||||
WorkTime int8
|
||||
Id int8 `json:"abwesenheit_id"`
|
||||
Name string `json:"abwesenheit_name"`
|
||||
WorkTime int8 `json:"arbeitszeit_equivalent"`
|
||||
}
|
||||
|
||||
type Absence struct {
|
||||
Day time.Time
|
||||
CounterId int
|
||||
CardUID string
|
||||
AbwesenheitTyp AbsenceType
|
||||
Datum time.Time
|
||||
DateFrom time.Time
|
||||
DateTo time.Time
|
||||
}
|
||||
|
||||
func NewAbsence(card_uid string, abwesenheit_typ int, datum time.Time) (Absence, error) {
|
||||
@@ -24,7 +27,7 @@ func NewAbsence(card_uid string, abwesenheit_typ int, datum time.Time) (Absence,
|
||||
return Absence{
|
||||
CardUID: card_uid,
|
||||
AbwesenheitTyp: AbsenceType{0, "Custom absence", 100},
|
||||
Datum: datum,
|
||||
DateFrom: datum,
|
||||
}, nil
|
||||
}
|
||||
_absenceType, ok := GetAbsenceTypesCached()[int8(abwesenheit_typ)]
|
||||
@@ -34,18 +37,64 @@ func NewAbsence(card_uid string, abwesenheit_typ int, datum time.Time) (Absence,
|
||||
return Absence{
|
||||
CardUID: card_uid,
|
||||
AbwesenheitTyp: _absenceType,
|
||||
Datum: datum,
|
||||
DateFrom: datum,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *Absence) Date() time.Time {
|
||||
return a.Day.Truncate(24 * time.Hour)
|
||||
}
|
||||
|
||||
func (a *Absence) TimeWorkVirtual(u User) time.Duration {
|
||||
return a.TimeWorkReal(u)
|
||||
}
|
||||
|
||||
func (a *Absence) TimeWorkReal(u User) time.Duration {
|
||||
if a.AbwesenheitTyp.WorkTime > 1 {
|
||||
return time.Duration(u.ArbeitszeitPerTag * float32(time.Hour)).Round(time.Minute)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (a *Absence) TimePauseReal(u User) (work, pause time.Duration) {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
func (a *Absence) TimeOvertimeReal(u User) time.Duration {
|
||||
if a.AbwesenheitTyp.WorkTime > 1 {
|
||||
return 0
|
||||
}
|
||||
return -u.ArbeitszeitProTag()
|
||||
}
|
||||
|
||||
func (a *Absence) ToString() string {
|
||||
return "Abwesenheit"
|
||||
}
|
||||
|
||||
func (a *Absence) IsWorkDay() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Absence) IsKurzArbeit() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Absence) GetDayProgress(u User) int8 {
|
||||
return 100
|
||||
}
|
||||
|
||||
func (a *Absence) RequiresAction() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Absence) Insert() error {
|
||||
qStr, err := DB.Prepare(`INSERT INTO abwesenheit (card_uid, abwesenheit_typ, datum) VALUES ($1, $2, $3) RETURNING counter_id;`)
|
||||
qStr, err := DB.Prepare(`INSERT INTO abwesenheit (card_uid, abwesenheit_typ, datum_from, datum_to) VALUES ($1, $2, $3, $4) RETURNING counter_id;`)
|
||||
if err != nil {
|
||||
log.Println("Error preparing sql Statement", err)
|
||||
return err
|
||||
}
|
||||
defer qStr.Close()
|
||||
err = qStr.QueryRow(a.CardUID, a.AbwesenheitTyp.Id, a.Datum).Scan(&a.CounterId)
|
||||
err = qStr.QueryRow(a.CardUID, a.AbwesenheitTyp.Id, a.DateFrom, a.DateTo).Scan(&a.CounterId)
|
||||
if err != nil {
|
||||
log.Println("Error executing insert statement", err)
|
||||
return err
|
||||
@@ -55,12 +104,12 @@ func (a *Absence) Insert() error {
|
||||
|
||||
func GetAbsenceById(counterId int) (Absence, error) {
|
||||
var absence Absence = Absence{CounterId: counterId}
|
||||
qStr, err := DB.Prepare("SELECT card_uid, abwesenheit_typ, datum FROM abwesenheit WHERE counter_id = $1;")
|
||||
qStr, err := DB.Prepare("SELECT card_uid, abwesenheit_typ, datum_from, datum_to FROM abwesenheit WHERE counter_id = $1;")
|
||||
if err != nil {
|
||||
return absence, err
|
||||
}
|
||||
defer qStr.Close()
|
||||
err = qStr.QueryRow(counterId).Scan(&absence.CardUID, &absence.AbwesenheitTyp.Id, &absence.Datum)
|
||||
err = qStr.QueryRow(counterId).Scan(&absence.CardUID, &absence.AbwesenheitTyp.Id, &absence.DateFrom, &absence.DateTo)
|
||||
if err != nil {
|
||||
return absence, err
|
||||
}
|
||||
@@ -69,7 +118,32 @@ func GetAbsenceById(counterId int) (Absence, error) {
|
||||
|
||||
func GetAbsencesByCardUID(card_uid string, tsFrom time.Time, tsTo time.Time) ([]Absence, error) {
|
||||
var absences []Absence
|
||||
qStr, err := DB.Prepare("SELECT counter_id, abwesenheit_typ, datum FROM abwesenheit WHERE card_uid = $1 AND datum BETWEEN $2::DATE AND $3::DATE ORDER BY datum;")
|
||||
// qStr, err := DB.Prepare(`SELECT counter_id, abwesenheit_typ, datum_from, datum_to FROM abwesenheit WHERE card_uid = $1 AND datum_from <= $2 AND datum_to >= $3 ORDER BY datum_from;`)
|
||||
qStr, err := DB.Prepare(`
|
||||
SELECT
|
||||
ab.counter_id,
|
||||
gs::DATE AS work_date,
|
||||
ab.card_uid,
|
||||
ab.datum_from,
|
||||
ab.datum_to,
|
||||
jsonb_build_object(
|
||||
'abwesenheit_id', sat.abwesenheit_id,
|
||||
'abwesenheit_name', sat.abwesenheit_name,
|
||||
'arbeitszeit_equivalent', sat.arbeitszeit_equivalent
|
||||
) AS abwesenheit_info
|
||||
FROM generate_series(
|
||||
$2,
|
||||
$3,
|
||||
INTERVAL '1 day'
|
||||
) gs
|
||||
JOIN abwesenheit ab
|
||||
ON ab.card_uid = $1
|
||||
AND ab.datum_from::DATE <= gs::DATE
|
||||
AND ab.datum_to::DATE >= gs::DATE
|
||||
LEFT JOIN s_abwesenheit_typen sat
|
||||
ON ab.abwesenheit_typ = sat.abwesenheit_id
|
||||
ORDER BY gs::DATE, ab.counter_id;
|
||||
`)
|
||||
if err != nil {
|
||||
return absences, err
|
||||
}
|
||||
@@ -81,15 +155,16 @@ func GetAbsencesByCardUID(card_uid string, tsFrom time.Time, tsTo time.Time) ([]
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var absence Absence
|
||||
if err := rows.Scan(&absence.CounterId, &absence.AbwesenheitTyp.Id, &absence.Datum); err != nil {
|
||||
var abwesenheitsTyp []byte
|
||||
if err := rows.Scan(&absence.CounterId, &absence.Day, &absence.CardUID, &absence.DateFrom, &absence.DateTo, &abwesenheitsTyp); err != nil {
|
||||
return absences, err
|
||||
}
|
||||
absence.AbwesenheitTyp, err = GetAbsenceTypeById(absence.AbwesenheitTyp.Id)
|
||||
if err == nil {
|
||||
absences = append(absences, absence)
|
||||
} else {
|
||||
log.Println("Cannot populate absence type!", err)
|
||||
err = json.Unmarshal(abwesenheitsTyp, &absence.AbwesenheitTyp)
|
||||
if err != nil {
|
||||
log.Println("Error parsing abwesenheitsTyp to JSON!", err)
|
||||
return absences, nil
|
||||
}
|
||||
absences = append(absences, absence)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return absences, err
|
||||
|
||||
@@ -85,6 +85,15 @@ func (u *User) GetAll() ([]User, error) {
|
||||
return users, nil
|
||||
}
|
||||
|
||||
// Returns the worktime per day rounded to minutes
|
||||
func (u *User) ArbeitszeitProTag() time.Duration {
|
||||
return time.Duration(u.ArbeitszeitPerTag * float32(time.Hour)).Round(time.Minute)
|
||||
}
|
||||
|
||||
func (u *User) ArbeitszeitProWoche() time.Duration {
|
||||
return time.Duration(u.ArbeitszeitPerWoche * float32(time.Hour)).Round(time.Minute)
|
||||
}
|
||||
|
||||
// Returns true if there is a booking 1 for today -> meaning the user is at work
|
||||
// Returns false if there is no booking today or the user is already booked out of the system
|
||||
func (u *User) CheckAnwesenheit() bool {
|
||||
|
||||
@@ -2,21 +2,144 @@ package models
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IWorkDay interface {
|
||||
Date() time.Time
|
||||
TimeWorkVirtual(User) time.Duration
|
||||
TimeWorkReal(User) time.Duration
|
||||
TimePauseReal(User) (work, pause time.Duration)
|
||||
TimeOvertimeReal(User) time.Duration
|
||||
ToString() string
|
||||
IsWorkDay() bool
|
||||
IsKurzArbeit() bool
|
||||
GetDayProgress(User) int8
|
||||
RequiresAction() bool
|
||||
}
|
||||
|
||||
type WorkDay struct {
|
||||
Day time.Time `json:"day"`
|
||||
Bookings []Booking `json:"bookings"`
|
||||
workTime time.Duration
|
||||
pauseTime time.Duration
|
||||
TimeFrom time.Time
|
||||
TimeTo time.Time
|
||||
Absence Absence
|
||||
Day time.Time `json:"day"`
|
||||
Bookings []Booking `json:"bookings"`
|
||||
workTime time.Duration
|
||||
pauseTime time.Duration
|
||||
realWorkTime time.Duration
|
||||
realPauseTime time.Duration
|
||||
TimeFrom time.Time
|
||||
TimeTo time.Time
|
||||
kurzArbeit bool
|
||||
}
|
||||
|
||||
func GetDays(user User, tsFrom, tsTo time.Time) []IWorkDay {
|
||||
var allDays map[string]IWorkDay = make(map[string]IWorkDay)
|
||||
var sortedDays []IWorkDay
|
||||
for _, day := range GetWorkDays(user, tsFrom, tsTo) {
|
||||
allDays[day.Date().Format("2006-01-02")] = &day
|
||||
}
|
||||
absences, err := GetAbsencesByCardUID(user.CardUID, tsFrom, tsTo)
|
||||
if err != nil {
|
||||
log.Println("Error gettings absences for all Days!", err)
|
||||
return sortedDays
|
||||
}
|
||||
for _, day := range absences {
|
||||
if helper.IsWeekend(day.Date()) {
|
||||
continue
|
||||
}
|
||||
if day.AbwesenheitTyp.WorkTime == 1 {
|
||||
if workDay, ok := allDays[day.Date().Format("2006-01-02")].(*WorkDay); ok {
|
||||
workDay.kurzArbeit = true
|
||||
}
|
||||
} else {
|
||||
allDays[day.Date().Format("2006-01-02")] = &day
|
||||
}
|
||||
}
|
||||
|
||||
for _, day := range allDays {
|
||||
sortedDays = append(sortedDays, day)
|
||||
}
|
||||
return sortedDays
|
||||
}
|
||||
|
||||
func (d *WorkDay) Date() time.Time {
|
||||
return d.Day
|
||||
}
|
||||
|
||||
func (d *WorkDay) TimeWorkVirtual(u User) time.Duration {
|
||||
if d.IsKurzArbeit() {
|
||||
return u.ArbeitszeitProTag()
|
||||
}
|
||||
return d.workTime
|
||||
}
|
||||
|
||||
func (d *WorkDay) TimeWorkReal(u User) time.Duration {
|
||||
d.realWorkTime, d.realPauseTime = 0, 0
|
||||
var lastBooking Booking
|
||||
for _, booking := range d.Bookings {
|
||||
if booking.CheckInOut%2 == 1 {
|
||||
if !lastBooking.Timestamp.IsZero() {
|
||||
d.realPauseTime += booking.Timestamp.Sub(lastBooking.Timestamp)
|
||||
}
|
||||
} else {
|
||||
d.realWorkTime += booking.Timestamp.Sub(lastBooking.Timestamp)
|
||||
}
|
||||
lastBooking = booking
|
||||
}
|
||||
if helper.IsSameDate(d.Date(), time.Now()) && len(d.Bookings)%2 == 1 {
|
||||
d.realWorkTime += time.Since(lastBooking.Timestamp.Local())
|
||||
}
|
||||
return d.realWorkTime
|
||||
}
|
||||
|
||||
func (d *WorkDay) TimeOvertimeReal(u User) time.Duration {
|
||||
workTime := d.TimeWorkVirtual(u)
|
||||
if workTime == 0 {
|
||||
workTime, _ = d.TimePauseReal(u)
|
||||
}
|
||||
if helper.IsWeekend(d.Day) && len(d.Bookings) == 0 {
|
||||
return 0
|
||||
}
|
||||
var overtime time.Duration
|
||||
overtime = workTime - u.ArbeitszeitProTag()
|
||||
return overtime
|
||||
}
|
||||
|
||||
func (d *WorkDay) TimePauseReal(u User) (work, pause time.Duration) {
|
||||
if d.realWorkTime == 0 {
|
||||
d.TimeWorkReal(u)
|
||||
}
|
||||
d.workTime, d.pauseTime = d.realWorkTime, d.realPauseTime
|
||||
if d.realWorkTime <= 6*time.Hour || d.realPauseTime > 45*time.Minute {
|
||||
return d.realWorkTime, d.realPauseTime
|
||||
}
|
||||
if d.realWorkTime <= (9*time.Hour) && d.realPauseTime < 30*time.Minute {
|
||||
diff := 30*time.Minute - d.pauseTime
|
||||
d.workTime -= diff
|
||||
d.pauseTime += diff
|
||||
} else if d.realPauseTime < 45*time.Minute {
|
||||
diff := 45*time.Minute - d.pauseTime
|
||||
d.workTime -= diff
|
||||
d.pauseTime += diff
|
||||
}
|
||||
return d.workTime, d.pauseTime
|
||||
}
|
||||
|
||||
func (d *WorkDay) ToString() string {
|
||||
return "WorkDay"
|
||||
}
|
||||
|
||||
func (d *WorkDay) IsWorkDay() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (d *WorkDay) SetKurzArbeit(kurzArbeit bool) {
|
||||
d.kurzArbeit = kurzArbeit
|
||||
}
|
||||
|
||||
func (d *WorkDay) IsKurzArbeit() bool {
|
||||
return d.kurzArbeit
|
||||
}
|
||||
|
||||
func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay {
|
||||
@@ -24,72 +147,60 @@ func GetWorkDays(user User, tsFrom, tsTo time.Time) []WorkDay {
|
||||
var workSec, pauseSec float64
|
||||
|
||||
qStr, err := DB.Prepare(`
|
||||
WITH all_days AS (
|
||||
SELECT generate_series($2::DATE, $3::DATE - INTERVAL '1 day', INTERVAL '1 day')::DATE AS work_date
|
||||
),
|
||||
ordered_bookings AS (
|
||||
SELECT
|
||||
a.timestamp::DATE AS work_date,
|
||||
a.timestamp,
|
||||
a.check_in_out,
|
||||
a.counter_id,
|
||||
a.anwesenheit_typ,
|
||||
sat.anwesenheit_name AS anwesenheit_typ_name,
|
||||
LAG(a.timestamp) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_timestamp,
|
||||
LAG(a.check_in_out) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_check
|
||||
FROM anwesenheit a
|
||||
LEFT JOIN s_anwesenheit_typen sat ON a.anwesenheit_typ = sat.anwesenheit_id
|
||||
WHERE a.card_uid = $1
|
||||
AND a.timestamp::DATE >= $2
|
||||
AND a.timestamp::DATE <= $3
|
||||
),
|
||||
abwesenheiten AS (
|
||||
SELECT
|
||||
datum::DATE AS work_date,
|
||||
abwesenheit_typ
|
||||
FROM abwesenheit
|
||||
WHERE card_uid = $1
|
||||
AND datum::DATE >= $2
|
||||
AND datum::DATE <= $3
|
||||
)
|
||||
SELECT
|
||||
d.work_date,
|
||||
COALESCE(MIN(b.timestamp), NOW()) AS time_from,
|
||||
COALESCE(MAX(b.timestamp), NOW()) AS time_to,
|
||||
COALESCE(
|
||||
EXTRACT(EPOCH FROM SUM(
|
||||
CASE
|
||||
WHEN b.prev_check IN (1, 3) AND b.check_in_out IN (2, 4, 254)
|
||||
THEN b.timestamp - b.prev_timestamp
|
||||
ELSE INTERVAL '0'
|
||||
END
|
||||
)), 0
|
||||
) AS total_work_seconds,
|
||||
COALESCE(
|
||||
EXTRACT(EPOCH FROM SUM(
|
||||
CASE
|
||||
WHEN b.prev_check IN (2, 4, 254) AND b.check_in_out IN (1, 3)
|
||||
THEN b.timestamp - b.prev_timestamp
|
||||
ELSE INTERVAL '0'
|
||||
END
|
||||
)), 0
|
||||
) AS total_pause_seconds,
|
||||
COALESCE(jsonb_agg(jsonb_build_object(
|
||||
'check_in_out', b.check_in_out,
|
||||
'timestamp', b.timestamp,
|
||||
'counter_id', b.counter_id,
|
||||
'anwesenheit_typ', b.anwesenheit_typ,
|
||||
'anwesenheit_typ', jsonb_build_object(
|
||||
'anwesenheit_id', b.anwesenheit_typ,
|
||||
'anwesenheit_name', b.anwesenheit_typ_name
|
||||
)
|
||||
) ORDER BY b.timestamp), '[]'::jsonb) AS bookings,
|
||||
a.abwesenheit_typ
|
||||
FROM all_days d
|
||||
LEFT JOIN ordered_bookings b ON d.work_date = b.work_date
|
||||
LEFT JOIN abwesenheiten a ON d.work_date = a.work_date
|
||||
GROUP BY d.work_date, a.abwesenheit_typ
|
||||
ORDER BY d.work_date ASC;`)
|
||||
WITH all_days AS (
|
||||
SELECT generate_series($2::DATE, $3::DATE - INTERVAL '1 day', INTERVAL '1 day')::DATE AS work_date),
|
||||
ordered_bookings AS (
|
||||
SELECT
|
||||
a.timestamp::DATE AS work_date,
|
||||
a.timestamp,
|
||||
a.check_in_out,
|
||||
a.counter_id,
|
||||
a.anwesenheit_typ,
|
||||
sat.anwesenheit_name AS anwesenheit_typ_name,
|
||||
LAG(a.timestamp) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_timestamp,
|
||||
LAG(a.check_in_out) OVER (PARTITION BY a.card_uid, a.timestamp::DATE ORDER BY a.timestamp) AS prev_check
|
||||
FROM anwesenheit a
|
||||
LEFT JOIN s_anwesenheit_typen sat ON a.anwesenheit_typ = sat.anwesenheit_id
|
||||
WHERE a.card_uid = $1
|
||||
AND a.timestamp::DATE >= $2
|
||||
AND a.timestamp::DATE <= $3
|
||||
)
|
||||
SELECT
|
||||
d.work_date,
|
||||
COALESCE(MIN(b.timestamp), NOW()) AS time_from,
|
||||
COALESCE(MAX(b.timestamp), NOW()) AS time_to,
|
||||
COALESCE(
|
||||
EXTRACT(EPOCH FROM SUM(
|
||||
CASE
|
||||
WHEN b.prev_check IN (1, 3) AND b.check_in_out IN (2, 4, 254)
|
||||
THEN b.timestamp - b.prev_timestamp
|
||||
ELSE INTERVAL '0'
|
||||
END
|
||||
)), 0
|
||||
) AS total_work_seconds,
|
||||
COALESCE(
|
||||
EXTRACT(EPOCH FROM SUM(
|
||||
CASE
|
||||
WHEN b.prev_check IN (2, 4, 254) AND b.check_in_out IN (1, 3)
|
||||
THEN b.timestamp - b.prev_timestamp
|
||||
ELSE INTERVAL '0'
|
||||
END
|
||||
)), 0
|
||||
) AS total_pause_seconds,
|
||||
COALESCE(jsonb_agg(jsonb_build_object(
|
||||
'check_in_out', b.check_in_out,
|
||||
'timestamp', b.timestamp,
|
||||
'counter_id', b.counter_id,
|
||||
'anwesenheit_typ', b.anwesenheit_typ,
|
||||
'anwesenheit_typ', jsonb_build_object(
|
||||
'anwesenheit_id', b.anwesenheit_typ,
|
||||
'anwesenheit_name', b.anwesenheit_typ_name
|
||||
)
|
||||
) ORDER BY b.timestamp), '[]'::jsonb) AS bookings
|
||||
FROM all_days d
|
||||
LEFT JOIN ordered_bookings b ON d.work_date = b.work_date
|
||||
GROUP BY d.work_date
|
||||
ORDER BY d.work_date ASC;`)
|
||||
|
||||
if err != nil {
|
||||
log.Println("Error preparing SQL statement", err)
|
||||
@@ -107,8 +218,7 @@ ORDER BY d.work_date ASC;`)
|
||||
for rows.Next() {
|
||||
var workDay WorkDay
|
||||
var bookings []byte
|
||||
var absenceType sql.NullInt16
|
||||
if err := rows.Scan(&workDay.Day, &workDay.TimeFrom, &workDay.TimeTo, &workSec, &pauseSec, &bookings, &absenceType); err != nil {
|
||||
if err := rows.Scan(&workDay.Day, &workDay.TimeFrom, &workDay.TimeTo, &workSec, &pauseSec, &bookings); err != nil {
|
||||
log.Println("Error scanning row!", err)
|
||||
return workDays
|
||||
}
|
||||
@@ -123,21 +233,8 @@ ORDER BY d.work_date ASC;`)
|
||||
if len(workDay.Bookings) == 1 && workDay.Bookings[0].CounterId == 0 {
|
||||
workDay.Bookings = []Booking{}
|
||||
}
|
||||
|
||||
if absenceType.Valid {
|
||||
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.CalcRealWorkTime(user)
|
||||
workDay.CalcWorkPauseDiff(user)
|
||||
} else {
|
||||
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{}) {
|
||||
workDay.TimePauseReal(user)
|
||||
if emptyDays && !helper.IsWeekend(workDay.Date()) {
|
||||
workDays = append(workDays, workDay)
|
||||
}
|
||||
}
|
||||
@@ -147,65 +244,16 @@ ORDER BY d.work_date ASC;`)
|
||||
return workDays
|
||||
}
|
||||
|
||||
func (d *WorkDay) CalcWorkPauseDiff(user User) (work, pause time.Duration) {
|
||||
if d.workTime == 0 {
|
||||
d.CalcRealWorkTime(user)
|
||||
func (d *WorkDay) GetAllWorkTimesReal(user User) (work, pause, overtime time.Duration) {
|
||||
if d.pauseTime == 0 || d.workTime == 0 {
|
||||
d.TimePauseReal(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
|
||||
return d.workTime.Round(time.Minute), d.pauseTime.Round(time.Minute), d.TimeOvertimeReal(user)
|
||||
}
|
||||
|
||||
func (d *WorkDay) CalcRealWorkTime(user User) time.Duration {
|
||||
if (len(d.Bookings) < 1 && d.Absence == Absence{}) {
|
||||
return 0
|
||||
}
|
||||
var realWorkTime, realPauseTime time.Duration
|
||||
var lastBooking Booking
|
||||
for _, booking := range d.Bookings {
|
||||
if booking.CheckInOut%2 == 1 {
|
||||
if !lastBooking.Timestamp.IsZero() {
|
||||
realPauseTime += booking.Timestamp.Sub(lastBooking.Timestamp)
|
||||
}
|
||||
} else {
|
||||
realWorkTime += booking.Timestamp.Sub(lastBooking.Timestamp)
|
||||
}
|
||||
lastBooking = booking
|
||||
}
|
||||
if helper.IsSameDate(d.Day, time.Now()) && len(d.Bookings)%2 == 1 {
|
||||
realWorkTime += time.Since(lastBooking.Timestamp.Local())
|
||||
}
|
||||
if d.Absence.AbwesenheitTyp.WorkTime > 0 {
|
||||
realWorkTime = time.Duration(user.ArbeitszeitPerTag * float32(time.Hour)).Round(time.Minute)
|
||||
}
|
||||
d.workTime = realWorkTime
|
||||
d.pauseTime = realPauseTime
|
||||
|
||||
return realWorkTime
|
||||
}
|
||||
|
||||
func (d *WorkDay) GetWorkTimeString() (work string, pause string) {
|
||||
workString := helper.FormatDuration(d.workTime)
|
||||
pauseString := helper.FormatDuration(d.pauseTime)
|
||||
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)
|
||||
func (d *WorkDay) GetAllWorkTimesVirtual(user User) (work, pause, overtime time.Duration) {
|
||||
_, pause, overtime = d.GetAllWorkTimesReal(user)
|
||||
return d.TimeWorkVirtual(user), pause, overtime
|
||||
}
|
||||
|
||||
// returns bool wheter the workday was ended with an automatic logout
|
||||
@@ -216,21 +264,23 @@ func (d *WorkDay) RequiresAction() bool {
|
||||
return d.Bookings[len(d.Bookings)-1].CheckInOut == 254
|
||||
}
|
||||
|
||||
// returns a integer percentage of how much day has been worked of
|
||||
func (d *WorkDay) GetWorkDayProgress(user User) uint8 {
|
||||
defaultWorkTime := time.Duration(user.ArbeitszeitPerTag * float32(time.Hour)).Round(time.Minute)
|
||||
progress := (d.workTime.Seconds() / defaultWorkTime.Seconds()) * 100
|
||||
return uint8(progress)
|
||||
func (d *WorkDay) GetDayProgress(u User) int8 {
|
||||
if d.RequiresAction() {
|
||||
return -1
|
||||
}
|
||||
workTime := d.TimeWorkVirtual(u)
|
||||
progress := (workTime.Seconds() / u.ArbeitszeitProTag().Seconds()) * 100
|
||||
return int8(progress)
|
||||
}
|
||||
|
||||
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)
|
||||
return overtime
|
||||
}
|
||||
// func (d *WorkDay) CalcOvertime(user User) time.Duration {
|
||||
// if d.workTime == 0 {
|
||||
// d.TimePauseReal(user)
|
||||
// }
|
||||
// if helper.IsWeekend(d.Day) && len(d.Bookings) == 0 {
|
||||
// return 0
|
||||
// }
|
||||
// var overtime time.Duration
|
||||
// overtime = d.workTime - user.ArbeitszeitProTag()
|
||||
// return overtime
|
||||
// }
|
||||
|
||||
@@ -20,11 +20,10 @@ var testWorkDay = models.WorkDay{
|
||||
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)
|
||||
workTime := testWorkDay.TimeWorkReal(testUser)
|
||||
if workTime != time.Hour*8 {
|
||||
t.Errorf("Calc Worktime Default not working, time should be 8h, but was %s", helper.FormatDuration(workTime))
|
||||
}
|
||||
@@ -64,10 +63,10 @@ func TestCalcWorkPauseDiff(t *testing.T) {
|
||||
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)
|
||||
testWorkDay.TimeWorkReal(testUser)
|
||||
testWorkDay.TimePauseReal(testUser)
|
||||
testWorkDay.TimeOvertimeReal(testUser)
|
||||
workTime, pauseTime, overTime := testWorkDay.GetAllWorkTimesReal(testUser)
|
||||
if workTime != test.expectedWorkTime {
|
||||
t.Errorf("Calculated wrong workTime: should be %s, but was %s", helper.FormatDuration(test.expectedWorkTime), helper.FormatDuration(workTime))
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"arbeitszeitmessung/helper"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"log"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -14,6 +14,7 @@ type WorkWeek struct {
|
||||
Id int
|
||||
WorkDays []WorkDay
|
||||
Absences []Absence
|
||||
Days []IWorkDay
|
||||
User User
|
||||
WeekStart time.Time
|
||||
Worktime time.Duration
|
||||
@@ -39,20 +40,21 @@ func NewWorkWeek(user User, tsMonday time.Time, populate bool) WorkWeek {
|
||||
Status: WeekStatusNone,
|
||||
}
|
||||
if populate {
|
||||
week.PopulateWithBookings(0, 0)
|
||||
week.PopulateWithDays(0, 0)
|
||||
}
|
||||
return week
|
||||
}
|
||||
|
||||
func (w *WorkWeek) PopulateWithBookings(worktime time.Duration, overtime time.Duration) {
|
||||
w.WorkDays = GetWorkDays(w.User, w.WeekStart, w.WeekStart.Add(7*24*time.Hour))
|
||||
if absences, err := GetAbsencesByCardUID(w.User.CardUID, w.WeekStart, w.WeekStart.Add(7*24*time.Hour)); err == nil {
|
||||
w.Absences = absences
|
||||
} else {
|
||||
log.Printf("Error populating absences in workWeek (%s): %v", w.WeekStart, err)
|
||||
func (w *WorkWeek) PopulateWithDays(worktime time.Duration, overtime time.Duration) {
|
||||
w.Days = GetDays(w.User, w.WeekStart, w.WeekStart.Add(6*24*time.Hour))
|
||||
sort.Slice(w.Days, func(i, j int) bool {
|
||||
return w.Days[i].Date().Before(w.Days[j].Date())
|
||||
})
|
||||
|
||||
for _, day := range w.Days {
|
||||
w.Worktime += day.TimeWorkVirtual(w.User)
|
||||
}
|
||||
w.Worktime = w.aggregateWorkTime()
|
||||
w.Overtime = w.Worktime - time.Duration(w.User.ArbeitszeitPerWoche*float32(time.Hour))
|
||||
w.Overtime = w.Worktime - w.User.ArbeitszeitProWoche()
|
||||
|
||||
w.Worktime = w.Worktime.Round(time.Minute)
|
||||
w.Overtime = w.Overtime.Round(time.Minute)
|
||||
@@ -99,10 +101,6 @@ func (w *WorkWeek) CheckStatus() WeekStatus {
|
||||
return w.Status
|
||||
}
|
||||
|
||||
func (w *WorkWeek) GetWorkHourString() string {
|
||||
return helper.FormatDuration(w.Worktime)
|
||||
}
|
||||
|
||||
func (w *WorkWeek) aggregateWorkTime() time.Duration {
|
||||
var workTime time.Duration
|
||||
for _, day := range w.WorkDays {
|
||||
@@ -138,7 +136,7 @@ func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
|
||||
return weeks
|
||||
}
|
||||
|
||||
week.PopulateWithBookings(week.Worktime, week.Overtime)
|
||||
week.PopulateWithDays(week.Worktime, week.Overtime)
|
||||
weeks = append(weeks, week)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
@@ -196,8 +194,8 @@ func (w *WorkWeek) Accept() error {
|
||||
}
|
||||
|
||||
func (w *WorkWeek) RequiresAction() bool {
|
||||
var requiresAction bool = true
|
||||
for _, day := range w.WorkDays {
|
||||
var requiresAction bool = false
|
||||
for _, day := range w.Days {
|
||||
requiresAction = requiresAction || day.RequiresAction()
|
||||
}
|
||||
return requiresAction
|
||||
|
||||
Reference in New Issue
Block a user