diff --git a/ReportImp/LebenshilfeStadeMID/CustomMitarbeiterstundenkontoBerechnung.cs b/ReportImp/LebenshilfeStadeMID/CustomMitarbeiterstundenkontoBerechnung.cs index d8a87a68b..b2366a5af 100644 --- a/ReportImp/LebenshilfeStadeMID/CustomMitarbeiterstundenkontoBerechnung.cs +++ b/ReportImp/LebenshilfeStadeMID/CustomMitarbeiterstundenkontoBerechnung.cs @@ -11,8 +11,10 @@ namespace LebenshilfeStadeMID public class CustomMitarbeiterstundenkontoBerechnung : MitarbeiterstundenkontoBerechnung { private const int ANZ_WOCHENTAGE = 5; - private Dictionary _SollStundenProTagDict = new Dictionary(); //Speicher die Sollstunden aus den Betreuungszeiten des Kindes pro EmployeeOid und Tag (Key = {employee oid}_{datum]) + private Dictionary _SollStundenProTagDict = new Dictionary(); //Speicher die Sollstunden aus den Betreuungszeiten des Kindes pro EmployeeOid und Tag (Key = {employee oid}_{datum]) private DateTime _BerichtsAnfang = DateTime.MinValue; + // Claude: Sonderfall Timo Lafram – Cache CustomerOid -> ist dieser Klient (vermeidet wiederholtes Nachladen des Customers) + private readonly Dictionary _IstTimoLaframCache = new Dictionary(); protected override void StarteBerechnung(DateTime berichtsAnfang, DateTime berichtsEnde, MitarbeiterstundenkontoRO.EmployeeDetail empDetail) { @@ -273,6 +275,106 @@ namespace LebenshilfeStadeMID return dd; } + + // Claude: Sonderfall Klient "Timo Lafram" – für zusammenhängende Arbeitsblöcke, die (auch) Zeit für diesen + // Klienten enthalten, wird KEINE automatische Pause abgezogen. Alle übrigen Tage/Blöcke bleiben unverändert + // (Standardverhalten wie bisher = SONST IMMER Pause). + public override decimal CalculateBreakMinutesNeu(MitarbeiterstundenkontoRO.DayDetail dayDetail) + { + // Kein Timo-Lafram-Eintrag an diesem Tag -> unverändertes Standardverhalten + if (dayDetail.ServiceRecords == null || !dayDetail.ServiceRecords.Any(IstTimoLaframRecord)) + { + return base.CalculateBreakMinutesNeu(dayDetail); + } + + decimal addHours = 0; + + if (dayDetail.ZusammenhaengendeZeitraeume != null && dayDetail.ZusammenhaengendeZeitraeume.Count > 0) + { + foreach (var span in dayDetail.ZusammenhaengendeZeitraeume) + { + // Blöcke mit Timo-Lafram-Zeit: keine automatische Pause abziehen + if (SpanEnthaeltTimoLaframZeit(dayDetail, span.StartDateTime, span.EndDateTime)) + continue; + + addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, span.StartDateTime, span.EndDateTime) / 60; + } + + return addHours; + } + + // Fallback (keine ZusammenhaengendeZeitraeume) analog zum Standard, Timo-Lafram-Blöcke überspringen + if (dayDetail.EndDateTime1.HasValue && dayDetail.StartDateTime1.HasValue + && !SpanEnthaeltTimoLaframZeit(dayDetail, dayDetail.StartDateTime1.Value, dayDetail.EndDateTime1.Value)) + { + addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, dayDetail.StartDateTime1.Value, dayDetail.EndDateTime1.Value) / 60; + } + if (dayDetail.EndDateTime2.HasValue && dayDetail.StartDateTime2.HasValue + && !SpanEnthaeltTimoLaframZeit(dayDetail, dayDetail.StartDateTime2.Value, dayDetail.EndDateTime2.Value)) + { + addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, dayDetail.StartDateTime2.Value, dayDetail.EndDateTime2.Value) / 60; + } + if (dayDetail.EndDateTime3.HasValue && dayDetail.StartDateTime3.HasValue + && !SpanEnthaeltTimoLaframZeit(dayDetail, dayDetail.StartDateTime3.Value, dayDetail.EndDateTime3.Value)) + { + addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, dayDetail.StartDateTime3.Value, dayDetail.EndDateTime3.Value) / 60; + } + if (dayDetail.EndDateTime4.HasValue && dayDetail.StartDateTime4.HasValue + && !SpanEnthaeltTimoLaframZeit(dayDetail, dayDetail.StartDateTime4.Value, dayDetail.EndDateTime4.Value)) + { + addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, dayDetail.StartDateTime4.Value, dayDetail.EndDateTime4.Value) / 60; + } + + return addHours; + } + + // Claude: Prüft, ob im angegebenen Zeitraum (zusammenhängender Arbeitsblock) ein ServiceRecord für den + // Klienten "Timo Lafram" liegt. Die Blöcke werden aus den Startzeiten der Records gebildet, daher genügt + // die Prüfung, ob der Start eines Timo-Records innerhalb des Blocks liegt. + private bool SpanEnthaeltTimoLaframZeit(MitarbeiterstundenkontoRO.DayDetail dayDetail, DateTime spanStart, DateTime spanEnde) + { + if (dayDetail.ServiceRecords == null) + return false; + + foreach (var sr in dayDetail.ServiceRecords) + { + if (!IstTimoLaframRecord(sr)) + continue; + + if (sr.Start >= spanStart && sr.Start < spanEnde) + return true; + } + + return false; + } + + // Claude: Erkennt den Klienten "Timo Lafram" über den Namen (Vorname + Nachname, Groß/Klein und + // Leerzeichen unkritisch). Ergebnis wird je CustomerOid gecacht. + private bool IstTimoLaframRecord(StundenkontoServiceRecord sr) + { + if (sr == null || !sr.CustomerOid.HasValue) + return false; + + long oid = sr.CustomerOid.Value; + bool istTimo; + if (_IstTimoLaframCache.TryGetValue(oid, out istTimo)) + return istTimo; + + istTimo = false; + var customer = sr.Customer; + if (customer != null && customer.Person != null) + { + var vorname = (customer.Person.FirstName ?? "").Trim(); + var nachname = (customer.Person.LastName ?? "").Trim(); + + istTimo = vorname.Equals("Timo", StringComparison.OrdinalIgnoreCase) + && nachname.Equals("Lafram", StringComparison.OrdinalIgnoreCase); + } + + _IstTimoLaframCache[oid] = istTimo; + return istTimo; + } + public override decimal? GetSollStundenForAbsenceTime(MitarbeiterstundenkontoRO.EmployeeDetail ed, AbsenceTime at, DateTime datum) { var key = String.Format("{0}_{1:yyyyMMdd}", ed.Employee.Oid, datum);