added interface for workday and absence + multiday absences closes #38, #39

This commit is contained in:
2025-09-23 12:30:02 +02:00
parent 55b0332600
commit db6fc10c28
19 changed files with 1396 additions and 1034 deletions

View File

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