From 0d7696cbc6b9e3eef8f075f47422de90e68be8fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Tr=C3=B6ger?= Date: Tue, 14 Oct 2025 01:05:02 +0200 Subject: [PATCH] adding more logging + working on displaying if a workday was submitted --- Backend/models/booking.go | 22 +++++ Backend/models/workDay.go | 2 + Backend/models/workWeek.go | 91 ++++++++++++++----- Backend/templates/headerComponent_templ.go | 2 +- Backend/templates/pages_templ.go | 2 +- Backend/templates/pdf_templ.go | 2 +- Backend/templates/teamComponents.templ | 2 +- Backend/templates/teamComponents_templ.go | 4 +- Backend/templates/timeComponents.templ | 3 + Backend/templates/timeComponents_templ.go | 16 +++- Backend/templates/timePage_templ.go | 2 +- DB/initdb/01_schema.sql | 28 +++--- .../20251013212224_buchungs_array.down.sql | 4 + .../20251013212224_buchungs_array.up.sql | 4 + migrations/atlas.sum | 24 ++--- 15 files changed, 146 insertions(+), 62 deletions(-) create mode 100644 migrations/20251013212224_buchungs_array.down.sql create mode 100644 migrations/20251013212224_buchungs_array.up.sql diff --git a/Backend/models/booking.go b/Backend/models/booking.go index 7b4d356..28b638a 100644 --- a/Backend/models/booking.go +++ b/Backend/models/booking.go @@ -6,6 +6,7 @@ import ( "database/sql" "fmt" "log" + "log/slog" "net/url" "strconv" "time" @@ -87,6 +88,27 @@ func (b *Booking) Verify() bool { return true } +func (b *Booking) IsSubmittedAndChecked() bool { + qStr, err := DB.Prepare(`SELECT bestaetigt from wochen_report WHERE $1 = ANY(anwesenheiten);`) + if err != nil { + slog.Warn("Error when preparing SQL Statement", "error", err) + return false + } + defer qStr.Close() + var isSubmittedAndChecked bool = false + + err = qStr.QueryRow(b.CounterId).Scan(&isSubmittedAndChecked) + if err == sql.ErrNoRows { + // No rows found ==> not even submitted + return false + } + + if err != nil { + slog.Warn("Unexpected error when executing SQL Statement", "error", err) + } + return isSubmittedAndChecked +} + func (b *Booking) Insert() error { if !checkLastBooking(*b) { return SameBookingError{} diff --git a/Backend/models/workDay.go b/Backend/models/workDay.go index 9780ce7..5ae30a0 100644 --- a/Backend/models/workDay.go +++ b/Backend/models/workDay.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "log" + "log/slog" "sort" "time" ) @@ -110,6 +111,7 @@ func (d *WorkDay) TimeWorkReal(u User) time.Duration { if helper.IsSameDate(d.Date(), time.Now()) && len(d.Bookings)%2 == 1 { d.realWorkTime += time.Since(lastBooking.Timestamp.Local()) } + slog.Debug("Calculated RealWorkTime for user", "user", u, slog.String("worktime", d.realWorkTime.String())) return d.realWorkTime } diff --git a/Backend/models/workWeek.go b/Backend/models/workWeek.go index 634d6fb..beaf504 100644 --- a/Backend/models/workWeek.go +++ b/Backend/models/workWeek.go @@ -4,23 +4,25 @@ 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 - Overtime time.Duration - Status WeekStatus - overtimeDiff time.Duration - worktimeDiff time.Duration + 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 @@ -45,16 +47,17 @@ func NewWorkWeek(user User, tsMonday time.Time, populate bool) WorkWeek { } func (w *WorkWeek) PopulateWithDays(worktime time.Duration, overtime time.Duration) { - log.Println("Got Days with overtime and worktime", worktime, overtime) + 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) - log.Println(w.Worktime) for _, day := range w.Days { - log.Println(day.TimeWorkVirtual(w.User)) - w.Worktime += day.TimeWorkVirtual(w.User) + work, _ := day.TimePauseReal(w.User) + w.Worktime += work + w.WorkTimeVirtual += day.TimeWorkVirtual(w.User) } - log.Println("Calculated new worktime", w.Worktime) - w.Overtime = w.Worktime - w.User.ArbeitszeitProWoche() + 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) @@ -65,8 +68,6 @@ func (w *WorkWeek) PopulateWithDays(worktime time.Duration, overtime time.Durati if overtime != w.Overtime || worktime != w.Worktime { w.Status = WeekStatusDifferences - w.overtimeDiff = overtime - w.worktimeDiff = worktime } } @@ -149,30 +150,70 @@ func (w *WorkWeek) GetSendWeeks(user User) []WorkWeek { 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 { - log.Println("Cannot send week, because it's the running week!") + 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) WHERE personal_nummer = $1 AND woche_start = $2;`) + 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 { - log.Println("Error preparing SQL statement", err) + 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) VALUES ($1, $2, make_interval(secs => $3::numeric / 1000000000), make_interval(secs => $4::numeric / 1000000000));`) + 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 { - log.Println("Error preparing SQL statement", err) + slog.Warn("Error preparing SQL statement", "error", err) return err } } - _, err = qStr.Exec(w.User.PersonalNummer, w.WeekStart, int64(w.Worktime), int64(w.Overtime)) + + _, 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 diff --git a/Backend/templates/headerComponent_templ.go b/Backend/templates/headerComponent_templ.go index 8e59160..aeef6ec 100644 --- a/Backend/templates/headerComponent_templ.go +++ b/Backend/templates/headerComponent_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.943 +// templ: version: v0.3.924 package templates //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/Backend/templates/pages_templ.go b/Backend/templates/pages_templ.go index ad17982..491784c 100644 --- a/Backend/templates/pages_templ.go +++ b/Backend/templates/pages_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.943 +// templ: version: v0.3.924 package templates //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/Backend/templates/pdf_templ.go b/Backend/templates/pdf_templ.go index 321301b..3c73ded 100644 --- a/Backend/templates/pdf_templ.go +++ b/Backend/templates/pdf_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.943 +// templ: version: v0.3.924 package templates //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/Backend/templates/teamComponents.templ b/Backend/templates/teamComponents.templ index e5738b9..d1dd820 100644 --- a/Backend/templates/teamComponents.templ +++ b/Backend/templates/teamComponents.templ @@ -81,7 +81,7 @@ templ weekDayComponent(user models.User, day models.WorkDay) { templ workWeekComponent(week models.WorkWeek, onlyAccept bool) { {{ year, kw := week.WeekStart.ISOWeek() - progress := (float32(week.Worktime.Hours()) / week.User.ArbeitszeitPerWoche) * 100 + progress := (float32(week.WorkTimeVirtual.Hours()) / week.User.ArbeitszeitPerWoche) * 100 }}
diff --git a/Backend/templates/teamComponents_templ.go b/Backend/templates/teamComponents_templ.go index c1d475e..1c0e46b 100644 --- a/Backend/templates/teamComponents_templ.go +++ b/Backend/templates/teamComponents_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.943 +// templ: version: v0.3.924 package templates //lint:file-ignore SA4006 This context is only used if a nested component is present. @@ -346,7 +346,7 @@ func workWeekComponent(week models.WorkWeek, onlyAccept bool) templ.Component { ctx = templ.ClearChildren(ctx) year, kw := week.WeekStart.ISOWeek() - progress := (float32(week.Worktime.Hours()) / week.User.ArbeitszeitPerWoche) * 100 + progress := (float32(week.WorkTimeVirtual.Hours()) / week.User.ArbeitszeitPerWoche) * 100 templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err diff --git a/Backend/templates/timeComponents.templ b/Backend/templates/timeComponents.templ index 799ebda..9730101 100644 --- a/Backend/templates/timeComponents.templ +++ b/Backend/templates/timeComponents.templ @@ -120,6 +120,9 @@ templ bookingComponent(booking models.Booking) { { booking.GetBookingType() }

+ if booking.IsSubmittedAndChecked() { +

submitted

+ }
} diff --git a/Backend/templates/timeComponents_templ.go b/Backend/templates/timeComponents_templ.go index c7ad17e..f024321 100644 --- a/Backend/templates/timeComponents_templ.go +++ b/Backend/templates/timeComponents_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.943 +// templ: version: v0.3.924 package templates //lint:file-ignore SA4006 This context is only used if a nested component is present. @@ -568,7 +568,17 @@ func bookingComponent(booking models.Booking) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if booking.IsSubmittedAndChecked() { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "

submitted

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -597,7 +607,7 @@ func LegendComponent() templ.Component { templ_7745c5c3_Var31 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "
Fehler
Arbeitszeit unter regulär
Arbeitszeit vollständig
Überstunden
Keine Buchungen
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "
Fehler
Arbeitszeit unter regulär
Arbeitszeit vollständig
Überstunden
Keine Buchungen
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/Backend/templates/timePage_templ.go b/Backend/templates/timePage_templ.go index dd29246..d668290 100644 --- a/Backend/templates/timePage_templ.go +++ b/Backend/templates/timePage_templ.go @@ -1,6 +1,6 @@ // Code generated by templ - DO NOT EDIT. -// templ: version: v0.3.943 +// templ: version: v0.3.924 package templates //lint:file-ignore SA4006 This context is only used if a nested component is present. diff --git a/DB/initdb/01_schema.sql b/DB/initdb/01_schema.sql index e3e795f..21e48ea 100755 --- a/DB/initdb/01_schema.sql +++ b/DB/initdb/01_schema.sql @@ -22,7 +22,7 @@ COMMENT ON COLUMN "anwesenheit"."geraet_id" IS 'ID des Lesegerätes'; DROP TABLE IF EXISTS "s_anwesenheit_typen"; CREATE TABLE "s_anwesenheit_typen" ( "anwesenheit_id" int2 PRIMARY KEY, - "anwesenheit_name" varchar(255) + "anwesenheit_name" varchar(255) NOT NULL ); -- ---------------------------- @@ -78,30 +78,34 @@ EXECUTE FUNCTION update_zuletzt_geandert(); DROP TABLE IF EXISTS "wochen_report"; CREATE TABLE "wochen_report" ( "id" serial PRIMARY KEY, - "personal_nummer" int4, - "woche_start" date, + "personal_nummer" int4 NOT NULL, + "woche_start" date NOT NULL, "bestaetigt" bool DEFAULT FALSE, - "arbeitszeit" interval, - "ueberstunden" interval, + "arbeitszeit" interval NOT NULL, + "ueberstunden" interval NOT NULL, + "anwesenheiten" int ARRAY, + "abwesenheiten" int ARRAY, UNIQUE ("personal_nummer", "woche_start") ); DROP TABLE IF EXISTS "abwesenheit"; CREATE TABLE "abwesenheit" ( "counter_id" bigserial PRIMARY KEY, - "card_uid" varchar(255), - "abwesenheit_typ" int2, - "datum_from" timestamptz DEFAULT NOW()::DATE, - "datum_to" timestamptz + "card_uid" varchar(255) NOT NULL, + "abwesenheit_typ" int2 NOT NULL, + "datum_from" timestamptz DEFAULT NOW()::DATE NOT NULL, + "datum_to" timestamptz NOT NULL ); DROP TABLE IF EXISTS "s_abwesenheit_typen"; CREATE TABLE "s_abwesenheit_typen" ( - "abwesenheit_id" int2 PRIMARY KEY, - "abwesenheit_name" varchar(255), - "arbeitszeit_equivalent" float4 + "abwesenheit_id" int2 PRIMARY KEY NOT NULL, + "abwesenheit_name" varchar(255) NOT NULL, + "arbeitszeit_equivalent" float4 NOT NULL ); +COMMENT ON COLUMN "s_abwesenheit_typen"."arbeitszeit_equivalent" IS '0=keine Arbeitszeit; 1=Arbeitszeit auffüllen; 2=Arbeitszeit austauschen'; + -- Adds crypto extension CREATE EXTENSION IF NOT EXISTS pgcrypto; diff --git a/migrations/20251013212224_buchungs_array.down.sql b/migrations/20251013212224_buchungs_array.down.sql new file mode 100644 index 0000000..b403a4c --- /dev/null +++ b/migrations/20251013212224_buchungs_array.down.sql @@ -0,0 +1,4 @@ +-- reverse: modify "wochen_report" table +ALTER TABLE "wochen_report" DROP COLUMN "abwesenheiten", DROP COLUMN "anwesenheiten", ALTER COLUMN "arbeitszeit" DROP NOT NULL, ALTER COLUMN "ueberstunden" DROP NOT NULL, ALTER COLUMN "woche_start" DROP NOT NULL; +-- reverse: modify "abwesenheit" table +ALTER TABLE "abwesenheit" ALTER COLUMN "datum_to" DROP NOT NULL; diff --git a/migrations/20251013212224_buchungs_array.up.sql b/migrations/20251013212224_buchungs_array.up.sql new file mode 100644 index 0000000..1baa199 --- /dev/null +++ b/migrations/20251013212224_buchungs_array.up.sql @@ -0,0 +1,4 @@ +-- modify "abwesenheit" table +ALTER TABLE "abwesenheit" ALTER COLUMN "datum_to" SET NOT NULL; +-- modify "wochen_report" table +ALTER TABLE "wochen_report" ALTER COLUMN "woche_start" SET NOT NULL, ALTER COLUMN "ueberstunden" SET NOT NULL, ALTER COLUMN "arbeitszeit" SET NOT NULL, ADD COLUMN "anwesenheiten" integer[] NULL, ADD COLUMN "abwesenheiten" integer[] NULL; diff --git a/migrations/atlas.sum b/migrations/atlas.sum index 4950d34..114628d 100644 --- a/migrations/atlas.sum +++ b/migrations/atlas.sum @@ -1,15 +1,9 @@ -h1:3AxgD8mnu/F+JGtJu9FZvA9Ro0UUtGPgyjskKtfTYUQ= -20250901201159_initial.down.sql h1:cmF5CvNGqEfcmbRgiqaqDWERdNNRaMzarbNLJ/Y35o4= -20250901201159_initial.up.sql h1:Yrak/+wfQ4Tu/dVR/cUZ/75DlAcv4G/OJXDqpgSw47U= -20250901201250_control_tables.down.sql h1:f/KmhO9pOI45J8ZRjFonvD3CypB+rOoGOPN2WMFHvOw= -20250901201250_control_tables.up.sql h1:of5E07p0N1aen9CdQNEOrO7ffbKZC6kp4oK5KPzU9+g= -20250901201710_triggers_extension.down.sql h1:a9va3FSfHBWzODJSJO+ywNa2hiZwjG/vmvYGb3L1lnM= -20250901201710_triggers_extension.up.sql h1:nUBPd2eDssi/TwMVF/nOJkIM5rUM0iINdg1K9pZRZN0= -20250903221313_overtime.down.sql h1:X+jJESqcZ6ZTd2H563z6kRaXb4dn4sA02D3ck2795v8= -20250903221313_overtime.up.sql h1:C3DSiNVpe9v0Un1DEQ0lsy5yToR8iqcggv91GSr6tRE= -20250903233030_non_null_contraints.down.sql h1:42TZzPsji2Ze50k6sLwgIuNo4Trk3m3ni/aIfQJ97dE= -20250903233030_non_null_contraints.up.sql h1:k6zR5YNSAP4fo5QEc58KZ0LxvEz1nl0X/AAcZ+TG3I4= -20250904114004_intervals.down.sql h1:SquJAPinzFIRN6fJjLLIRsz59Tyr4RwGiGuOFI/N1SQ= -20250904114004_intervals.up.sql h1:AFqncTGOiEZVBbhWFqN2zlQ7DyhybB5wJr6a36Atk1E= -20250916093608_kurzarbeit.down.sql h1:ljM1a1pQCxOQiXRaXU04GC4V9yy2y20x5eUNQ/zyx+o= -20250916093608_kurzarbeit.up.sql h1:pTiw0VfGaf26mhJg4wf98Fqwn1kShJ+PiN2PiM4q1kk= +h1:gE7ikkZS7bQbedAjVspQKftSo5ODij2eiQGWbfQEYmI= +20250901201159_initial.up.sql h1:Mb1RlVdFvcxqU9HrSK6oNeURqFa3O4KzB3rDa+6+3gc= +20250901201250_control_tables.up.sql h1:a5LATgR/CRiC4GsqxkJ94TyJOxeTcW74eCnodIy+c1E= +20250901201710_triggers_extension.up.sql h1:z9b6Hk9btE2Ns4mU7B16HjvYBP6EEwHAXVlvPpkn978= +20250903221313_overtime.up.sql h1:t/B435ShW5ZEnzC81jRABWVZ5gNm7tPZPnOO6/ZY6ow= +20250903233030_non_null_contraints.up.sql h1:YKeYgazfh+jPyh7hFT/pV+By8eHnk1taXnlgSLyXSA0= +20250904114004_intervals.up.sql h1:gDdN8cJ4xH1vQhAbbhqD5lwdyEO1N9EIqEYkmWGiWIU= +20250916093608_kurzarbeit.up.sql h1:yDAAMLyUXz6b7+MI6XK/HZMPzutKoT2NNNOCjFaqSts= +20251013212224_buchungs_array.up.sql h1:mbhvnwMUkEFFQQ41NC47auqxbtvNkztziWvpLDFm6tA=