245 lines
7.0 KiB
Go
245 lines
7.0 KiB
Go
package models
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"log"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"github.com/lib/pq"
|
|
)
|
|
|
|
// Workweeks are
|
|
|
|
type WorkWeek struct {
|
|
Id int
|
|
WorkDays []WorkDay
|
|
Absences []Absence
|
|
Days []IWorkDay
|
|
User User
|
|
WeekStart time.Time
|
|
Worktime time.Duration
|
|
WorkTimeVirtual time.Duration
|
|
Overtime time.Duration
|
|
Status WeekStatus
|
|
}
|
|
|
|
type WeekStatus int8
|
|
|
|
const (
|
|
WeekStatusNone WeekStatus = iota
|
|
WeekStatusSent
|
|
WeekStatusAccepted
|
|
WeekStatusDifferences
|
|
)
|
|
|
|
func NewWorkWeek(user User, tsMonday time.Time, populate bool) WorkWeek {
|
|
var week WorkWeek = WorkWeek{
|
|
User: user,
|
|
WeekStart: tsMonday,
|
|
Status: WeekStatusNone,
|
|
}
|
|
if populate {
|
|
week.PopulateWithDays(0, 0)
|
|
}
|
|
return week
|
|
}
|
|
|
|
func (w *WorkWeek) PopulateWithDays(worktime time.Duration, overtime time.Duration) {
|
|
slog.Debug("Got Days with overtime and worktime", slog.String("worktime", worktime.String()), slog.String("overtime", overtime.String()))
|
|
w.Days = GetDays(w.User, w.WeekStart, w.WeekStart.Add(6*24*time.Hour), false)
|
|
|
|
for _, day := range w.Days {
|
|
work, _ := day.TimePauseReal(w.User)
|
|
w.Worktime += work
|
|
w.WorkTimeVirtual += day.TimeWorkVirtual(w.User)
|
|
}
|
|
slog.Debug("Got worktime for user", "user", w.User, "worktime", w.Worktime.String(), "virtualWorkTime", w.WorkTimeVirtual.String())
|
|
|
|
w.Overtime = w.WorkTimeVirtual - w.User.ArbeitszeitProWoche()
|
|
|
|
w.Worktime = w.Worktime.Round(time.Minute)
|
|
w.Overtime = w.Overtime.Round(time.Minute)
|
|
|
|
if overtime == 0 && worktime == 0 {
|
|
return
|
|
}
|
|
|
|
if overtime != w.Overtime || worktime != w.Worktime {
|
|
w.Status = WeekStatusDifferences
|
|
}
|
|
}
|
|
|
|
func (w *WorkWeek) CheckStatus() WeekStatus {
|
|
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;`)
|
|
if err != nil {
|
|
log.Println("Error preparing SQL statement", err)
|
|
return w.Status
|
|
}
|
|
defer qStr.Close()
|
|
var beastatigt bool
|
|
err = qStr.QueryRow(w.WeekStart, w.User.PersonalNummer).Scan(&beastatigt)
|
|
if err == sql.ErrNoRows {
|
|
return w.Status
|
|
}
|
|
if err != nil {
|
|
log.Println("Error querying database", err)
|
|
return w.Status
|
|
}
|
|
if beastatigt {
|
|
w.Status = WeekStatusAccepted
|
|
} else {
|
|
w.Status = WeekStatusSent
|
|
}
|
|
return w.Status
|
|
}
|
|
|
|
func (w *WorkWeek) aggregateWorkTime() time.Duration {
|
|
var workTime time.Duration
|
|
for _, day := range w.WorkDays {
|
|
workTime += day.workTime
|
|
}
|
|
// for _, absence := range w.Absences {
|
|
// log.Println(absence)
|
|
// absenceWorkTime := float32(8) // := 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
|
|
}
|
|
|
|
func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek {
|
|
var weeks []WorkWeek
|
|
qStr, err := DB.Prepare(`SELECT id, woche_start::DATE, (EXTRACT(epoch FROM arbeitszeit)*1000000000)::BIGINT, (EXTRACT(epoch FROM ueberstunden)*1000000000)::BIGINT FROM wochen_report WHERE bestaetigt = FALSE AND personal_nummer = $1;`)
|
|
if err != nil {
|
|
log.Println("Error preparing SQL statement", err)
|
|
return weeks
|
|
}
|
|
defer qStr.Close()
|
|
|
|
rows, err := qStr.Query(user.PersonalNummer)
|
|
if err != nil {
|
|
log.Println("Error querining db!", err)
|
|
return weeks
|
|
}
|
|
defer rows.Close()
|
|
for rows.Next() {
|
|
var week WorkWeek = WorkWeek{User: user}
|
|
var workTime, overTime time.Duration
|
|
if err := rows.Scan(&week.Id, &week.WeekStart, &workTime, &overTime); err != nil {
|
|
log.Println("Error scanning row!", err)
|
|
return weeks
|
|
}
|
|
|
|
week.PopulateWithDays(workTime, overTime)
|
|
weeks = append(weeks, week)
|
|
}
|
|
if err = rows.Err(); err != nil {
|
|
return weeks
|
|
}
|
|
|
|
return weeks
|
|
}
|
|
|
|
var ErrRunningWeek = errors.New("Week is in running week")
|
|
|
|
func (w *WorkWeek) GetBookingIds() (anwesenheitsIds, abwesenheitsIds []int64, err error) {
|
|
qStr, err := DB.Prepare(`
|
|
SELECT
|
|
(SELECT array_agg(counter_id ORDER BY counter_id)
|
|
FROM anwesenheit
|
|
WHERE card_uid = $1
|
|
AND timestamp::DATE >= $2
|
|
AND timestamp::DATE < $3) AS anwesenheit,
|
|
|
|
(SELECT array_agg(counter_id ORDER BY counter_id)
|
|
FROM abwesenheit
|
|
WHERE card_uid = $1
|
|
AND datum_from < $3
|
|
AND datum_to >= $2) AS abwesenheit;
|
|
`)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
defer qStr.Close()
|
|
|
|
slog.Debug("Inserting parameters into qStr:", "user card_uid", w.User.CardUID, "week_start", w.WeekStart, "week_end", w.WeekStart.AddDate(0, 0, 5))
|
|
|
|
err = qStr.QueryRow(w.User.CardUID, w.WeekStart, w.WeekStart.AddDate(0, 0, 5)).Scan(pq.Array(&anwesenheitsIds), pq.Array(&abwesenheitsIds))
|
|
if err != nil {
|
|
return anwesenheitsIds, abwesenheitsIds, err
|
|
}
|
|
return anwesenheitsIds, abwesenheitsIds, nil
|
|
}
|
|
|
|
// creates a new entry in the woche_report table with the given workweek
|
|
func (w *WorkWeek) SendWeek() error {
|
|
var qStr *sql.Stmt
|
|
var err error
|
|
|
|
slog.Info("Sending workWeek to team head", "week", w.WeekStart.String())
|
|
|
|
anwBookings, awBookings, err := w.GetBookingIds()
|
|
if err != nil {
|
|
slog.Warn("Error querying bookings from work week", slog.Any("error", err))
|
|
return err
|
|
}
|
|
|
|
slog.Debug("Recieved Booking Ids", "anwesenheiten", anwBookings)
|
|
|
|
if time.Since(w.WeekStart) < 5*24*time.Hour {
|
|
slog.Warn("Cannot send week, because it's the running week!")
|
|
return ErrRunningWeek
|
|
}
|
|
|
|
if w.CheckStatus() != WeekStatusNone {
|
|
qStr, err = DB.Prepare(`UPDATE "wochen_report" SET bestaetigt = FALSE, arbeitszeit = make_interval(secs => $3::numeric / 1000000000), ueberstunden = make_interval(secs => $4::numeric / 1000000000), anwesenheiten=$5, abwesenheiten=$6 WHERE personal_nummer = $1 AND woche_start = $2;`)
|
|
if err != nil {
|
|
slog.Warn("Error preparing SQL statement", "error", err)
|
|
return err
|
|
}
|
|
} else {
|
|
qStr, err = DB.Prepare(`INSERT INTO wochen_report (personal_nummer, woche_start, arbeitszeit, ueberstunden, anwesenheiten, abwesenheiten) VALUES ($1, $2, make_interval(secs => $3::numeric / 1000000000), make_interval(secs => $4::numeric / 1000000000), $5, $6);`)
|
|
if err != nil {
|
|
slog.Warn("Error preparing SQL statement", "error", err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
_, err = qStr.Exec(w.User.PersonalNummer, w.WeekStart, int64(w.Worktime), int64(w.Overtime), pq.Array(anwBookings), pq.Array(awBookings))
|
|
if err != nil {
|
|
log.Println("Error executing query!", err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *WorkWeek) Accept() error {
|
|
qStr, err := DB.Prepare(`UPDATE "wochen_report" SET bestaetigt = TRUE WHERE personal_nummer = $1 AND woche_start = $2;`)
|
|
if err != nil {
|
|
log.Println("Error preparing SQL statement", err)
|
|
return err
|
|
}
|
|
_, err = qStr.Exec(w.User.PersonalNummer, w.WeekStart)
|
|
if err != nil {
|
|
log.Println("Error executing query!", err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (w *WorkWeek) RequiresAction() bool {
|
|
var requiresAction bool = false
|
|
for _, day := range w.Days {
|
|
requiresAction = requiresAction || day.RequiresAction()
|
|
}
|
|
return requiresAction
|
|
}
|