From 23b45881a14f8400cc71550727458f1aa3a2f061 Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 25 May 2026 22:29:13 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20=C3=9Cberstunden=20tages-weise=20berechn?= =?UTF-8?q?en=20statt=20Gesamtzeitraum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vorher: worked_total vs expected_total über den gesamten Zeitraum → Urlaubs-/Kranktage senkten das Konto obwohl keine Überstunden gemacht wurden Jetzt: pro Tag mit Eintrag wird worked_on_day vs scheduled_on_day verglichen → nur echte Mehrarbeit zählt als Überstunde Co-Authored-By: Claude Sonnet 4.6 --- backend/app/services/report_service.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/backend/app/services/report_service.py b/backend/app/services/report_service.py index 0bebf86..599ec14 100644 --- a/backend/app/services/report_service.py +++ b/backend/app/services/report_service.py @@ -223,9 +223,22 @@ async def _recalculate_overtime_balance( return float(sa.factor) return 1.0 - expected = _expected_hours(schedule, date_from_all, date_to_all) - worked = sum((e.worked_hours or 0.0) * _fza_factor(e.date) for e in entries) - overtime = max(0.0, worked - expected) + # Tages-weise Berechnung: nur Tage mit Eintrag werden verglichen. + # Hintergrund: Fehl- und Urlaubstage sollen das Überstunden-Konto nicht + # senken – nur an Arbeitstagen wo tatsächlich gestempelt wurde, wird + # geprüft ob mehr als das Soll gearbeitet wurde. + daily_schedule = _schedule_daily(schedule) + + # Einträge nach Datum gruppieren (mehrere Einträge am gleichen Tag summieren) + hours_by_date: dict[date, float] = {} + for e in entries: + fac = _fza_factor(e.date) + hours_by_date[e.date] = hours_by_date.get(e.date, 0.0) + (e.worked_hours or 0.0) * fac + + overtime = 0.0 + for entry_date, worked_on_day in hours_by_date.items(): + expected_on_day = daily_schedule.get(entry_date.weekday(), 0.0) + overtime += max(0.0, worked_on_day - expected_on_day) bal.total_hours = Decimal(str(round(overtime, 2))) bal.last_calculated = datetime.utcnow()