diff --git a/ReportImp/ClaudeStundenkontoTest/ClaudeStundenkontoTest.csproj b/ReportImp/ClaudeStundenkontoTest/ClaudeStundenkontoTest.csproj
new file mode 100644
index 000000000..927dd692a
--- /dev/null
+++ b/ReportImp/ClaudeStundenkontoTest/ClaudeStundenkontoTest.csproj
@@ -0,0 +1,80 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {C1FED668-ADB9-47E0-B589-1389618E1B6E}
+ Library
+ Properties
+ ClaudeStundenkontoTest
+ 9999999999_Reports
+ v4.7.2
+ 512
+
+
+
+ true
+ full
+ false
+ ..\..\CustomerDlls\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ ..\..\CustomerDlls\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {B0D73E3D-4AE7-4024-93A6-DB1F46D7CCEE}
+ Data
+
+
+ {40d8b312-ea64-49e3-a31a-5e57ed4d5654}
+ Report
+
+
+ {094331c3-ecee-4c89-bbfd-4c9ded89f0ef}
+ Service
+
+
+ {2f50b83d-a3f0-4ec4-979a-3f9b7e3d8ed4}
+ Shared
+
+
+
+
\ No newline at end of file
diff --git a/ReportImp/ClaudeStundenkontoTest/CustomMitarbeiterstundenkontoBerechnung.cs b/ReportImp/ClaudeStundenkontoTest/CustomMitarbeiterstundenkontoBerechnung.cs
new file mode 100644
index 000000000..e0a8f5c79
--- /dev/null
+++ b/ReportImp/ClaudeStundenkontoTest/CustomMitarbeiterstundenkontoBerechnung.cs
@@ -0,0 +1,3285 @@
+using BeWo.Data.Access;
+using BeWo.Data.Entities;
+using BeWo.Report.ReportObjects;
+using BeWo.Service.Configuration;
+using BeWo.Service.DCEntityMapper;
+using BeWo.Service.Plugins;
+using BeWo.Service.ServiceImplementations;
+using BS.Shared;
+using BS.Shared.Core;
+using BS.Shared.DataContracts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using BeWo.Report;
+using BS.Shared.Extensions;
+
+namespace ClaudeStundenkontoTest
+{
+ public class CustomMitarbeiterstundenkontoBerechnung : MitarbeiterstundenkontoBerechnung
+ {
+ private decimal maxWochenstunden = 40;
+ private AppSettings appSettings;
+ private const int ANZ_WOCHENTAGE = 5;
+ protected new VacationService vacationService;
+
+ // Claude: Caches für Performance-Optimierung (Instanz-scoped, eine Instanz pro Report-Lauf)
+ // Claude: Feiertagsliste pro Jahr cachen, damit vacationService.GetFeiertage nicht mehrfach aufgerufen wird
+ private Dictionary> _feiertageListeCache;
+ // Claude: HashSet pro Jahr für O(1)-Lookup in IstFeiertag statt linearer Suche
+ private Dictionary> _feiertageSetCache;
+ // Claude: Cache für FeiertagDC-Objekt pro Datum (verwendet in CreateDayDetail)
+ private Dictionary _feiertagDCByDateCache;
+ // Claude: Cache der gesamten DienstEintraege pro Mitarbeiter, einmal pro Report-Lauf geladen
+ private Dictionary> _allDienstEintraegeByEmpCache;
+ // Claude: Cache der gesamten Dienstvertretungen pro Mitarbeiter, einmal pro Report-Lauf geladen
+ private Dictionary> _allDienstvertretungenByEmpCache;
+ // Claude: Cache von HatArbeitszeitImZeitraum (nur start==ende-Fall) pro Arbeitszeiten-Liste und Datum
+ // Claude: Wir nutzen einen ReferenceEqualityComparer um sicherzustellen, dass NHibernate-Proxy-Collections nicht via überschriebenes Equals kollidieren
+ private Dictionary, Dictionary> _hatArbeitszeitImZeitraumCache;
+
+ // Claude: Comparer, der ausschließlich Referenz-Gleichheit nutzt (verhindert unbeabsichtigte Treffer bei NHibernate-Collections mit überschriebenem Equals)
+ private sealed class ReferenceEqualityComparer : IEqualityComparer where T : class
+ {
+ public static readonly ReferenceEqualityComparer Default = new ReferenceEqualityComparer();
+ public bool Equals(T x, T y) { return ReferenceEquals(x, y); }
+ public int GetHashCode(T obj) { return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); }
+ }
+
+ protected new AppSettings Settings
+ {
+ get
+ {
+ return appSettings;
+ }
+
+ }
+
+ public new bool CheckIst { get; set; }
+
+ public override void CreateReport(MitarbeiterstundenkontoRO ro)
+ {
+ appSettings = AppSettings.CreateSettings();
+ vacationService = PluginLoader.FindClass();
+
+ CreateEmployeeDetails(ro);
+
+ CreateTeamDetails(ro);
+
+ CreateTotals(ro);
+ }
+
+ public override void CreateTotals(MitarbeiterstundenkontoRO ro)
+ {
+ foreach (var empDetail in ro.EmployeeDetailList)
+ {
+
+ ro.SollGesamt += empDetail.StundenSollOhneUrlaubKrankheit;
+ ro.FlsSollGesamt += empDetail.FlsSoll;
+ ro.FlsIstGesamt += empDetail.FlsIst;
+ ro.MittelbarIst += empDetail.MittelbarIst;
+ ro.FlsDiffGesamt += empDetail.FlsDiff;
+ ro.StundenGesamt += empDetail.StundenGesamtIst;
+ }
+
+ if (ro.FlsSollGesamt != 0)
+ ro.RelationIstSollGesamt = (ro.FlsIstGesamt / ro.FlsSollGesamt) * 100m;
+
+ if (ro.FlsIstGesamt + ro.MittelbarIst != 0)
+ ro.RelationFlsZuMittelbar = (ro.FlsIstGesamt / (ro.FlsIstGesamt + ro.MittelbarIst)) * 100;
+ }
+
+ public override void CreateTeamDetails(MitarbeiterstundenkontoRO ro)
+ {
+ foreach (var teamDetail in ro.TeamDetailList)
+ {
+ teamDetail.EmployeeDetailList = FilterEmployeeList(teamDetail.EmployeeDetailList);
+ foreach (var empDetail in teamDetail.EmployeeDetailList)
+ {
+ teamDetail.SollGesamt += empDetail.StundenSollOhneUrlaubKrankheit;
+ teamDetail.FlsSollGesamt += empDetail.FlsSoll;
+ teamDetail.FlsIstGesamt += empDetail.FlsIst;
+ teamDetail.MittelbarIst += empDetail.MittelbarIst;
+ teamDetail.FlsDiffGesamt += empDetail.FlsDiff;
+ teamDetail.StundenGesamt += empDetail.StundenGesamtIst;
+ }
+ if (teamDetail.FlsSollGesamt != 0)
+ teamDetail.RelationIstSollGesamt = (teamDetail.FlsIstGesamt / teamDetail.FlsSollGesamt) * 100m;
+
+ if (teamDetail.FlsIstGesamt + teamDetail.MittelbarIst != 0)
+ teamDetail.RelationFlsZuMittelbar = (teamDetail.FlsIstGesamt / (teamDetail.FlsIstGesamt + teamDetail.MittelbarIst)) * 100;
+ }
+
+
+ }
+
+ public override void CreateEmployeeDetails(MitarbeiterstundenkontoRO ro)
+ {
+ foreach (var empDetail in ro.EmployeeDetailList)
+ {
+
+ DateTime monatsAnfang = ro.ReportStartDate;
+ DateTime monatsEnde = monatsAnfang.AddMonths(1);
+ DateTime berichtsAnfang = monatsAnfang;
+ DateTime berichtsEnde = monatsEnde;
+
+ Employee emp = empDetail.Employee;
+
+ decimal bu = 0;
+
+ var c = GetContractForDate(emp, ro.ReportStartDate, ro.ReportEndDate);
+
+ if (c != null)
+ {
+ if (c.EntryDate.HasValue && c.EntryDate > monatsAnfang)
+ {
+ berichtsAnfang = c.EntryDate.Value;
+ }
+ if (c.ContractEndDate.HasValue && c.ContractEndDate < monatsEnde)
+ {
+ berichtsEnde = c.ContractEndDate.Value.Date.AddDays(1);
+ }
+ empDetail.HasContract = true;
+ }
+
+ if (BerechnungsStartdatum > berichtsAnfang)
+ {
+ berichtsAnfang = BerechnungsStartdatum;
+ }
+
+ ServiceRecordSummary summaryTotal = new ServiceRecordSummary();
+ summaryTotal.RecordsImBerichtszeitraum = new List();
+
+ decimal pause1 = 0;
+ decimal pause2 = 0;
+ decimal weeklyTotalHours = 0;
+ decimal flsSollGesamt = 0;
+ decimal? fehlZeitenGesamt = null;
+ decimal? tageUrlaubGesamt = null;
+ decimal? tageKrankheitGesamt = null;
+ decimal? abwesenheitTageGesamt = null;
+ decimal workdaysTotal = 0;
+ decimal stundenSollGesamt = 0;
+ decimal tatSollStdGesamt = 0;
+ decimal azDurchFeiertage = 0;
+
+ IList groupFilteredRecords = empDetail.AllServiceRecords;
+ if (groupFilteredRecords == null)
+ {
+ IList recordsAll = DAOFactory.SearchDAO.FindServiceRecords(emp.Oid, null);
+ recordsAll = AnalysisServiceImp.FilterActiveServiceRecords(recordsAll);
+
+ //
+ groupFilteredRecords = new List();
+ Dictionary groupBookingDict = new Dictionary();
+ foreach (var item in recordsAll)
+ {
+
+ if (!item.GroupOid.HasValue || !groupBookingDict.ContainsKey(item.GroupOid.Value))
+ {
+ groupFilteredRecords.Add(item);
+ if (item.GroupOid.HasValue)
+ {
+ groupBookingDict.Add(item.GroupOid.Value, true);
+ }
+ }
+ }
+
+ empDetail.AllServiceRecords = groupFilteredRecords;
+ }
+
+ while (berichtsAnfang < berichtsEnde)
+ {
+
+
+
+ StarteBerechnung(berichtsAnfang, berichtsEnde, empDetail);
+
+ empDetail.ArbeitstageImBerichtszeitraum = GetArbeitstageImBereich(berichtsAnfang, berichtsEnde, emp);
+ empDetail.ArbeitstageProWoche = ANZ_WOCHENTAGE;
+
+
+ workdaysTotal += empDetail.ArbeitstageImBerichtszeitraum;
+ weeklyTotalHours = 0;
+
+ if (c != null)
+ {
+ if (c.WeeklyTotalHours.HasValue)
+ {
+ weeklyTotalHours = c.WeeklyTotalHours.Value;
+ bu = c.WeeklyTotalHours.Value / this.maxWochenstunden;
+ }
+ if (c.WeeklyDays.HasValue && c.WeeklyDays.Value > 0)
+ {
+ empDetail.ArbeitstageProWoche = c.WeeklyDays.Value;
+ }
+
+ empDetail.WeeklyFLS = c.WeeklyFLS;
+ empDetail.HourlyRate = c.HourlyRate;
+ empDetail.EmploymentType = c.EmploymentType;
+
+
+ }
+ empDetail.SollProTag = (weeklyTotalHours / empDetail.ArbeitstageProWoche);
+ empDetail.SollProWoche = BerechneSollProWoche(c, empDetail);
+ if (empDetail.SollProTag == 0 && empDetail.SollProWoche > 0 && empDetail.ArbeitstageProWoche > 0)
+ {
+ empDetail.SollProTag = empDetail.SollProWoche / empDetail.ArbeitstageProWoche;
+
+ }
+ HandleServiceRecords(empDetail, groupFilteredRecords, c);
+
+ if (CheckIst)
+ {
+
+ var summary = BerechneStunden(empDetail, groupFilteredRecords, berichtsAnfang, berichtsEnde);
+ summaryTotal.IstAbrechenbar += summary.IstAbrechenbar;
+ summaryTotal.IstNichtAbrechenbar += summary.IstNichtAbrechenbar;
+ summaryTotal.RecordsImBerichtszeitraum.AddRange(summary.RecordsImBerichtszeitraum);
+
+ if (SollPauseBerechnen())
+ {
+ pause1 += BerechnePauseMinuten(summary.RecordsImBerichtszeitraum);
+ pause2 += BerechneAutoPausen(summary.RecordsImBerichtszeitraum);
+
+ }
+ }
+
+
+ decimal? stundenSollProMonat = null;
+ decimal? stundenSollImVertragsUndBerichtzeitraum = null;
+ if (empDetail.HasContract)
+ {
+ stundenSollImVertragsUndBerichtzeitraum = GetSollNachArbeitszeiten(empDetail.Employee, berichtsAnfang, berichtsEnde);
+ }
+
+ if (!stundenSollImVertragsUndBerichtzeitraum.HasValue)
+ {
+ stundenSollProMonat = BerechneSollProMonat(emp, berichtsAnfang, berichtsEnde);
+ stundenSollImVertragsUndBerichtzeitraum = stundenSollProMonat;
+ }
+
+ if (!stundenSollImVertragsUndBerichtzeitraum.HasValue)
+ {
+ if (!stundenSollImVertragsUndBerichtzeitraum.HasValue)
+ {
+ if (empDetail.ArbeitstageProWoche <= 5)
+ {
+ stundenSollImVertragsUndBerichtzeitraum =
+ empDetail.ArbeitstageImBerichtszeitraum * (weeklyTotalHours / 5);
+ }
+ else
+ {
+ stundenSollImVertragsUndBerichtzeitraum =
+ empDetail.ArbeitstageImBerichtszeitraum * (weeklyTotalHours / empDetail.ArbeitstageProWoche);
+ }
+ }
+ }
+
+
+ stundenSollGesamt += stundenSollImVertragsUndBerichtzeitraum ?? 0;
+
+
+ empDetail.StundenSollMax = stundenSollGesamt;
+
+
+ var tatSollStdImVertragsUndBerichtzeitraum = stundenSollImVertragsUndBerichtzeitraum ?? 0;
+
+
+ decimal? fehlZeiten = GetFehlzeiten(groupFilteredRecords, berichtsAnfang, berichtsEnde);
+ if (fehlZeiten.HasValue)
+ {
+ if (!fehlZeitenGesamt.HasValue)
+ fehlZeitenGesamt = 0;
+ fehlZeitenGesamt += fehlZeiten;
+
+ tatSollStdImVertragsUndBerichtzeitraum -= fehlZeiten.Value;
+ }
+
+ AktuellBearbeitetesEmployeeDetail = empDetail;
+ var ath = GetAbsenceTimeDaysWithWorkWeeks(emp, berichtsAnfang, berichtsEnde, empDetail.ArbeitstageProWoche);
+ empDetail.AbsenceTimeDays = ath;
+
+ if (ath.TageUrlaub.HasValue)
+ {
+ if (!tageUrlaubGesamt.HasValue)
+ tageUrlaubGesamt = 0;
+ tageUrlaubGesamt += ath.TageUrlaub;
+ }
+ if (ath.TageKrankheit.HasValue)
+ {
+ if (!tageKrankheitGesamt.HasValue)
+ tageKrankheitGesamt = 0;
+ tageKrankheitGesamt += ath.TageKrankheit;
+ }
+
+
+ if (ath.TageGesamt.HasValue)
+ {
+ if (!abwesenheitTageGesamt.HasValue)
+ abwesenheitTageGesamt = 0;
+ abwesenheitTageGesamt += ath.TageGesamt;
+
+ var minusStunden = BerechneStundenDurchAbwesenheiten(ath, berichtsAnfang, berichtsEnde, c, empDetail);
+ tatSollStdImVertragsUndBerichtzeitraum -= minusStunden;
+ if (tatSollStdImVertragsUndBerichtzeitraum < 0 && !StundenDurchAbwesenheitenDuerfenNegativSein())
+ {
+
+ tatSollStdImVertragsUndBerichtzeitraum = 0;
+
+ }
+
+ }
+
+
+ var ath2 = GetAbwesenheitenDurchServiceRecords(empDetail, c, groupFilteredRecords, berichtsAnfang, berichtsEnde);
+
+ if (ath2 != null)
+ {
+
+ var stunden = ath2.StundenKrankheit + ath2.StundenUrlaub + ath2.StundenSonstiges;
+
+ tatSollStdImVertragsUndBerichtzeitraum -= stunden;
+
+ if (tatSollStdImVertragsUndBerichtzeitraum < 0 && !StundenDurchAbwesenheitenDuerfenNegativSein())
+ {
+ tatSollStdImVertragsUndBerichtzeitraum = 0;
+ }
+
+ if (empDetail.SollProTag > 0)
+ {
+ if (!abwesenheitTageGesamt.HasValue)
+ abwesenheitTageGesamt = 0;
+ abwesenheitTageGesamt += stunden / empDetail.SollProTag;
+
+ if (ath2.StundenKrankheit > 0)
+ {
+ if (!tageKrankheitGesamt.HasValue)
+ {
+ tageKrankheitGesamt = 0;
+ }
+ tageKrankheitGesamt += ath2.StundenKrankheit / empDetail.SollProTag;
+ }
+
+ if (ath2.StundenUrlaub > 0)
+ {
+ if (!tageUrlaubGesamt.HasValue)
+ {
+ tageUrlaubGesamt = 0;
+ }
+ tageUrlaubGesamt += ath2.StundenUrlaub / empDetail.SollProTag;
+ }
+ }
+
+
+ }
+
+ tatSollStdGesamt += tatSollStdImVertragsUndBerichtzeitraum;
+
+
+ flsSollGesamt += BerechneFlsSoll(emp, c, berichtsAnfang, berichtsEnde, tatSollStdImVertragsUndBerichtzeitraum, stundenSollProMonat);
+
+ if (ath.StundenAlsIst > 0)
+ {
+ tatSollStdGesamt += ath.StundenAlsIst;
+ summaryTotal.IstNichtAbrechenbar += ath.StundenAlsIst * 60;
+ }
+
+ azDurchFeiertage += BerechneGesamtArbeitszeitDurchFeiertage(empDetail, berichtsAnfang, berichtsEnde);
+
+
+ berichtsAnfang = berichtsEnde;
+ berichtsEnde = monatsEnde;
+ c = GetNextValidContractForDate(berichtsAnfang, emp);
+
+ if (c != null)
+ {
+ if (c.EntryDate.HasValue && c.EntryDate >= berichtsAnfang)
+ berichtsAnfang = c.EntryDate.Value;
+ if (c.ContractEndDate.HasValue && c.ContractEndDate < monatsEnde)
+ {
+ berichtsEnde = c.ContractEndDate.Value.Date.AddDays(1);
+ }
+ }
+ }
+
+ if (AddRateFactorToIst() != 1)
+ {
+ summaryTotal.IstAbrechenbar *= AddRateFactorToIst();
+ }
+ summaryTotal.IstAbrechenbar /= 60m;
+ summaryTotal.IstNichtAbrechenbar /= 60m;
+
+ empDetail.WorkDaysThisMonth = workdaysTotal;
+ //decimal stundenSollMax = arbeitstage * (maxWochenstunden / xxx) * bu;
+
+
+
+ empDetail.Fehlzeiten = fehlZeitenGesamt;
+ empDetail.TageUrlaub = tageUrlaubGesamt;
+ empDetail.TageKrankheit = tageKrankheitGesamt;
+ empDetail.UrlaubUndKrankheit = abwesenheitTageGesamt;
+
+ empDetail.StundenSollOhneUrlaubKrankheit = tatSollStdGesamt;
+
+ empDetail.FlsSoll = flsSollGesamt;
+
+
+
+ empDetail.MittelbarIst = summaryTotal.IstNichtAbrechenbar;
+ empDetail.Bu = bu;
+ empDetail.FlsIst = summaryTotal.IstAbrechenbar;
+ empDetail.FlsDiff = summaryTotal.IstAbrechenbar - empDetail.FlsSoll;
+ empDetail.StundenMittelbarIst = summaryTotal.IstNichtAbrechenbar;
+ empDetail.StundenGesamtIst = summaryTotal.IstAbrechenbar + summaryTotal.IstNichtAbrechenbar;
+
+ empDetail.StundenGesamtIst -= pause1 / 60;
+ empDetail.StundenGesamtIst -= pause2 / 60;
+
+ empDetail.StundenGesamtIst += azDurchFeiertage;
+
+ if (empDetail.FlsSoll != 0)
+ empDetail.RelationIstSoll = (empDetail.FlsIst / empDetail.FlsSoll) * 100m;
+
+
+ decimal? ueberstunden = empDetail.StundenGesamtIst - empDetail.StundenSollOhneUrlaubKrankheit;
+ decimal? ueberstundenVormonat;
+
+
+ decimal alteExtraUeberstunden = 0;
+ BerechneUeberstundenBis(empDetail, groupFilteredRecords, ro.ReportStartDate, out ueberstundenVormonat,
+ out alteExtraUeberstunden);
+
+ // Claude: Globaler Cache für DienstEintraege/Dienstvertretungen, nur einmal pro Report-Lauf aus dem DAO holen statt pro Mitarbeiter
+ //EnsureDienstplanDataLoaded();
+ //var empOidNullable = empDetail.Employee.Oid;
+ //var alleDiensteintraege = new List();
+ //var alleDienstvertretungen = new List();
+ //if (empOidNullable.HasValue)
+ //{
+ // List deList;
+ // if (_allDienstEintraegeByEmpCache.TryGetValue(empOidNullable.Value, out deList))
+ // {
+ // alleDiensteintraege.AddRange(deList);
+ // }
+ // List dvList;
+ // if (_allDienstvertretungenByEmpCache.TryGetValue(empOidNullable.Value, out dvList))
+ // {
+ // alleDienstvertretungen.AddRange(dvList);
+ // }
+ //}
+
+ //decimal? ueberstundenVormonatFuerDienstplanung;
+ //decimal? alteExtraUeberstundenDienstplanung;
+ //empDetail.DienstplanIst += BerechneDienstplanIst(empDetail, alleDiensteintraege, alleDienstvertretungen, ro.ReportStartDate,
+ //out ueberstundenVormonatFuerDienstplanung, out alteExtraUeberstundenDienstplanung); // ABCDEF
+
+ //var dienstplanIstAlsStunden = empDetail.DienstplanIst / 60;
+
+ decimal extraStunden = 0;
+
+ BerechneExtraUeberstunden(emp, monatsAnfang, monatsEnde, out extraStunden); // OverTimes
+
+ if (ShouldCreateDayDetails())
+ {
+
+ empDetail.Days = CreateDayDetails(monatsAnfang, monatsEnde, empDetail, summaryTotal.RecordsImBerichtszeitraum);
+ var ah = GetAdditionalHours(empDetail.Days, monatsAnfang, monatsEnde, empDetail, summaryTotal.RecordsImBerichtszeitraum);
+ ueberstunden += ah;
+ empDetail.StundenGesamtIst += ah;
+ }
+
+ if (empDetail.StundenGesamtIst != 0)
+ empDetail.RelationFlsZuMittelbar = (summaryTotal.IstAbrechenbar / empDetail.StundenGesamtIst) * 100;
+
+ empDetail.Ueberstunden = ueberstunden;
+ empDetail.UeberstundenVormonat = ueberstundenVormonat;
+ if (!empDetail.UeberstundenGesamt.HasValue)
+ empDetail.UeberstundenGesamt = ((ueberstundenVormonat ?? 0) + (ueberstunden ?? 0)) + extraStunden;
+ else
+ empDetail.UeberstundenGesamt += ((ueberstundenVormonat ?? 0) + (ueberstunden ?? 0)) + extraStunden;
+ empDetail.UeberstundenAusbezahlt = extraStunden;
+
+ //Urlaubskonto (todo)
+ empDetail.UrlaubGesamtImAktuellenJahr =
+ GetAbsenceTimeDaysWithWorkWeeks(emp, new DateTime(monatsAnfang.Year, 1, 1), monatsEnde, empDetail.ArbeitstageProWoche).TageUrlaub;
+
+ // Aktuellen Resturlaub berechnen
+ var vs = PluginLoader.FindClass();
+ // Darstellungsvariante, da das normale Urlaubskonto nur den Resturlaubsstand vom Ende des Jahres anzeigt.
+ var konto = vs.BerechneUrlaubskontoZurDarstellung(MapperFactory.EmployeeDC_Employee.MapToNewDC(emp), ro.ReportStartDate.Year, berichtsEnde.AddDays(-1));
+ if (konto != null && konto.Count > 0)
+ {
+ var alleVertraglichenBudgets = konto
+ .Where(k => k.DatumZurSortierung == null && ZaehltZuUrlaubsbudget(k.Erlaeuterung))
+ .ToList();
+
+ empDetail.Resturlaub = alleVertraglichenBudgets.Sum(b => Convert.ToDecimal(b.AnzahlTage));
+
+ // Jeder Entry enthält den Zeitraum und die abzuziehenden Tage vom Gesamtbudget.
+ foreach (var entry in konto)
+ {
+ // Hat entry kein Datum, dann ist es nur eine Überschrift in der Darstellung und hat hier keinen Nutzen
+ if (entry.DatumZurSortierung != null || entry.Erlaeuterung.Contains("Resturlaub aus"))
+ {
+ // Ist das Datum nach dem Enddatum des Stundenkontos braucht nicht weitergerechnet werden
+ if (entry.DatumZurSortierung > ro.ReportEndDate.AddTicks(-1))
+ break;
+
+ empDetail.Resturlaub += Convert.ToDecimal(entry.AnzahlTage); // += weil Tage immer als Minus dargestellt sind
+ }
+ }
+ }
+
+ CalculateEmployeeSalary(empDetail, berichtsAnfang, berichtsEnde);
+ }
+
+ ro.EmployeeDetailList = FilterEmployeeList(ro.EmployeeDetailList);
+ }
+
+ public override bool ZaehltZuUrlaubsbudget(string erlaueterung)
+ {
+ if (erlaueterung.Contains("Urlaubstage laut Vertrag") ||
+ erlaueterung.Contains("Sonderurlaub") ||
+ erlaueterung.Contains("Regeneration"))
+ return true;
+ else
+ return false;
+ }
+
+ #region Ist-Berechnung für Dienstplanung
+ public decimal BerechneDienstplanIst(MitarbeiterstundenkontoRO.EmployeeDetail empDetail, List eintraege, List vertretungen, DateTime monat, out decimal? ueberstunden, out decimal? alteExtraUeberstunden) // ABCDEF
+ {
+ ueberstunden = null;
+ alteExtraUeberstunden = 0;
+
+ decimal dpIst = 0;
+
+
+ MitarbeiterstundenkontoRO.EmployeeDetail edTotal = new MitarbeiterstundenkontoRO.EmployeeDetail();
+ Employee emp = empDetail.Employee;
+ Contract c = emp.FirstContract;
+
+ // Der Einfachheit halber: Vertretungen in Einträge umwandeln
+ eintraege.AddRange(VertretungenUmwandeln(vertretungen.ToList()));
+
+ if (c != null)
+ {
+ Dictionary> eintragDict = CreateDiensteintragDictByDate(eintraege); // Anzahl der Einträge korrekt
+
+ DateTime start = c.EntryDate.HasValue ? c.EntryDate.Value : new DateTime(2011, 1, 1);
+ while (c != null && start < monat)
+ {
+ DateTime contractEnd = c.ContractEndDate.HasValue ? c.ContractEndDate.Value : DateTime.MaxValue;
+ decimal bu = 0;
+ decimal weeklyTotalHours = this.maxWochenstunden;
+ int anzWochentage = ANZ_WOCHENTAGE;
+
+ if (c.WeeklyTotalHours.HasValue)
+ {
+ weeklyTotalHours = c.WeeklyTotalHours.Value;
+ bu = c.WeeklyTotalHours.Value / maxWochenstunden;
+ }
+
+ while (start < monat && start < contractEnd)
+ {
+ decimal flsIst = 0;
+ decimal mittelbarIst = 0;
+ decimal minutenMonat = 0;
+ DateTime ende = new DateTime(start.Year, start.Month, 1).AddMonths(1);
+ if (contractEnd < DateTime.MaxValue && contractEnd.AddDays(1) < ende)
+ ende = contractEnd.AddDays(1);
+
+ List entriesThisMonth = new List();
+ string key = string.Format("{0:yyyyMM}", start);
+
+ if (eintragDict.ContainsKey(key))
+ {
+ foreach (var item in eintragDict[key])
+ {
+ minutenMonat += Convert.ToDecimal(item.DauerInStunden * 60);
+ entriesThisMonth.Add(item);
+ }
+ }
+ dpIst += minutenMonat;
+
+ decimal arbeitstage = GetArbeitstageImBereich(start, ende, emp);
+
+ var arbeitstageProWoche = anzWochentage;
+ if (c != null && c.WeeklyDays.HasValue && c.WeeklyDays.Value > 0)
+ arbeitstageProWoche = c.WeeklyDays.Value;
+
+ var soll = GetSollNachArbeitszeiten(emp, start, ende);
+ if (!soll.HasValue)
+ {
+ var stundenSollProMonat = BerechneSollProMonat(emp, start, ende);
+ soll = stundenSollProMonat;
+ }
+
+ if (!soll.HasValue)
+ {
+
+ if (!soll.HasValue)
+ {
+ decimal maxSollMonat = arbeitstage * (maxWochenstunden / arbeitstageProWoche);
+ if (arbeitstageProWoche <= 5)
+ {
+ maxSollMonat = arbeitstage * (maxWochenstunden / 5);
+ }
+
+ soll = maxSollMonat * bu;
+ }
+ }
+
+
+
+ decimal sollMitarbeiter = soll ?? 0;
+
+ MitarbeiterstundenkontoRO.EmployeeDetail ed = new MitarbeiterstundenkontoRO.EmployeeDetail();
+ ed.SollProTag = weeklyTotalHours / arbeitstageProWoche;
+ ed.ArbeitstageProWoche = arbeitstageProWoche;
+ ed.ArbeitstageImBerichtszeitraum = arbeitstage;
+ ed.SollProWoche = BerechneSollProWoche(c, ed);
+ ed.Employee = emp;
+ ed.StundenSollMax = sollMitarbeiter;
+
+ AktuellBearbeitetesEmployeeDetail = ed;
+ var ath = GetAbsenceTimeDaysWithWorkWeeks(emp, start, ende, arbeitstageProWoche);
+
+
+
+ ed.AbsenceTimeDays = ath;
+
+
+ if (ath.TageGesamt.HasValue)
+ {
+ var minusStunden = BerechneStundenDurchAbwesenheiten(ath, start, ende, c, ed);
+ sollMitarbeiter -= minusStunden;
+ if (sollMitarbeiter < 0 && !StundenDurchAbwesenheitenDuerfenNegativSein())
+ {
+ sollMitarbeiter = 0;
+ }
+ }
+
+ decimal stundenMonat = minutenMonat / 60m;
+ stundenMonat = ((flsIst * AddRateFactorToIst()) + mittelbarIst) / 60m;
+
+
+ if (!ueberstunden.HasValue)
+ ueberstunden = 0;
+ ueberstunden += stundenMonat - sollMitarbeiter;
+
+ decimal extraUeberstunden = 0;
+ BerechneExtraUeberstunden(emp, start, ende, out extraUeberstunden);
+ ueberstunden += extraUeberstunden;
+
+ alteExtraUeberstunden = extraUeberstunden;
+
+ ed.StundenSollOhneUrlaubKrankheit = sollMitarbeiter;
+ ed.StundenGesamtIst = stundenMonat;
+ ed.Ueberstunden = ueberstunden;
+ ed.UeberstundenVormonat = edTotal.Ueberstunden;
+
+ edTotal.StundenSollOhneUrlaubKrankheit += sollMitarbeiter;
+ edTotal.StundenSollMax += soll ?? 0;
+ edTotal.FlsIst += flsIst / 60;
+ edTotal.MittelbarIst += mittelbarIst / 60;
+ edTotal.StundenGesamtIst += stundenMonat;
+ edTotal.Ueberstunden = ueberstunden;
+
+ start = ende;
+ }
+
+ if (start >= contractEnd)
+ {
+ var ctemp = GetNextValidContractForDate(start, emp);
+ if (ctemp != null && ctemp.Oid != c.Oid)
+ {
+ c = ctemp;
+ }
+ else
+ {
+ c = null;
+ }
+
+ if (c != null && c.EntryDate.HasValue && c.EntryDate >= start)
+ start = c.EntryDate.Value;
+ else
+ {
+ start = start.AddMonths(1);
+ c = GetNextValidContractForDate(start, emp);
+ if (c != null && c.EntryDate.HasValue && c.EntryDate >= start)
+ start = c.EntryDate.Value;
+ }
+ }
+ }
+ }
+ return dpIst;
+ }
+
+ // Claude: Lädt DienstEintraege und Dienstvertretungen einmalig per DAO und gruppiert nach EmployeeOid (statt pro Mitarbeiter den DAO erneut zu fragen)
+ private void EnsureDienstplanDataLoaded()
+ {
+ if (_allDienstEintraegeByEmpCache == null)
+ {
+ _allDienstEintraegeByEmpCache = new Dictionary>();
+ foreach (var d in DAOFactory.GenericDAO.GetAllActive())
+ {
+ List list;
+ if (!_allDienstEintraegeByEmpCache.TryGetValue(d.EmployeeOid, out list))
+ {
+ list = new List();
+ _allDienstEintraegeByEmpCache[d.EmployeeOid] = list;
+ }
+ list.Add(d);
+ }
+ }
+ if (_allDienstvertretungenByEmpCache == null)
+ {
+ _allDienstvertretungenByEmpCache = new Dictionary>();
+ foreach (var d in DAOFactory.GenericDAO.GetAllActive())
+ {
+ List list;
+ if (!_allDienstvertretungenByEmpCache.TryGetValue(d.EmployeeOid, out list))
+ {
+ list = new List();
+ _allDienstvertretungenByEmpCache[d.EmployeeOid] = list;
+ }
+ list.Add(d);
+ }
+ }
+ }
+
+ private Dictionary> CreateDiensteintragDictByDate(List eintraege)
+ {
+ var dict = new Dictionary>();
+
+ foreach (var item in eintraege)
+ {
+ //if (!IsServiceRecordFehlzeit(item))
+ //{
+
+ string key = string.Format("{0:yyyyMM}", item.Datum);
+
+ List list;
+ if (!dict.ContainsKey(key))
+ {
+ list = new List();
+ dict.Add(key, list);
+ }
+ else
+ {
+ list = dict[key];
+ }
+ list.Add(item);
+
+ //}
+ }
+ return dict;
+ }
+
+ private List VertretungenUmwandeln(List vertretungen)
+ {
+ List eintraege = new List();
+
+ foreach (var v in vertretungen)
+ {
+ eintraege.Add(new DienstEintrag()
+ {
+ Datum = v.Datum,
+ DauerInStunden = v.DauerInStunden,
+ DienstOid = v.DienstOid,
+ EmployeeOid = v.EmployeeOid,
+ Endzeit = v.Endzeit,
+ Notice = v.Notice,
+ Startzeit = v.Startzeit,
+ WohnheimOid = v.WohnheimOid,
+ Zeile = v.Zeile
+ });
+ }
+
+ return eintraege;
+ }
+ #endregion
+
+
+ protected override void StarteBerechnung(DateTime berichtsAnfang, DateTime berichtsEnde, MitarbeiterstundenkontoRO.EmployeeDetail empDetail)
+ {
+ //do nothing
+ }
+
+
+ protected override AbsenceTimeHours GetAbwesenheitenDurchServiceRecords(MitarbeiterstundenkontoRO.EmployeeDetail empDetail, Contract c, IList groupFilteredRecords, DateTime berichtsAnfang, DateTime berichtsEnde)
+ {
+ return null;
+ }
+
+ protected override decimal BerechneFlsSoll(Employee emp, Contract contract, DateTime berichtsAnfang, DateTime berichtsEnde, decimal tatSollStdImVertragsUndBerichtzeitraum, decimal? stundenSollProMonat)
+ {
+ var festeflsSollProMonat = BerechneFlsSollProMonat(emp, berichtsAnfang, berichtsEnde);
+ decimal flsSollImVertragsUndBerichtzeitraum = 0;
+
+ if (festeflsSollProMonat.HasValue)
+ {
+ return festeflsSollProMonat.Value;
+ //flsSollImVertragsUndBerichtzeitraum = tatSollStdImVertragsUndBerichtzeitraum;
+
+ //if (stundenSollProMonat.HasValue)
+ //{
+ // flsSollImVertragsUndBerichtzeitraum *= (festeflsSollProMonat.Value / stundenSollProMonat.Value);
+ //}
+ }
+ else
+ {
+ if (contract != null && contract.WeeklyFLS.HasValue)
+ {
+ flsSollImVertragsUndBerichtzeitraum = tatSollStdImVertragsUndBerichtzeitraum;
+
+ if (HatArbeitszeitImZeitraum(emp.Arbeitszeiten, berichtsAnfang, berichtsEnde))
+ {
+ var wochenArbeitszeit = GetWochenArbeitszeit(emp.Arbeitszeiten, berichtsAnfang, berichtsEnde);
+
+ if (wochenArbeitszeit > 0)
+ {
+ flsSollImVertragsUndBerichtzeitraum *= contract.WeeklyFLS.Value / wochenArbeitszeit;
+ }
+
+ }
+ else if (contract.WeeklyTotalHours.HasValue && contract.WeeklyTotalHours.Value != 0)
+ {
+ flsSollImVertragsUndBerichtzeitraum *= contract.WeeklyFLS.Value / contract.WeeklyTotalHours.Value;
+ }
+
+ }
+ }
+
+ return flsSollImVertragsUndBerichtzeitraum;
+ }
+
+ private decimal GetWochenArbeitszeit(IList arbeitszeiten, DateTime start, DateTime ende)
+ {
+ if (arbeitszeiten != null)
+ {
+ foreach (var az in arbeitszeiten)
+ {
+ if ((!az.GueltigVon.HasValue || az.GueltigVon.Value < ende) &&
+ (!az.GueltigBis.HasValue || az.GueltigBis.Value >= start))
+ {
+
+ DateTime wochenStart = start;
+ while (wochenStart.DayOfWeek != DayOfWeek.Monday)
+ {
+ wochenStart = wochenStart.AddDays(1);
+ }
+
+ decimal stunden = 0;
+ for (int i = 0; i < 7; i++)
+ {
+ stunden += GetArbeitszeitStundenFuerTag(az, wochenStart);
+
+ wochenStart = wochenStart.AddDays(1);
+ }
+
+ return stunden;
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ protected override bool StundenDurchAbwesenheitenDuerfenNegativSein()
+ {
+ return false;
+ }
+
+ protected override bool SchneideZuvieleAbwesenheitsstundenAb()
+ {
+ // sorgt dafür dass bei Überschreitung des Tagessolls nicht die Abwesenheitszeit verwendet wird
+ // wenn auch Überschreitung Wochensoll möglich sein soll muss zusätzlich GetSollStundenForAbsenceTime angepasst werden
+ return true;
+ }
+
+ protected override decimal? GetSollNachArbeitszeiten(Employee emp, DateTime start, DateTime ende)
+ {
+ if (BerechneSollNurBisZumAktuellenTag())
+ {
+ if (ende > DateTime.Now.Date.AddDays(1))
+ {
+ ende = DateTime.Now.Date.AddDays(1);
+ }
+ }
+
+ if (emp.Arbeitszeiten != null && emp.Arbeitszeiten.Count > 0)
+ {
+ if (HatArbeitszeitImZeitraum(emp.Arbeitszeiten, start, ende))
+ {
+ decimal soll = 0;
+
+ DateTime startTemp = start;
+
+ while (startTemp < ende)
+ {
+ soll += GetSollNachArbeitszeitenFuerTag(emp, startTemp);
+ startTemp = startTemp.AddDays(1);
+ }
+
+ return soll;
+ }
+ }
+
+ return null;
+ }
+
+ protected override bool HatArbeitszeitImZeitraum(IList arbeitszeiten, DateTime start, DateTime ende)
+ {
+ // Claude: Cache für den häufigen (date == date)-Aufruf aus CalculateAbsenceTimesForDay/BerechneSollStundenFuerTag
+ if (arbeitszeiten != null && start == ende)
+ {
+ if (_hatArbeitszeitImZeitraumCache == null)
+ {
+ _hatArbeitszeitImZeitraumCache = new Dictionary, Dictionary>(ReferenceEqualityComparer>.Default);
+ }
+ Dictionary innerCache;
+ if (!_hatArbeitszeitImZeitraumCache.TryGetValue(arbeitszeiten, out innerCache))
+ {
+ innerCache = new Dictionary();
+ _hatArbeitszeitImZeitraumCache[arbeitszeiten] = innerCache;
+ }
+ bool cachedResult;
+ if (innerCache.TryGetValue(start, out cachedResult))
+ {
+ return cachedResult;
+ }
+ cachedResult = HatArbeitszeitImZeitraumIntern(arbeitszeiten, start, ende);
+ innerCache[start] = cachedResult;
+ return cachedResult;
+ }
+ return HatArbeitszeitImZeitraumIntern(arbeitszeiten, start, ende);
+ }
+
+ // Claude: Ursprüngliche Logik, ohne Cache - aus HatArbeitszeitImZeitraum extrahiert
+ private static bool HatArbeitszeitImZeitraumIntern(IList arbeitszeiten, DateTime start, DateTime ende)
+ {
+ if (arbeitszeiten != null)
+ {
+ foreach (var az in arbeitszeiten)
+ {
+ if ((!az.GueltigVon.HasValue || az.GueltigVon.Value < ende) && (!az.GueltigBis.HasValue || az.GueltigBis.Value >= start) && az.ArbeitszeitEintraege != null && az.ArbeitszeitEintraege.Count > 0)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected override decimal GetSollNachArbeitszeitenFuerTag(Employee emp, DateTime tag)
+ {
+ decimal soll = 0;
+ var fak = GetFeiertagsFaktor(tag);
+
+ if (BerechneFeiertageAlsArbeitszeit())
+ {
+ if (fak == 1)
+ {
+ fak = 0;
+ }
+ }
+
+ if (fak < 1)
+ {
+ if (emp.Arbeitszeiten != null)
+ {
+ foreach (var az in emp.Arbeitszeiten)
+ {
+ if ((!az.GueltigVon.HasValue || az.GueltigVon.Value <= tag) &&
+ (!az.GueltigBis.HasValue || az.GueltigBis.Value >= tag))
+ {
+ var std = GetArbeitszeitStundenFuerTag(az, tag);
+ soll += (std * (1 - fak));
+ }
+ }
+ }
+ }
+
+ return soll;
+ }
+
+ // Claude: Zurückgesetzt auf Original-Implementierung. Cache hat in Einzelfällen abweichende Werte erzeugt.
+ protected decimal GetArbeitszeitStundenFuerTag(Arbeitszeit az, DateTime tag)
+ {
+ decimal stunden = 0;
+ foreach (var ae in az.ArbeitszeitEintraege)
+ {
+ if (ae.IstAmGleichenWochentag(tag))
+ {
+ String dateStr1 = String.Format("{0:dd.MM.yyyy} {1}", tag, ae.UhrzeitVon);
+ String dateStr2 = String.Format("{0:dd.MM.yyyy} {1}", tag, ae.UhrzeitBis);
+
+ DateTime date1;
+ DateTime date2;
+
+ if (DateTime.TryParse(dateStr1, out date1) && DateTime.TryParse(dateStr2, out date2))
+ {
+
+ stunden += (decimal)date2.Subtract(date1).TotalHours;
+ }
+ }
+ }
+
+ return stunden;
+ }
+
+ protected override void CalculateEmployeeSalary(MitarbeiterstundenkontoRO.EmployeeDetail empDetail, DateTime start, DateTime ende)
+ {
+ decimal maxpayment = 0;
+
+ Contract cstart = empDetail.Employee.GetValidContractForDate(start);
+ Contract cend = empDetail.Employee.GetValidContractForDate(ende);
+ if (cstart != null && cstart.HourlyRate.HasValue && cstart.WeeklyTotalHours.HasValue && cend != null)
+ {
+ if (cstart.Oid.Value == cend.Oid.Value || !cend.EntryDate.HasValue)
+ {
+ maxpayment = cstart.HourlyRate.Value * cstart.WeeklyTotalHours.Value * 4;
+ }
+ else
+ {
+ decimal totalHoursPerDay = cstart.WeeklyTotalHours.Value / 7m;
+ DateTime end = cend.EntryDate.Value;
+ TimeSpan ts = end.Subtract(start);
+ int days = (int)ts.TotalDays;
+
+ maxpayment = totalHoursPerDay * cstart.HourlyRate.Value * days;
+
+ Contract c2 = empDetail.Employee.GetValidContractForDate(end.AddDays(1));
+ if (c2 != null && c2.HourlyRate.HasValue && c2.WeeklyTotalHours.HasValue)
+ {
+ decimal totalHoursPerDay2 = c2.WeeklyTotalHours.Value / 7m;
+ TimeSpan ts2 = ende.Subtract(end);
+ int days2 = (int)ts2.TotalDays + 1;
+
+ maxpayment += totalHoursPerDay2 * c2.HourlyRate.Value * days2;
+ }
+ }
+
+
+ }
+ empDetail.EmployeeSalaryByContract = maxpayment;
+
+ decimal salary = 0;
+
+ if (empDetail.ServiceRecordsInSelectedTimeSpan != null)
+ {
+ foreach (var sr in empDetail.ServiceRecordsInSelectedTimeSpan)
+ {
+ if (sr.ServiceDescription.Name != "Freizeit" || sr.ServiceDescription.ServiceCategory.Name != "Selbstzahler Freizeit")
+ {
+ Contract con = empDetail.Employee.GetValidContractForDate(sr.Start);
+ if (con != null && con.HourlyRate.HasValue)
+ {
+ salary += con.HourlyRate.Value * (sr.RoundedDuration / 60);
+ }
+
+ }
+ }
+ }
+
+ empDetail.EmployeeSalaryByHours = salary;
+ }
+
+ protected override ServiceRecordSummary BerechneStunden(MitarbeiterstundenkontoRO.EmployeeDetail empDetail, IList groupFilteredRecords, DateTime berichtsAnfang, DateTime berichtsEnde)
+ {
+ decimal flsGesamt = 0;
+ decimal arbeitszeit = 0;
+
+ var sum = new ServiceRecordSummary();
+ sum.RecordsImBerichtszeitraum = new List();
+
+ empDetail.ServiceRecordsInSelectedTimeSpan = new List();
+
+ foreach (var item in groupFilteredRecords)
+ {
+ if (!IsServiceRecordFehlzeit(item))
+ {
+ if (item.Start.Value >= berichtsAnfang && item.Start.Value < berichtsEnde)
+ {
+ //String test = String.Format("{0:dd.MM.yyyy HH:mm}, {1:0} Minuten, {2}, {3}", item.Start, item.RoundedDuration, item.ServiceDescription.Name, item.Notice);
+
+ HandleServiceRecord(empDetail, item);
+
+ if (Settings.ShowWorktime && Settings.EnableArbeitszeiterfassung)
+ {
+ empDetail.ServiceRecordsInSelectedTimeSpan.Add(item);
+
+ decimal duration = GetDuration(item, empDetail);
+
+ if (IstArbeitszeit(item))
+ {
+ arbeitszeit += duration;
+ }
+ else if (item.ServiceDescription != null && item.ServiceDescription.ServiceCategory != null && item.ServiceDescription.ServiceCategory.IsBillable)
+ {
+ flsGesamt += duration;
+ }
+ }
+ else if (AddServiceRecordToDuration(item))
+ {
+ empDetail.ServiceRecordsInSelectedTimeSpan.Add(item);
+
+ decimal duration = GetDuration(item, empDetail);
+
+ if (item.ServiceDescription != null &&
+ item.ServiceDescription.ServiceCategory != null &&
+ item.ServiceDescription.ServiceCategory.IsBillable)
+ sum.IstAbrechenbar += duration;
+ else
+ sum.IstNichtAbrechenbar += duration;
+
+
+ //Prüfe ProzentStundenkonto, wenn der Wert != 100 ist, dann packe die prozentuale Differenz von abbrechenbar auf nicht abbrechenbar
+ decimal prozent = 100;
+ if (item.ServiceDescription.ProzentStundenkonto.HasValue)
+ {
+ prozent = item.ServiceDescription.ProzentStundenkonto.Value;
+ }
+ else if (item.ServiceDescription.ServiceCategory.ProzentStundenkonto.HasValue)
+ {
+ prozent = item.ServiceDescription.ServiceCategory.ProzentStundenkonto.Value;
+ }
+ if (prozent == 0)
+ {
+ int test = 0;
+ }
+ if (prozent != 100 && prozent > 0)
+ {
+ var ohneProzent = duration / prozent * 100;
+
+ var diff = duration - ohneProzent;
+
+ sum.IstAbrechenbar -= diff;
+ sum.IstNichtAbrechenbar += diff;
+ }
+
+
+
+ }
+ sum.RecordsImBerichtszeitraum.Add(item);
+ }
+ }
+ }
+ if (Settings.ShowWorktime && Settings.EnableArbeitszeiterfassung)
+ {
+ sum.IstAbrechenbar = flsGesamt;
+ sum.IstNichtAbrechenbar = arbeitszeit - flsGesamt;
+ }
+ return sum;
+ }
+
+ public override bool UseServiceDescriptionPercentage()
+ {
+ return false;
+ }
+
+ public override decimal BerechneAutoPausen(IList recordsThisMonth)
+ {
+ return 0;
+ }
+
+ public override decimal BerechnePauseMinuten(IList srList)
+ {
+ decimal pausenZeit = 0;
+
+ List pausen = new List();
+ List nichtpausen = new List();
+
+ foreach (var sr in srList)
+ {
+ if (sr.ServiceDescription.Name == "Pause")
+ {
+ pausen.Add(sr);
+ }
+ else
+ {
+ nichtpausen.Add(sr);
+ }
+ }
+
+ foreach (var pause in pausen)
+ {
+ foreach (var arbeitszeit in nichtpausen)
+ {
+ var pauseSpan = new DateTimeSpan
+ {
+ StartDateTime = pause.Start.Value,
+ EndDateTime = pause.Start.Value.AddMinutes((double)pause.RoundedDuration)
+ };
+
+ var arbeitszeitSpan = new DateTimeSpan
+ {
+ StartDateTime = arbeitszeit.Start.Value,
+ EndDateTime = arbeitszeit.Start.Value.AddMinutes((double)arbeitszeit.RoundedDuration)
+ };
+
+ if (arbeitszeit.GroupOid.HasValue)
+ {
+ arbeitszeitSpan.EndDateTime =
+ arbeitszeit.Start.Value.AddMinutes((double)(arbeitszeit.GroupRoundedDuration ?? 0));
+ }
+
+
+ if (pauseSpan.Overlaps(arbeitszeitSpan))
+ {
+ DateTime startUeberlappung = pauseSpan.StartDateTime;
+ if (arbeitszeitSpan.StartDateTime > startUeberlappung)
+ startUeberlappung = arbeitszeitSpan.StartDateTime;
+
+ DateTime endUeberlappung = pauseSpan.EndDateTime;
+
+ if (arbeitszeitSpan.EndDateTime < endUeberlappung)
+ endUeberlappung = arbeitszeitSpan.EndDateTime;
+
+ var overlapping = (decimal)endUeberlappung.Subtract(startUeberlappung).TotalMinutes;
+
+ pausenZeit += overlapping;
+ }
+ }
+ }
+ return pausenZeit;
+ }
+
+ public override bool SollPauseBerechnen()
+ {
+ return false;
+ }
+
+ public override bool BerechneSollNurBisZumAktuellenTag()
+ {
+ return false;
+ }
+
+ public override List FilterEmployeeList(List employeeDetailList)
+ {
+ List newEmpList = new List();
+
+ foreach (var ed in employeeDetailList)
+ {
+ if (ed.Employee.IsActive == ActivationTypeId.Active)
+ {
+ newEmpList.Add(ed);
+ }
+ else if (ed.Employee.IsActive == ActivationTypeId.Archived)
+ {
+ if (ed.StundenGesamtIst != 0)
+ {
+ newEmpList.Add(ed);
+ }
+ }
+ }
+
+ return newEmpList;
+ }
+
+ public override void HandleServiceRecords(MitarbeiterstundenkontoRO.EmployeeDetail empDetail, IList groupFilteredRecords, Contract contract)
+ {
+ //do nothing
+ }
+
+ public override void CorrectOverlappingTimes(IList serviceRecords)
+ {
+ List oldRecords = new List();
+
+ foreach (var newRecord in serviceRecords)
+ {
+ if (!CorrectOverlappingTime(newRecord))
+ {
+ continue;
+ }
+ if (newRecord.Start.HasValue && newRecord.Start.Value.Second > 0)
+ {
+ continue;
+ }
+
+ bool isGroup = false;
+ var minuten = newRecord.RoundedDuration;
+ if (newRecord.GroupRoundedDuration.HasValue)
+ {
+ minuten = newRecord.GroupRoundedDuration.Value;
+ isGroup = true;
+ }
+ var newSpan = new DateTimeSpan
+ {
+ StartDateTime = newRecord.Start.Value,
+ EndDateTime = newRecord.Start.Value.AddMinutes((double)minuten)
+ };
+
+ foreach (var oldRecord in oldRecords)
+ {
+ if (!CorrectOverlappingTime(oldRecord, newRecord))
+ {
+ continue;
+ }
+ var oldSpan = new DateTimeSpan
+ {
+ StartDateTime = oldRecord.Start.Value,
+ EndDateTime = oldRecord.Start.Value.AddMinutes((double)oldRecord.RoundedDuration)
+ };
+ if (oldRecord.GroupRoundedDuration.HasValue)
+ {
+ oldSpan.EndDateTime = oldRecord.Start.Value.AddMinutes((double)oldRecord.GroupRoundedDuration);
+ }
+
+ if (oldSpan.Overlaps(newSpan))
+ {
+ DateTime start = newSpan.StartDateTime;
+ if (oldSpan.StartDateTime > start)
+ start = oldSpan.StartDateTime;
+
+ DateTime end = newSpan.EndDateTime;
+
+ if (oldSpan.EndDateTime < end)
+ end = oldSpan.EndDateTime;
+
+
+ if (newSpan.StartDateTime >= oldSpan.StartDateTime)
+ {
+ newSpan.StartDateTime = oldSpan.EndDateTime;
+ if (newSpan.StartDateTime > newSpan.EndDateTime)
+ {
+ newSpan.StartDateTime = newSpan.EndDateTime;
+ }
+ newRecord.Start = newSpan.StartDateTime;
+ newRecord.End = newSpan.EndDateTime;
+
+ if (!isGroup)
+ {
+ newRecord.RoundedDuration =
+ (decimal)newRecord.End.Value.Subtract(newRecord.Start.Value).TotalMinutes;
+ }
+ else
+ {
+ newRecord.GroupRoundedDuration = (decimal)newRecord.End.Value.Subtract(newRecord.Start.Value).TotalMinutes;
+ }
+ }
+ else
+ {
+ oldSpan.StartDateTime = newSpan.EndDateTime;
+ if (oldSpan.StartDateTime > oldSpan.EndDateTime)
+ {
+ oldSpan.StartDateTime = oldSpan.EndDateTime;
+ }
+ oldRecord.Start = oldSpan.StartDateTime;
+ oldRecord.End = oldSpan.EndDateTime;
+
+
+ if (!oldRecord.GroupRoundedDuration.HasValue)
+ //if (!isGroup)
+ {
+ oldRecord.RoundedDuration = (decimal)oldRecord.End.Value.Subtract(oldRecord.Start.Value).TotalMinutes;
+ }
+ else
+ {
+ oldRecord.GroupRoundedDuration = (decimal)oldRecord.End.Value.Subtract(oldRecord.Start.Value).TotalMinutes;
+ }
+
+ }
+
+ }
+ }
+ oldRecords.Add(newRecord);
+
+ }
+ }
+
+ public override bool CorrectOverlappingTime(ServiceRecord newRecord)
+ {
+ return true;
+ }
+
+ public override bool CorrectOverlappingTime(ServiceRecord record1, ServiceRecord record2)
+ {
+ return true;
+ }
+
+ public override decimal? BerechneSollProMonat(Employee emp, DateTime start, DateTime ende)
+ {
+ Contract c = GetContractForDate(emp, start, ende);
+
+
+ if (c != null && c.MonthlyTotalHours.HasValue && c.MonthlyTotalHours.Value > 0)
+ {
+ return c.MonthlyTotalHours.Value;
+
+ DateTime monatsAnfang = start;
+ DateTime monatsEnde = ende;
+
+ if (c.EntryDate.HasValue && c.EntryDate > monatsAnfang)
+ {
+ monatsAnfang = c.EntryDate.Value;
+ }
+ if (c.ContractEndDate.HasValue && c.ContractEndDate < monatsEnde)
+ {
+ monatsEnde = c.ContractEndDate.Value.Date.AddDays(1);
+ }
+
+ if (monatsAnfang < monatsEnde)
+ {
+ //if (monatsAnfang.Day == 1 && monatsEnde.Day == 1)
+ //{
+ return c.MonthlyTotalHours.Value;
+ //}
+ }
+ }
+
+ return null;
+ }
+
+ public override decimal? BerechneFlsSollProMonat(Employee emp, DateTime start, DateTime ende)
+ {
+ Contract c = GetContractForDate(emp, start, ende);
+
+
+ if (c != null && c.MonthlyFLS.HasValue && c.MonthlyFLS.Value > 0)
+ {
+ return c.MonthlyFLS.Value;
+
+ DateTime monatsAnfang = start;
+ DateTime monatsEnde = ende;
+
+ if (c.EntryDate.HasValue && c.EntryDate > monatsAnfang)
+ {
+ monatsAnfang = c.EntryDate.Value;
+ }
+ if (c.ContractEndDate.HasValue && c.ContractEndDate < monatsEnde)
+ {
+ monatsEnde = c.ContractEndDate.Value.Date.AddDays(1);
+ }
+
+ if (monatsAnfang < monatsEnde)
+ {
+ //if (monatsAnfang.Day == 1 && monatsEnde.Day == 1)
+ //{
+ return c.MonthlyFLS.Value;
+ //}
+ }
+ }
+
+ return null;
+ }
+
+ public override void HandleServiceRecord(MitarbeiterstundenkontoRO.EmployeeDetail emp, ServiceRecord item)
+ {
+ //do nothing
+ }
+
+ public override bool ShouldCreateDayDetails()
+ {
+ //if (Settings.ShowWorktime && Settings.EnableArbeitszeiterfassung)
+ //{
+ // return true;
+ //}
+ //CB ab 04.09.2024 immer true
+ return true;
+ }
+
+ public override bool ShouldCreateDayDetailsForEachServiceRecord()
+ {
+ return false;
+ }
+
+ public override IList CreateDayDetails(DateTime berichtStart, DateTime berichtEnde, MitarbeiterstundenkontoRO.EmployeeDetail ed, IList recordsAll)
+ {
+ if (ShouldCreateDayDetailsForEachServiceRecord())
+ {
+ return CreateDayDetailsForEachServiceRecord(berichtStart, berichtEnde, ed, recordsAll);
+ }
+
+ Dictionary dayToDetail = new Dictionary();
+
+ for (int i = berichtStart.Day; i <= berichtEnde.AddDays(-1).Day; i++)
+ {
+ MitarbeiterstundenkontoRO.DayDetail dd = CreateDayDetail(new DateTime(berichtStart.Year, berichtStart.Month, i), ed);
+ dayToDetail.Add(i, dd);
+ }
+
+ foreach (var sr in recordsAll)
+ {
+ if (sr.Start.HasValue && sr.Start >= berichtStart && sr.Start < berichtEnde && sr.End.HasValue)
+ {
+ if (sr.Start.Value.Year == 2023 && sr.Start.Value.Month == 10 && sr.Start.Value.Day == 3)
+ {
+ int test = 0;
+ }
+ var dd = dayToDetail[sr.Start.Value.Day];
+
+ if (!IsServiceRecordFehlzeit(sr) && AddServiceRecordToDuration(sr))
+ {
+ decimal duration = GetDuration(sr, ed);
+ dd.MinutesTotal += duration;
+ TimeSpan tspan = TimeSpan.FromMinutes((double)dd.MinutesTotal);
+ dd.TimeTotal = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ if (dd.ServiceRecords == null)
+ dd.ServiceRecords = new List();
+ dd.ServiceRecords.Add(sr);
+
+ CalculateSpecialHours(dd, sr, duration);
+ }
+ }
+ }
+
+ //Berechne von bis Zeiten
+ foreach (var ddKeyValue in dayToDetail)
+ {
+ var dd = ddKeyValue.Value;
+ if (dd.ZusammenhaengendeZeitraeume == null)
+ {
+ dd.ZusammenhaengendeZeitraeume = new List();
+ }
+
+ if (dd.ServiceRecords != null)
+ {
+ var orderedList = dd.ServiceRecords.OrderBy(i => i.Start);
+
+ DateTime lastEnd = DateTime.MinValue;
+ var gapCount = -1;
+
+ foreach (var sr in orderedList)
+ {
+ if (sr.Start.HasValue && sr.Start.Value.Hour == 0 && sr.Start.Value.Second > 0)
+ {
+ continue;
+ }
+ decimal duration = GetDuration(sr, ed);
+
+
+ DateTime dtEnd = sr.Start.Value.AddMinutes((double)duration);
+ if (sr.Start > lastEnd)
+ {
+ gapCount++;
+
+ }
+ if (dtEnd > lastEnd)
+ {
+ lastEnd = dtEnd;
+ }
+
+ DateTimeSpan currentSpan = null;
+ if (gapCount + 1 > dd.ZusammenhaengendeZeitraeume.Count)
+ {
+ currentSpan = new DateTimeSpan();
+ currentSpan.StartDateTime = DateTime.MaxValue;
+ currentSpan.EndDateTime = DateTime.MinValue;
+ dd.ZusammenhaengendeZeitraeume.Add(currentSpan);
+ }
+ else
+ {
+ currentSpan = dd.ZusammenhaengendeZeitraeume[gapCount];
+ }
+
+ if (sr.Start < currentSpan.StartDateTime)
+ {
+ currentSpan.StartDateTime = sr.Start.Value;
+ }
+ if (dtEnd > currentSpan.EndDateTime)
+ {
+ currentSpan.EndDateTime = dtEnd;
+ }
+
+ if (gapCount == 0)
+ {
+ if (!dd.StartDateTime1.HasValue || sr.Start < dd.StartDateTime1)
+ {
+ dd.StartDateTime1 = sr.Start;
+ }
+ if (!dd.EndDateTime1.HasValue || dtEnd > dd.EndDateTime1)
+ {
+ dd.EndDateTime1 = dtEnd;
+ }
+ }
+ else if (gapCount == 1)
+ {
+ if (!dd.StartDateTime2.HasValue || sr.Start < dd.StartDateTime2)
+ {
+ dd.StartDateTime2 = sr.Start;
+ }
+ if (!dd.EndDateTime2.HasValue || dtEnd > dd.EndDateTime2)
+ {
+ dd.EndDateTime2 = dtEnd;
+ }
+ }
+ else if (gapCount == 2)
+ {
+ if (!dd.StartDateTime3.HasValue || sr.Start < dd.StartDateTime3)
+ {
+ dd.StartDateTime3 = sr.Start;
+ }
+ if (!dd.EndDateTime3.HasValue || dtEnd > dd.EndDateTime3)
+ {
+ dd.EndDateTime3 = dtEnd;
+ }
+ }
+ else
+ {
+ if (!dd.StartDateTime4.HasValue || sr.Start < dd.StartDateTime4)
+ {
+ dd.StartDateTime4 = sr.Start;
+ }
+ if (!dd.EndDateTime4.HasValue || dtEnd > dd.EndDateTime4)
+ {
+ dd.EndDateTime4 = dtEnd;
+ }
+ }
+ }
+ }
+ }
+
+
+
+
+ return dayToDetail.Values.OrderBy(dd => dd.Date).ToList();
+ }
+
+ public override IList CreateDayDetailsForEachServiceRecord(DateTime berichtStart, DateTime berichtEnde, MitarbeiterstundenkontoRO.EmployeeDetail ed, IList recordsAll)
+ {
+ var details = new List();
+
+ foreach (var sr in recordsAll)
+ {
+ if (sr.Start.HasValue && sr.Start >= berichtStart && sr.Start < berichtEnde && sr.End.HasValue)
+ {
+ if (sr.Start.Value.Day == 8)
+ {
+ int test = 0;
+ }
+ if (!IsServiceRecordFehlzeit(sr) && AddServiceRecordToDuration(sr))
+ {
+ var dd = CreateDayDetail(sr.Start.Value, ed);
+ details.Add(dd);
+
+ decimal duration = GetDuration(sr, ed);
+
+
+ dd.MinutesTotal += duration;
+ TimeSpan tspan = TimeSpan.FromMinutes((double)dd.MinutesTotal);
+ dd.TimeTotal = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ if (dd.ServiceRecords == null)
+ dd.ServiceRecords = new List();
+ dd.ServiceRecords.Add(sr);
+
+ dd.Date = sr.Start.Value;
+
+ if (sr.Start.Value.Second == 0)
+ {
+ DateTime dtEnd = sr.Start.Value.AddMinutes((double)duration);
+ dd.StartDateTime1 = sr.Start;
+
+ dd.EndDateTime1 = dtEnd;
+
+ }
+
+ CalculateSpecialHours(dd, sr, duration);
+
+ }
+ }
+ }
+
+ return details.OrderBy(dd => dd.Date).ToList();
+ }
+
+ protected override decimal GetDuration(ServiceRecord sr, MitarbeiterstundenkontoRO.EmployeeDetail ed)
+ {
+
+ foreach (var at in ed.Employee.AbsenceTimes)
+ {
+ if (!StundenBeiAbwesenheitBerechnen(at))
+ {
+ DateTime start = DateTime.MinValue;
+ DateTime end = DateTime.MaxValue;
+
+ if (at.Start.HasValue)
+ {
+ start = at.Start.Value;
+ }
+ if (at.End.HasValue)
+ {
+ end = at.End.Value;
+ if (end.Hour == 0)
+ {
+ end = end.AddDays(1);
+ }
+ }
+
+ if (start <= sr.Start && sr.Start < end)
+ {
+ return 0;
+ }
+ }
+
+ }
+
+ decimal duration = 0;
+
+ if (sr.GroupRoundedDuration.HasValue)
+ {
+ duration = sr.GroupRoundedDuration.Value;
+ }
+ else
+ {
+ if (appSettings.ShowRoundedDurationInEmployeeAnalysis)
+ {
+ duration = sr.RoundedDuration;
+ }
+ else
+ {
+ duration = (decimal)sr.End.Value.Subtract(sr.Start.Value).TotalMinutes;
+ }
+ }
+
+ if (UseServiceDescriptionPercentage())
+ {
+ decimal prozent = sr.ServiceDescription.ServiceCategory.ProzentStundenkonto ?? sr.ServiceDescription.ServiceCategory.ProzentAbrechnung;
+
+ if (sr.ServiceDescription.ProzentStundenkonto.HasValue)
+ {
+ prozent = sr.ServiceDescription.ProzentStundenkonto.Value;
+ }
+ duration = (duration * prozent) / 100;
+ }
+ else
+ {
+ decimal prozent = 100;
+ if (sr.ServiceDescription.ProzentStundenkonto.HasValue)
+ {
+ prozent = sr.ServiceDescription.ProzentStundenkonto.Value;
+ }
+ else if (sr.ServiceDescription.ServiceCategory.ProzentStundenkonto.HasValue)
+ {
+ prozent = sr.ServiceDescription.ServiceCategory.ProzentStundenkonto.Value;
+ }
+
+
+ duration *= (prozent / 100);
+ }
+
+
+ return duration;
+ }
+
+ //Mit "SpecialHours" sind Sonderzeiten wie Nachtzeiten oder Arbeitszeiten sonn- und feiertags gemeint. Da diese gesondert abgerechnet werden können.
+ public override void CalculateSpecialHours(MitarbeiterstundenkontoRO.DayDetail dd, ServiceRecord sr, decimal duration)
+ {
+ //Prüfe, ob Start oder Ende des Eintrags an einem Sonntag sind, dann die am Sonntag geleisteten Minuten berechnen
+ //Todo: Bei mehrtägigen Einträgen prüfen, ob sie vor einem Sonntag anfangen und nach einem Sonntag enden. Unklar, ob das vorkommen kann und ob dann Sonntagszuschläge berechnet werden müssen
+ if (sr.Start.Value.DayOfWeek == DayOfWeek.Sunday || sr.End.Value.DayOfWeek == DayOfWeek.Sunday)
+ {
+ DateTime sonntag = sr.Start.Value.Date;
+ if (sr.Start.Value.DayOfWeek != DayOfWeek.Sunday && sr.End.Value.DayOfWeek == DayOfWeek.Sunday)
+ {
+ sonntag = sr.End.Value.Date;
+ }
+
+ var sonnatgsMinuten = GetOverlappingMinutes(sr.Start.Value, sr.End.Value, sonntag, sonntag.AddDays(1));
+ dd.HoursSunday += (decimal)sonnatgsMinuten / 60;
+ }
+ //Prüfe, ob Start oder Ende des Eintrags an einem Feiertag sind, dann die am Feiertag geleisteten Minuten berechnen
+ //Todo: Bei mehrtägigen Einträgen prüfen, ob sie vor einem Feiertag anfangen und nach einem Feiertag enden. Unklar, ob das vorkommen kann
+ else if (IstFeiertag(sr.Start.Value) || IstFeiertag(sr.End.Value))
+ {
+ var feiertagsMinuten = duration;
+ DateTime feiertag = sr.Start.Value.Date;
+ if (IstFeiertag(sr.Start.Value) && !IstFeiertag(sr.End.Value))
+ {
+ feiertagsMinuten = (decimal)GetOverlappingMinutes(sr.Start.Value, sr.End.Value, feiertag, feiertag.AddDays(1));
+ }
+ else if (!IstFeiertag(sr.Start.Value) && IstFeiertag(sr.End.Value))
+ {
+ feiertag = sr.End.Value.Date;
+ feiertagsMinuten = (decimal)GetOverlappingMinutes(sr.Start.Value, sr.End.Value, feiertag, feiertag.AddDays(1));
+ }
+ dd.HoursHoliday += feiertagsMinuten / 60;
+ }
+
+ if (sr.Start.Value.Second == 0 && dd.NightEnd.HasValue && dd.NightStart.HasValue)
+ {
+ var minuten = CheckNachtzeiten(sr.Start.Value, dd.NightStart.Value, dd.NightEnd.Value, (double)duration);
+
+ if (minuten > 0)
+ {
+ dd.SpecialHours += (decimal)(Math.Round(minuten, 0) / 60);
+ }
+
+ //DateTime dtEnd = sr.Start.Value.AddMinutes((double)duration);
+
+ //if (dtEnd.Hour >= dd.NightStart.Value)
+ //{
+ // DateTime dtStart = dtEnd.Date.AddHours(dd.NightStart.Value);
+ // if (sr.Start.Value > dtStart)
+ // dtStart = sr.Start.Value;
+
+ // TimeSpan ts = dtEnd.Subtract(dtStart);
+ // dd.SpecialHours += (decimal)ts.TotalMinutes / 60;
+ //}
+
+ //if (sr.Start.Value.Hour < dd.NightEnd)
+ //{
+ // DateTime dtEnd2 = sr.Start.Value.Date.AddHours(dd.NightEnd.Value);
+ // if (dtEnd < dtEnd2)
+ // dtEnd2 = dtEnd;
+ // TimeSpan ts = dtEnd2.Subtract(sr.Start.Value);
+ // dd.SpecialHours += (decimal)(Math.Round(ts.TotalMinutes, 0) / 60);
+ //}
+ }
+ }
+
+ private double CheckNachtzeiten(DateTime start, int nightStart, int nightEnd, double minuten)
+ {
+ DateTime nightStartDt = start.Date.AddHours(nightStart);
+ DateTime nightEndDt = start.Date.AddDays(1).AddHours(nightEnd);
+
+
+ DateTime dtEnd = start.AddMinutes(minuten);
+
+ double minutes = GetOverlappingMinutes(start, dtEnd, nightStartDt, nightEndDt);
+ minutes += GetOverlappingMinutes(start, dtEnd, nightStartDt.AddDays(-1), nightEndDt.AddDays(-1));
+ minutes += GetOverlappingMinutes(start, dtEnd, nightStartDt.AddDays(1), nightEndDt.AddDays(1));
+
+ return minutes;
+
+ }
+
+ public double GetOverlappingMinutes(DateTime start1, DateTime end1, DateTime start2, DateTime end2)
+ {
+ if (end1 > start2 && start1 < end2)
+ {
+ DateTime dtStart = start1;
+
+ if (dtStart < start2)
+ dtStart = start2;
+
+ DateTime dtEnd = end1;
+
+ if (dtEnd > end2)
+ dtEnd = end2;
+ TimeSpan ts = dtEnd.Subtract(dtStart);
+
+ return ts.TotalMinutes;
+
+ }
+
+ return 0;
+ }
+
+ public override MitarbeiterstundenkontoRO.DayDetail CreateDayDetail(DateTime date, MitarbeiterstundenkontoRO.EmployeeDetail ed)
+ {
+ MitarbeiterstundenkontoRO.DayDetail dd = new MitarbeiterstundenkontoRO.DayDetail();
+ dd.ZusammenhaengendeZeitraeume = new List();
+ dd.Date = date.Date;
+ dd.NightStart = 20;
+ dd.NightEnd = 6;
+ dd.TimeTotal = "00:00";
+ dd.TimeInVacation = "00:00";
+ dd.TimeSick = "00:00";
+ dd.TimeOtherAbsenceTimes = "00:00";
+ ConfigurePauseSettings(dd);
+ CalculateAbsenceTimesForDay(dd, ed);
+ // Claude: vacationService.GetFeiertag pro Datum cachen, da dieselbe Datum-Abfrage über viele Mitarbeiter wiederholt wird
+ // Claude: Vollständiges DateTime als Key (nicht .Date), damit das Verhalten bei evtl. nicht-mitternächtigen Aufrufen identisch bleibt
+ if (_feiertagDCByDateCache == null)
+ {
+ _feiertagDCByDateCache = new Dictionary();
+ }
+ FeiertagDC feiertagDC;
+ if (!_feiertagDCByDateCache.TryGetValue(date, out feiertagDC))
+ {
+ feiertagDC = vacationService.GetFeiertag(date);
+ _feiertagDCByDateCache[date] = feiertagDC;
+ }
+ dd.Feiertag = feiertagDC;
+ BerechneSollStundenFuerDayDetail(ed, dd);
+
+ if (BerechneFeiertageAlsArbeitszeit())
+ {
+ if (dd.Feiertag != null)
+ {
+ dd.MinutesTotal = dd.SollStunden * 60;// * GetFeiertagsFaktor(dd.Date);
+ TimeSpan tspan = TimeSpan.FromMinutes((double)dd.MinutesTotal);
+ dd.TimeTotal = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ }
+ }
+
+ return dd;
+ }
+
+ public override void ConfigurePauseSettings(MitarbeiterstundenkontoRO.DayDetail dd)
+ {
+ if (Settings.ShowWorktime && Settings.EnableArbeitszeiterfassung)
+ {
+ if (Settings.EnableAutomaticPauses)
+ {
+ dd.MaxHoursUntilBreak = 6;
+ dd.BreakMinutes = 30;
+ dd.MaxHoursUntilBreak2 = 9;
+ dd.BreakMinutes2 = 15;
+ }
+ }
+ //nicht mehr nötig. Im Default werden keine Pausen mehr berechnet
+ //else
+ // {
+ // //Alter Default
+ // dd.MaxHoursUntilBreak = null;
+ // dd.BreakMinutes = 30;
+ //}
+ }
+
+ public override void BerechneSollStundenFuerDayDetail(MitarbeiterstundenkontoRO.EmployeeDetail ed, MitarbeiterstundenkontoRO.DayDetail dd)
+ {
+ var sollMax = BerechneSollStundenFuerTag(ed, dd.Date);
+
+ if (BerechneSollNurBisZumAktuellenTag())
+ {
+ if (dd.Date > DateTime.Now.Date)
+ {
+ sollMax = 0;
+ }
+ }
+
+
+ dd.SollStunden = sollMax;
+
+ }
+
+ public override decimal BerechneSollStundenFuerTag(MitarbeiterstundenkontoRO.EmployeeDetail ed, DateTime date)
+ {
+ decimal sollDiesenTag = 0;
+ var faktor = GetFeiertagsFaktor(date);
+ if (BerechneFeiertageAlsArbeitszeit())
+ {
+ if (faktor == 1)
+ {
+ faktor = 0;
+ }
+ }
+ if (faktor < 1)
+ {
+ //Berechne Soll, entweder aus Vertrag oder Arbeitszeiten
+ if (HatArbeitszeitImZeitraum(ed.Employee.Arbeitszeiten, date, date))
+ {
+ sollDiesenTag = GetSollNachArbeitszeitenFuerTag(ed.Employee, date);
+
+ }
+ else
+ {
+ //sollDiesenTag = ed.SollProTag;
+
+ var c = ed.Employee.GetValidContractForDate(date);
+ if (c != null)
+ {
+ decimal wth = 0;
+ int apw = ANZ_WOCHENTAGE;
+ if (c.WeeklyDays.HasValue && c.WeeklyDays.Value > 0)
+ {
+ apw = c.WeeklyDays.Value;
+ }
+ if (c.WeeklyTotalHours.HasValue)
+ {
+ wth = c.WeeklyTotalHours.Value;
+ }
+ else if (c.MonthlyTotalHours.HasValue && ed.ArbeitstageImBerichtszeitraum > 0)
+ {
+ wth = c.MonthlyTotalHours.Value / ed.ArbeitstageImBerichtszeitraum;
+ wth *= apw;
+ }
+
+
+ sollDiesenTag = wth / apw;
+
+ bool istWochenende = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
+ if (apw == 6)
+ {
+ istWochenende = date.DayOfWeek == DayOfWeek.Sunday;
+ }
+ else if (apw > 6)
+ {
+ istWochenende = false;
+ }
+ if (istWochenende)
+ {
+ sollDiesenTag = 0;
+ }
+ }
+ if (faktor != 0)
+ {
+ sollDiesenTag *= faktor; //halbe Feiertage etc. berücksichtigen
+ }
+ }
+ }
+
+ return sollDiesenTag;
+
+ }
+
+ public override void CalculateAbsenceTimesForDay(MitarbeiterstundenkontoRO.DayDetail dd, MitarbeiterstundenkontoRO.EmployeeDetail ed)
+ {
+ var absenceTimes = ed.Employee.AbsenceTimes;
+
+ if (absenceTimes != null)
+ {
+ foreach (var absenceTime in absenceTimes)
+ {
+
+ //if (IsUrlaubAbsenceTime(absenceTime) || IsKrankheitAbsenceTime(absenceTime))
+ //{
+ DateTimeSpan span = new DateTimeSpan();
+
+ span.StartDate = dd.Date;
+
+ if (absenceTime.Start.HasValue && absenceTime.Start.Value > span.StartDate)
+ {
+ span.StartDate = absenceTime.Start.Value;
+ }
+
+ if (absenceTime.End.HasValue && absenceTime.End.Value.Date == span.StartDate.Date &&
+ absenceTime.End.Value.Hour > 0)
+ {
+ span.EndDate = absenceTime.End.Value;
+ }
+ else
+ {
+ span.EndDate = dd.Date.AddDays(1);
+ if (absenceTime.End.HasValue && absenceTime.End.Value.Date.AddDays(1) < span.EndDate)
+ {
+ span.EndDate = absenceTime.End.Value.Date.AddDays(1);
+ }
+ }
+
+ if (span.EndDate > dd.Date && span.StartDate < dd.Date.AddDays(1))
+ {
+ DateTime day = span.StartDate;
+ var faktor = GetFeiertagsFaktor(day);
+
+
+ if (faktor < 1 && (day.DayOfWeek != DayOfWeek.Saturday || ed.ArbeitstageProWoche == 6) && (day.DayOfWeek != DayOfWeek.Sunday || ed.ArbeitstageProWoche > 6)) // Prüfung auf 6 + bzw 7 Tage Woche
+ {
+ //Berechne Soll, entweder aus Vertrag oder Arbeitszeiten
+ var sollDiesenTag = ed.SollProTag;
+
+ bool checkMaxWochenSoll = true;
+
+ if (!IstAnrechenbareAbsenceTime(absenceTime))
+ {
+ sollDiesenTag = 0;
+ }
+ else if (HatArbeitszeitImZeitraum(ed.Employee.Arbeitszeiten, dd.Date, dd.Date))
+ {
+ sollDiesenTag = BerechneAlleAbwesenheitsStundenFuerArbeitszeiten(ed.Employee, dd.Date); // Hier wird der Feiertagsfaktor schon applied
+
+ }
+ else
+ {
+ var ssa = GetSollStundenForAbsenceTime(ed, absenceTime, dd.Date);
+ if (ssa.HasValue)
+ {
+ sollDiesenTag = ssa.Value;
+ checkMaxWochenSoll = false;
+
+ }
+ else
+ {
+ var c = ed.Employee.GetValidContractForDate(dd.Date);
+ if (c != null)
+ {
+ decimal wth = 0;
+ int apw = ANZ_WOCHENTAGE;
+ if (c.WeeklyDays.HasValue && c.WeeklyDays.Value > 0)
+ {
+ apw = c.WeeklyDays.Value;
+ }
+
+ if (c.WeeklyTotalHours.HasValue)
+ {
+ wth = c.WeeklyTotalHours.Value;
+ }
+ else if (c.MonthlyTotalHours.HasValue)
+ {
+
+ sollDiesenTag = c.MonthlyTotalHours.Value / ed.ArbeitstageImBerichtszeitraum;
+ wth = sollDiesenTag * apw;
+ }
+
+
+ sollDiesenTag = wth / apw;
+ }
+ }
+ }
+
+
+ //Dauer der Abwesenheit = Soll an diesem Tag
+ bool istGanztaegig = true;
+ var abwesenheitsDauer = sollDiesenTag;
+
+ if (span.StartDate.Hour > 0 && span.EndDate.Hour > 0)
+ {
+ istGanztaegig = false;
+
+ var ts = span.EndDate.Subtract(span.StartDate);
+ abwesenheitsDauer = (decimal)ts.TotalHours;
+ if (SchneideZuvieleAbwesenheitsstundenAb() && abwesenheitsDauer > sollDiesenTag)
+ {
+ abwesenheitsDauer = sollDiesenTag;
+ }
+
+ sollDiesenTag -= abwesenheitsDauer;
+ if (sollDiesenTag < 0)
+ {
+ sollDiesenTag = 0;
+ }
+
+ faktor = 1; // Keine halben Feiertag berücksichtigen, wenn die Abwesenheit in Stunden angegeben wurde
+ }
+
+ if (faktor != 0 && !HatArbeitszeitImZeitraum(ed.Employee.Arbeitszeiten, dd.Date, dd.Date))
+ {
+ abwesenheitsDauer *= faktor; //halbe Feiertage etc. berücksichtigen
+ }
+
+ if (ed.AbsenceTimeDays != null && ed.AbsenceTimeDays.TageProWocheDict != null)
+ {
+ if (ed.AbsenceTimeDays.RestStundenProWocheDict == null)
+ {
+ ed.AbsenceTimeDays.RestStundenProWocheDict = new Dictionary();
+ }
+ foreach (var week in ed.AbsenceTimeDays.TageProWocheDict.Keys)
+ {
+ if (!ed.AbsenceTimeDays.RestStundenProWocheDict.ContainsKey(week))
+ {
+ ed.AbsenceTimeDays.RestStundenProWocheDict.Add(week, ed.SollProWoche);
+ }
+
+ if (day.Date >= week.StartDate && day.Date <= week.EndDate && checkMaxWochenSoll)
+ {
+
+ var rest = ed.AbsenceTimeDays.RestStundenProWocheDict[week];
+ if (rest < abwesenheitsDauer)
+ {
+ abwesenheitsDauer = rest;
+ }
+ rest -= abwesenheitsDauer;
+ if (rest < 0)
+ {
+ rest = 0;
+ }
+
+ ed.AbsenceTimeDays.RestStundenProWocheDict[week] = rest;
+ }
+ }
+ }
+
+
+ if (!AbsenceTimeAufIstStundenAddieren(absenceTime) && IsUrlaubAbsenceTime(absenceTime))
+ { // Vermutlich diese Methode in CustomMitarbeiterStundenKontoBerechnung überschreiben und hier Prüfung machen, ob absenceReason ohne Fortzahlung ist
+ //und wenn ja, HoursSick oder Soll oder so für den Tag auf 0 setzen
+
+ dd.HoursInVacation = abwesenheitsDauer;
+ var tspan = TimeSpan.FromHours((double)abwesenheitsDauer);
+ dd.TimeInVacation = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ }
+ else if (!AbsenceTimeAufIstStundenAddieren(absenceTime) && IsKrankheitAbsenceTime(absenceTime))
+ {
+ dd.HoursSick = abwesenheitsDauer;
+ var tspan = TimeSpan.FromHours((double)abwesenheitsDauer);
+ dd.TimeSick = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ }
+ else
+ {
+ if (!AbsenceTimeAufIstStundenAddieren(absenceTime))
+ {
+ dd.HoursOtherAbsenceTimes = abwesenheitsDauer;
+ var tspan = TimeSpan.FromHours((double)abwesenheitsDauer);
+ dd.TimeOtherAbsenceTimes = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ }
+ if (istGanztaegig)
+ {
+ //Bei ganztägigen Abwesenheiten darf nicht noch zusätzlich gearbeitet werden
+ dd.MinutesTotal = 0;
+ dd.TimeTotal = "00:00";
+ }
+ dd.MinutesTotal += abwesenheitsDauer * 60;
+ TimeSpan tspan2 = TimeSpan.FromMinutes((double)dd.MinutesTotal);
+ dd.TimeTotal = String.Format("{0:00}:{1:00}", tspan2.Hours, tspan2.Minutes);
+ }
+
+ if (dd.AbsenceTimes == null)
+ {
+ dd.AbsenceTimes = new List();
+ }
+ dd.AbsenceTimes.Add(absenceTime);
+ //dd.MinutesTotal += sollDiesenTag * 60;
+ }
+ }
+
+ }
+ }
+
+ }
+
+ public override bool IstAnrechenbareAbsenceTime(AbsenceTime item)
+ {
+ //CB Erst ab 2026 Überstundenfrei berücksichtigen
+ if (item.Start.HasValue && item.Start.Value.Date >= new DateTime(2026, 1, 1) && item.AbsenceReason != null && (item.AbsenceReason.Description.ToLower().Contains("überstundenfrei") || item.AbsenceReason.Description.ToLower().Contains("überstundenabbau") ||
+ item.AbsenceReason.Description.ToLower().Contains("ü-frei")))
+ return false;
+ else
+ return true;
+ }
+
+ public override bool IsKrankheitAbsenceTime(AbsenceTime absenceTime)
+ {
+ return !String.IsNullOrEmpty(absenceTime.AbsenceReason.Description) &&
+ (absenceTime.AbsenceReason.Description.ToLower().IndexOf("arbeitsunfähig") >= 0
+ || absenceTime.AbsenceReason.Description.ToLower().IndexOf("krank") >= 0 || absenceTime.AbsenceReason.Description.ToLower() == "au");
+ }
+
+ public override bool IsUrlaubAbsenceTime(AbsenceTime absenceTime)
+ {
+ return !String.IsNullOrEmpty(absenceTime.AbsenceReason.Description) &&
+ absenceTime.AbsenceReason.Description.ToLower().IndexOf("urlaub") >= 0;
+ }
+
+ public override decimal? GetSollStundenForAbsenceTime(MitarbeiterstundenkontoRO.EmployeeDetail ed, AbsenceTime at, DateTime datum)
+ {
+ return null;
+
+ }
+
+ public override bool AddAbsenceTimeToIst()
+ {
+ return false;
+ }
+
+
+ public override bool AddServiceRecordToDuration(ServiceRecord item)
+ {
+ if (Settings.ShowWorktime && Settings.EnableArbeitszeiterfassung)
+ {
+ return IstArbeitszeit(item);
+
+ }
+
+ return true;
+ }
+
+ public override bool IstArbeitszeit(ServiceRecord item)
+ {
+ if (item.ServiceDescription != null && item.ServiceDescription.ServiceCategory != null)
+ {
+ if (item.ServiceDescription.Name.StartsWith("Arbeitszeit") || item.ServiceDescription.ServiceCategory.Name.StartsWith("Arbeitszeit")) //Arbeitszeit und Arbeitszeiterfassung etc.
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public override decimal AddRateFactorToIst()
+ {
+ return 1;
+ }
+
+ public override MitarbeiterstundenkontoRO.EmployeeDetail BerechneUeberstundenBis(MitarbeiterstundenkontoRO.EmployeeDetail edOrig, IList records, DateTime monat, out decimal? ueberstunden, out decimal alteExtraUeberstunden)
+ {
+ MitarbeiterstundenkontoRO.EmployeeDetail edTotal = new MitarbeiterstundenkontoRO.EmployeeDetail();
+
+ ueberstunden = null;
+ alteExtraUeberstunden = 0;
+
+ var emp = edOrig.Employee;
+
+ Contract c = emp.FirstContract;
+
+
+
+ if (c != null)
+ {
+
+ Dictionary> srDict = CreateServiceRecordDictByDate(records);
+
+ DateTime start = c.EntryDate.HasValue ? c.EntryDate.Value : new DateTime(2011, 1, 1);
+ if (BerechnungsStartdatum > start)
+ {
+ start = BerechnungsStartdatum;
+ c = GetNextValidContractForDate(start, emp);
+ }
+ while (c != null && start < monat)
+ {
+ DateTime contractEnd = c.ContractEndDate.HasValue ? c.ContractEndDate.Value : DateTime.MaxValue;
+ if (contractEnd < DateTime.MaxValue)
+ {
+ contractEnd = contractEnd.Date.AddDays(1).AddTicks(-1);
+ }
+ decimal bu = 0;
+ decimal weeklyTotalHours = this.maxWochenstunden;
+ int anzWochentage = ANZ_WOCHENTAGE;
+
+
+ if (c.WeeklyTotalHours.HasValue)
+ {
+ weeklyTotalHours = c.WeeklyTotalHours.Value;
+ bu = c.WeeklyTotalHours.Value / maxWochenstunden;
+ }
+
+
+ //while (start < monat && start <= contractEnd)
+ while (start < monat && start < contractEnd)
+ {
+ if (start >= new DateTime(2024, 8, 1))
+ {
+ int test = 0;
+ }
+
+ decimal flsIst = 0;
+ decimal mittelbarIst = 0;
+ decimal minutenMonat = 0;
+ DateTime ende = new DateTime(start.Year, start.Month, 1).AddMonths(1);
+ if (contractEnd < DateTime.MaxValue && contractEnd.Date.AddDays(1) < ende)
+ ende = contractEnd.Date.AddDays(1);
+
+ IList recordsThisMonth = new List();
+ String key = String.Format("{0:yyyyMM}", start);
+
+ if (srDict.ContainsKey(key))
+ {
+ foreach (var item in srDict[key])
+ {
+ if (item.Start.HasValue && item.Start >= start && item.Start < ende)
+ {
+ if (AddServiceRecordToDuration(item))
+ {
+
+ decimal duration = GetDuration(item, edOrig);
+
+
+ minutenMonat += duration;
+
+ if (item.ServiceDescription != null &&
+ item.ServiceDescription.ServiceCategory != null &&
+ item.ServiceDescription.ServiceCategory.IsBillable)
+ flsIst += duration;
+ else
+ mittelbarIst += duration;
+
+ }
+ recordsThisMonth.Add(item);
+ }
+ }
+ }
+
+
+
+ decimal arbeitstage = GetArbeitstageImBereich(start, ende, emp);
+ var arbeitstageProWoche = anzWochentage;
+ if (c != null && c.WeeklyDays.HasValue && c.WeeklyDays.Value > 0)
+ {
+ arbeitstageProWoche = c.WeeklyDays.Value;
+ }
+
+ //if (anzWochentage != 5)
+ //{
+ // arbeitstage *= anzWochentage / 5m;
+ //}
+ var soll = GetSollNachArbeitszeiten(emp, start, ende);
+
+ if (!soll.HasValue)
+ {
+ var stundenSollProMonat = BerechneSollProMonat(emp, start, ende);
+ soll = stundenSollProMonat;
+ }
+
+ if (!soll.HasValue)
+ {
+
+ if (!soll.HasValue)
+ {
+ decimal maxSollMonat = arbeitstage * (maxWochenstunden / arbeitstageProWoche);
+ if (arbeitstageProWoche <= 5)
+ {
+ maxSollMonat = arbeitstage * (maxWochenstunden / 5);
+ }
+
+ soll = maxSollMonat * bu;
+ }
+ }
+
+ decimal sollMitarbeiter = soll ?? 0;
+
+ MitarbeiterstundenkontoRO.EmployeeDetail ed = new MitarbeiterstundenkontoRO.EmployeeDetail();
+ ed.SollProTag = weeklyTotalHours / arbeitstageProWoche;
+ ed.ArbeitstageProWoche = arbeitstageProWoche;
+ ed.ArbeitstageImBerichtszeitraum = arbeitstage;
+ ed.SollProWoche = BerechneSollProWoche(c, ed);
+
+ ed.Employee = emp;
+
+ AktuellBearbeitetesEmployeeDetail = ed;
+ var ath = GetAbsenceTimeDaysWithWorkWeeks(emp, start, ende, arbeitstageProWoche);
+ ed.AbsenceTimeDays = ath;
+
+ decimal? fehlzeit = GetFehlzeiten(records, start, ende);
+
+ if (fehlzeit.HasValue)
+ {
+ sollMitarbeiter -= fehlzeit.Value;
+
+ }
+
+ ed.StundenSollMax = sollMitarbeiter;
+
+
+ if (ath.TageGesamt.HasValue)
+ {
+ var minusStunden = BerechneStundenDurchAbwesenheiten(ath, start, ende, c, ed);
+ sollMitarbeiter -= minusStunden;
+ if (sollMitarbeiter < 0 && !StundenDurchAbwesenheitenDuerfenNegativSein())
+ {
+ sollMitarbeiter = 0;
+ }
+ //if (stundenSollProMonat.HasValue)
+ //{
+ // decimal stundenProTag = stundenSollProMonat.Value;
+ // if (arbeitstage != 0)
+ // {
+ // stundenProTag = stundenSollProMonat.Value/arbeitstage;
+ // }
+ // sollMitarbeiter -= (ath.TageGesamt.Value * stundenProTag);
+ //}
+ //else
+ //{
+ // decimal sollProWoche = maxWochenstunden*bu;
+
+
+ // var minusStunden = BerechneStundenDurchAbwesenheiten(ath, start, ende, c, sollProWoche);
+
+
+ //}
+ }
+
+ var ath2 = GetAbwesenheitenDurchServiceRecords(ed, c, records, start, ende);
+
+ if (ath2 != null)
+ {
+
+ var stunden = ath2.StundenKrankheit + ath2.StundenUrlaub + ath2.StundenSonstiges;
+
+ sollMitarbeiter -= stunden;
+
+ if (sollMitarbeiter < 0 && !StundenDurchAbwesenheitenDuerfenNegativSein())
+ {
+ sollMitarbeiter = 0;
+ }
+
+
+ }
+
+ var azDurchFeiertage = BerechneGesamtArbeitszeitDurchFeiertage(ed, start, ende);
+ sollMitarbeiter -= azDurchFeiertage;
+
+ decimal stundenMonat = minutenMonat / 60m;
+ stundenMonat = ((flsIst * AddRateFactorToIst()) + mittelbarIst) / 60m;
+
+
+ if (!ueberstunden.HasValue)
+ ueberstunden = 0;
+ ueberstunden += stundenMonat - sollMitarbeiter;
+
+ if (SollPauseBerechnen())
+ {
+ var pause1 = BerechnePauseMinuten(recordsThisMonth);
+ var pause2 = BerechneAutoPausen(recordsThisMonth);
+
+ ueberstunden -= pause1 / 60;
+ ueberstunden -= pause2 / 60;
+ }
+
+ decimal extraUeberstunden = 0;
+
+ BerechneExtraUeberstunden(emp, start, ende, out extraUeberstunden);
+ ueberstunden += extraUeberstunden;
+
+ alteExtraUeberstunden += extraUeberstunden;
+
+ if (ShouldCreateDayDetails())
+ {
+ var days = CreateDayDetails(start, ende, ed, records);
+ var ah = GetAdditionalHours(days, start, ende, ed, records);
+ ueberstunden += ah;
+ }
+
+
+ ed.StundenSollOhneUrlaubKrankheit = sollMitarbeiter;
+ ed.FlsIst = flsIst / 60;
+ ed.MittelbarIst = mittelbarIst / 60;
+ ed.StundenGesamtIst = stundenMonat;
+ ed.Ueberstunden = ueberstunden;
+ ed.UeberstundenVormonat = edTotal.Ueberstunden;
+
+ ueberstunden = BerechneUeberstunden(ed, ueberstunden, c, start, ende);
+
+ edTotal.StundenSollOhneUrlaubKrankheit += sollMitarbeiter;
+ edTotal.StundenSollMax += soll ?? 0;
+ edTotal.FlsIst += flsIst / 60;
+ edTotal.MittelbarIst += mittelbarIst / 60;
+ edTotal.StundenGesamtIst += stundenMonat;
+ edTotal.Ueberstunden = ueberstunden;
+
+ start = ende;
+ }
+
+ //if (start > contractEnd)
+ if (start >= contractEnd)
+ {
+ var ctemp = GetNextValidContractForDate(start, emp);
+ if (ctemp != null && ctemp.Oid != c.Oid)
+ {
+ c = ctemp;
+ }
+ else
+ {
+ c = null;
+ }
+
+ if (c != null && c.EntryDate.HasValue && c.EntryDate >= start)
+ start = c.EntryDate.Value;
+ else
+ {
+ start = start.AddMonths(1);
+ c = GetNextValidContractForDate(start, emp);
+ if (c != null && c.EntryDate.HasValue && c.EntryDate >= start)
+ start = c.EntryDate.Value;
+ }
+ }
+ }
+
+ }
+
+ return edTotal;
+ }
+
+ protected override decimal? BerechneUeberstunden(MitarbeiterstundenkontoRO.EmployeeDetail ed, decimal? ueberstundenStd, Contract contract, DateTime start, DateTime ende)
+ {
+ return ueberstundenStd;
+ }
+
+ public override decimal BerechneSollAmTag(MitarbeiterstundenkontoRO.EmployeeDetail ed, DateTime date)
+ {
+ decimal soll = 0;
+ var c = ed.Employee.GetValidContractForDate(date);
+
+ if (c != null)
+ {
+ int arbeitstageProWoche = ANZ_WOCHENTAGE;
+
+ if (c.WeeklyDays.HasValue && c.WeeklyDays > 0)
+ {
+ arbeitstageProWoche = c.WeeklyDays.Value;
+ }
+ if (c.WeeklyTotalHours.HasValue)
+ {
+ return c.WeeklyTotalHours.Value / arbeitstageProWoche;
+ }
+ }
+ return soll;
+ }
+ public override decimal BerechneSollProWoche(Contract c, MitarbeiterstundenkontoRO.EmployeeDetail ed)
+ {
+ if (c != null)
+ {
+ if (c.MonthlyTotalHours.HasValue && c.MonthlyTotalHours.Value > 0)
+ {
+ if (ed.ArbeitstageImBerichtszeitraum > 0)
+ {
+
+ decimal stundenProTag = GetSollArbeitszeitProArbeitstag(c, ed);
+
+ return stundenProTag * 5;
+ //return stundenProTag * ed.ArbeitstageProWoche;
+ //sollStundenProWoche = stundenProTag * ed.ArbeitstageProWoche;
+
+ //return (ath.TageGesamt.Value * stundenProTag);
+ }
+ }
+ else if (c.WeeklyTotalHours.HasValue)
+ {
+ return c.WeeklyTotalHours.Value;
+ }
+ }
+ return 0;
+ }
+
+
+
+
+ public override decimal BerechneAbwesenheitsStundenDurchArbeitszeiten(Employee emp, DateTime start, DateTime ende)
+ {
+ decimal stunden = 0;
+
+ DateTime startTemp = start;
+
+ while (startTemp < ende)
+ {
+ stunden += BerechneAlleAbwesenheitsStundenFuerArbeitszeiten(emp, startTemp);
+
+ startTemp = startTemp.AddDays(1);
+ }
+
+ return stunden;
+ }
+
+ // Claude: Zurückgesetzt auf Original-Implementierung. Die TimeSpan-Optimierung hat in Einzelfällen abweichende Werte erzeugt.
+ public override decimal BerechneAlleAbwesenheitsStundenFuerArbeitszeiten(Employee emp, DateTime tag)
+ {
+ decimal stunden = 0;
+
+ foreach (var az in emp.Arbeitszeiten)
+ {
+ if ((!az.GueltigVon.HasValue || az.GueltigVon.Value <= tag) &&
+ (!az.GueltigBis.HasValue || az.GueltigBis.Value >= tag))
+ {
+ foreach (var ae in az.ArbeitszeitEintraege)
+ {
+ if (ae.IstAmGleichenWochentag(tag))
+ {
+
+ String dateStr1 = String.Format("{0:dd.MM.yyyy} {1}", tag, ae.UhrzeitVon);
+ String dateStr2 = String.Format("{0:dd.MM.yyyy} {1}", tag, ae.UhrzeitBis);
+
+ DateTime date1;
+ DateTime date2;
+
+ if (DateTime.TryParse(dateStr1, out date1) && DateTime.TryParse(dateStr2, out date2))
+ {
+ stunden += BerechneAbwesenheitsStundenFuerArbeitstag(emp, date1, date2);
+
+ }
+ }
+ }
+ }
+ }
+
+ return stunden;
+ }
+
+ public override decimal BerechneAbwesenheitsStundenFuerArbeitstag(Employee emp, DateTime arbeitstagStart, DateTime arbeitstagEnde)
+ {
+ foreach (var at in emp.AbsenceTimes)
+ {
+ if (IstAnrechenbareAbsenceTime(at))
+ {
+ if ((!at.Start.HasValue || at.Start.Value <= arbeitstagEnde) && (!at.End.HasValue || at.End.Value >= arbeitstagStart.Date))
+ {
+ var faktor = GetFeiertagsFaktor(arbeitstagStart);
+
+ if (faktor < 1)
+ {
+ var hours = (decimal)arbeitstagEnde.Subtract(arbeitstagStart).TotalHours;
+
+ // Prüfen, ob halber Tag
+ if ((at.Start.HasValue && at.Start.Value.Date != at.Start.Value) || (at.End.HasValue && at.End.Value.Date != at.End.Value))
+ {
+ var start = at.Start;
+ var end = at.End;
+ if (!at.End.HasValue || at.End.HasValue && at.End.Value > arbeitstagEnde)
+ end = arbeitstagEnde;
+ if (!at.Start.HasValue || at.Start.HasValue && at.Start.Value < arbeitstagStart)
+ start = arbeitstagStart;
+
+ hours = Convert.ToDecimal((end.Value - start.Value).TotalHours);
+ }
+
+ if (faktor != 0)
+ {
+ hours *= faktor;
+ }
+
+ return hours;
+ }
+
+
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ //CB 12.4.18: Die Abwesenheitsstunden wurden bei Arbeitstagen < 5 früher falsch berechnet. Damit bereits erstellte Stundenkonto nicht zu veränderten Werten führen, habe
+ //ich einen Schalter eingebaut, mit dem bestimmt werden kann, ob die alte Berechnung angewendet werden soll oder nicht (Standard ist ja)
+ public override bool BerechneAlteAbwesenheitsStunden(DateTime monatAnfang)
+ {
+ if (monatAnfang < new DateTime(2018, 4, 1))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ private decimal BerechneStundenDurchAbwesenheitenAlt(AbsenceTimeDays ath, DateTime start, DateTime ende, Contract c, MitarbeiterstundenkontoRO.EmployeeDetail ed)
+ {
+
+ decimal? sollProMonat = BerechneSollProMonat(ed.Employee, start, ende);
+
+ if (sollProMonat.HasValue)
+ {
+ decimal stundenProTag = sollProMonat.Value / ed.ArbeitstageImBerichtszeitraum;
+ return (ath.TageGesamt.Value * stundenProTag);
+ }
+ else
+ {
+ decimal sollProWoche = BerechneSollProWoche(c, ed);
+ decimal stunden = 0;
+
+ if (ath.TageGesamt.HasValue)
+ {
+ if (ed.ArbeitstageProWoche == 5)
+ {
+ stunden = ath.TageGesamt.Value * (sollProWoche / ed.ArbeitstageProWoche);
+ }
+ else if (ath.TageProWocheDict != null && ed.ArbeitstageProWoche > 0)
+ {
+ foreach (var week in ath.TageProWocheDict.Keys)
+ {
+ var tage = ath.TageProWocheDict[week];
+
+ if (tage > 0)
+ {
+ if (tage > ed.ArbeitstageProWoche)
+ {
+ tage = ed.ArbeitstageProWoche;
+
+ }
+ stunden += tage * (sollProWoche / ed.ArbeitstageProWoche);
+ }
+ }
+ }
+ }
+
+ return stunden;
+ }
+ }
+
+ public override decimal GetSollArbeitszeitProArbeitstag(Contract contract, MitarbeiterstundenkontoRO.EmployeeDetail ed)
+ {
+ if (contract.MonthlyTotalHours.HasValue && contract.MonthlyTotalHours.Value > 0 && ed.ArbeitstageImBerichtszeitraum > 0)
+ {
+ return contract.MonthlyTotalHours.Value / ed.ArbeitstageImBerichtszeitraum;
+ }
+ else if (contract.WeeklyTotalHours.HasValue && ed.ArbeitstageProWoche > 0)
+ {
+ return contract.WeeklyTotalHours.Value / ed.ArbeitstageProWoche;
+ }
+
+ return 0;
+ }
+
+
+ public override Dictionary> CreateServiceRecordDictByDate(IList records)
+ {
+ var dict = new Dictionary>();
+ foreach (var item in records)
+ {
+ if (!IsServiceRecordFehlzeit(item))
+ {
+ if (item.Start.HasValue)
+ {
+ String key = String.Format("{0:yyyyMM}", item.Start.Value);
+
+ IList list;
+ if (!dict.ContainsKey(key))
+ {
+ list = new List();
+ dict.Add(key, list);
+ }
+ else
+ {
+ list = dict[key];
+ }
+ list.Add(item);
+ }
+ }
+ }
+ return dict;
+ }
+
+ public override decimal GetAdditionalHours(IList days, DateTime berichtStart, DateTime berichtEnde, MitarbeiterstundenkontoRO.EmployeeDetail ed, IList recordsAll)
+ {
+ decimal addHours = 0;
+
+ foreach (var dayDetail in days)
+ {
+ if (dayDetail.Date >= new DateTime(2023, 1, 1))
+ {
+ addHours += CalculateBreakMinutesNeu(dayDetail);
+ }
+ else
+ {
+ if (dayDetail.BreakMinutes2.HasValue && dayDetail.MaxHoursUntilBreak2.HasValue)
+ {
+ addHours += CalculateBreakMinutes(dayDetail, dayDetail.MaxHoursUntilBreak2.Value, dayDetail.BreakMinutes2.Value);
+ }
+ if (dayDetail.BreakMinutes.HasValue && dayDetail.MaxHoursUntilBreak.HasValue)
+ {
+ addHours += CalculateBreakMinutes(dayDetail, dayDetail.MaxHoursUntilBreak.Value, dayDetail.BreakMinutes.Value);
+ }
+ }
+ }
+
+ return addHours;
+ }
+
+ public override decimal CalculateBreakMinutesNeu(MitarbeiterstundenkontoRO.DayDetail dayDetail)
+ {
+ decimal addHours = 0;
+
+ if (dayDetail.ZusammenhaengendeZeitraeume != null && dayDetail.ZusammenhaengendeZeitraeume.Count > 0)
+ {
+ foreach (var span in dayDetail.ZusammenhaengendeZeitraeume)
+ {
+ addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, span.StartDateTime, span.EndDateTime) / 60;
+ }
+
+ return addHours;
+ }
+
+ if (dayDetail.EndDateTime1.HasValue && dayDetail.StartDateTime1.HasValue)
+ {
+ addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, dayDetail.StartDateTime1.Value, dayDetail.EndDateTime1.Value) / 60;
+ }
+ if (dayDetail.EndDateTime2.HasValue && dayDetail.StartDateTime2.HasValue)
+ {
+ addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, dayDetail.StartDateTime2.Value, dayDetail.EndDateTime2.Value) / 60;
+ }
+ if (dayDetail.EndDateTime3.HasValue && dayDetail.StartDateTime3.HasValue)
+ {
+ addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, dayDetail.StartDateTime3.Value, dayDetail.EndDateTime3.Value) / 60;
+ }
+ if (dayDetail.EndDateTime4.HasValue && dayDetail.StartDateTime4.HasValue)
+ {
+ addHours -= BerechneGesetzlichePauseInMinuten(dayDetail, dayDetail.StartDateTime4.Value, dayDetail.EndDateTime4.Value) / 60;
+ }
+
+ return addHours;
+ }
+
+ public override decimal BerechneGesetzlichePauseInMinuten(MitarbeiterstundenkontoRO.DayDetail dayDetail, DateTime startDateTime, DateTime endDateTime)
+ {
+ decimal arbeitsstunden = (decimal)endDateTime.Subtract(startDateTime).TotalHours;
+ decimal gesetzlichePauseMinuten = 0;
+ if (dayDetail.MaxHoursUntilBreak.HasValue && arbeitsstunden > dayDetail.MaxHoursUntilBreak)
+ {
+ gesetzlichePauseMinuten += dayDetail.BreakMinutes ?? 0;
+ }
+ arbeitsstunden -= gesetzlichePauseMinuten / 60;
+
+ if (dayDetail.MaxHoursUntilBreak2.HasValue && arbeitsstunden > dayDetail.MaxHoursUntilBreak2)
+ {
+ gesetzlichePauseMinuten += dayDetail.BreakMinutes2 ?? 0;
+ }
+
+ dayDetail.MinutesTotal -= gesetzlichePauseMinuten;
+ TimeSpan tspan = TimeSpan.FromMinutes((double)dayDetail.MinutesTotal);
+ dayDetail.TimeTotal = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ dayDetail.PauseInMinuten += gesetzlichePauseMinuten;
+
+ return gesetzlichePauseMinuten;
+ }
+
+ public override decimal CalculateBreakMinutes(MitarbeiterstundenkontoRO.DayDetail dayDetail, int maxHoursUntilBreak, int breakMinutes)
+ {
+ decimal addHours = 0;
+
+ if (dayDetail.ZusammenhaengendeZeitraeume != null && dayDetail.ZusammenhaengendeZeitraeume.Count > 0)
+ {
+ foreach (var span in dayDetail.ZusammenhaengendeZeitraeume)
+ {
+ if (span.EndDateTime.Subtract(span.StartDateTime).TotalHours > maxHoursUntilBreak)
+ {
+ addHours -= (breakMinutes / 60m);
+ dayDetail.MinutesTotal -= breakMinutes;
+ TimeSpan tspan = TimeSpan.FromMinutes((double)dayDetail.MinutesTotal);
+ dayDetail.TimeTotal = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ dayDetail.PauseInMinuten += breakMinutes;
+ }
+ }
+
+ return addHours;
+ }
+
+ if (dayDetail.EndDateTime1.HasValue && dayDetail.StartDateTime1.HasValue)
+ {
+ if (dayDetail.EndDateTime1.Value.Subtract(dayDetail.StartDateTime1.Value).TotalHours > maxHoursUntilBreak)
+ {
+ addHours -= (breakMinutes / 60m);
+ dayDetail.MinutesTotal -= breakMinutes;
+ TimeSpan tspan = TimeSpan.FromMinutes((double)dayDetail.MinutesTotal);
+ dayDetail.TimeTotal = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ dayDetail.PauseInMinuten += breakMinutes;
+ }
+ }
+ if (dayDetail.EndDateTime2.HasValue && dayDetail.StartDateTime2.HasValue)
+ {
+ if (dayDetail.EndDateTime2.Value.Subtract(dayDetail.StartDateTime2.Value).TotalHours > maxHoursUntilBreak)
+ {
+ addHours -= (breakMinutes / 60m);
+ dayDetail.MinutesTotal -= breakMinutes;
+ TimeSpan tspan = TimeSpan.FromMinutes((double)dayDetail.MinutesTotal);
+ dayDetail.TimeTotal = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ dayDetail.PauseInMinuten += breakMinutes;
+ }
+ }
+ if (dayDetail.EndDateTime3.HasValue && dayDetail.StartDateTime3.HasValue)
+ {
+ if (dayDetail.EndDateTime3.Value.Subtract(dayDetail.StartDateTime3.Value).TotalHours > maxHoursUntilBreak)
+ {
+ addHours -= (breakMinutes / 60m);
+ dayDetail.MinutesTotal -= breakMinutes;
+ TimeSpan tspan = TimeSpan.FromMinutes((double)dayDetail.MinutesTotal);
+ dayDetail.TimeTotal = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ dayDetail.PauseInMinuten += breakMinutes;
+ }
+ }
+ if (dayDetail.EndDateTime4.HasValue && dayDetail.StartDateTime4.HasValue)
+ {
+ if (dayDetail.EndDateTime4.Value.Subtract(dayDetail.StartDateTime4.Value).TotalHours > maxHoursUntilBreak)
+ {
+ addHours -= (breakMinutes / 60m);
+ dayDetail.MinutesTotal -= breakMinutes;
+ TimeSpan tspan = TimeSpan.FromMinutes((double)dayDetail.MinutesTotal);
+ dayDetail.TimeTotal = String.Format("{0:00}:{1:00}", tspan.Hours, tspan.Minutes);
+ dayDetail.PauseInMinuten += breakMinutes;
+ }
+ }
+
+ return addHours;
+ }
+
+ public override void BerechneExtraUeberstunden(Employee emp, DateTime start, DateTime ende, out decimal stunden)
+ {
+ stunden = 0;
+
+ if (emp.Overtimes != null)
+ {
+ foreach (var ot in emp.Overtimes)
+ {
+ if (ot.Date >= start && ot.Date < ende)
+ {
+ stunden += ot.Betrag;
+ }
+ }
+ }
+ }
+
+ public override Contract GetNextValidContractForDate(DateTime dt, Employee emp)
+ {
+ Contract contractNextToStartDate = emp.GetValidContractForDate(dt);
+
+ if (contractNextToStartDate == null)
+ {
+
+ foreach (var item in emp.Contracts)
+ {
+ if (item.EntryDate.HasValue && item.EntryDate.Value >= dt)
+ {
+ if (contractNextToStartDate == null || item.EntryDate.Value < contractNextToStartDate.EntryDate.Value)
+ {
+ contractNextToStartDate = item;
+ }
+ }
+
+
+ }
+ }
+ return contractNextToStartDate;
+ }
+
+ public override decimal? GetFehlzeiten(IList records, DateTime monat)
+ {
+
+ return GetFehlzeiten(records, monat, monat.AddMonths(1));
+ }
+
+ public override decimal? GetFehlzeiten(IList records, DateTime start, DateTime ende)
+ {
+ decimal? stunden = null;
+ decimal minuten = 0;
+ if (records != null)
+ {
+ foreach (var item in records)
+ {
+ if (IsServiceRecordFehlzeit(item))
+ {
+ if (item.Start.Value >= start && item.Start.Value < ende)
+ {
+ if (item.GroupRoundedDuration.HasValue)
+ {
+ minuten += item.GroupRoundedDuration.Value;
+ }
+ else
+ {
+ if (appSettings.ShowRoundedDurationInEmployeeAnalysis)
+ {
+ minuten += item.RoundedDuration;
+ }
+ else
+ {
+ minuten += (decimal)item.End.Value.Subtract(item.Start.Value).TotalMinutes;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (minuten != 0)
+ stunden = minuten / 60;
+ return stunden;
+ }
+
+
+ public override decimal GetArbeitstageImMonat(DateTime datum, Employee emp)
+ {
+ Contract c = GetNextValidContractForDate(datum, emp);
+ if (c == null)
+ return 0;
+
+ DateTime start = new DateTime(datum.Year, datum.Month, 1);
+
+ DateTime end = start.AddMonths(1);
+
+ if (c.EntryDate.HasValue)
+ {
+ if (c.EntryDate >= end)
+ return 0;
+
+ if (c.EntryDate > start)
+ start = c.EntryDate.Value;
+ }
+ if (c.ContractEndDate.HasValue && c.ContractEndDate.Value.AddDays(1) < end)
+ end = c.ContractEndDate.Value.AddDays(1);
+
+ return GetArbeitstageImBereich(start, end, emp);
+
+ }
+
+ public override decimal GetArbeitstageImBereich(DateTime von, DateTime bis, Employee emp)
+ {
+ return GetArbeitstageImBereich(von, bis, emp, false);
+
+ }
+
+ // Wenn das Flag berechneAnhandArbeitszeiten gesetzt ist, werden nur die Wochentage als Arbeitstage berechent, für die auch im Reiter Arbeitszeiten ein entsprechender Arbeitstag hinterlegt wurde.
+ // Nur relevant bei < 5 Wochenarbeitstagen
+ // Aus Kompatibilitätsgründen ist das Flag standardmäßig auf false
+ public override decimal GetArbeitstageImBereich(DateTime von, DateTime bis, Employee emp, bool berechneAnhandArbeitszeiten)
+ {
+ DateTime start = von;
+ DateTime end = bis;
+
+ if (BerechneSollNurBisZumAktuellenTag())
+ {
+ if (end > DateTime.Now.Date.AddDays(1))
+ {
+ end = DateTime.Now.Date.AddDays(1);
+ }
+ }
+
+ TimeSpan ts = end.Subtract(start);
+
+ var maxDays = (decimal)ts.TotalDays;
+
+ while (start < end)
+ {
+ int arbeitstageProWoche = ANZ_WOCHENTAGE;
+
+ var c = GetContractForDate(emp, start, start);
+
+ if (c != null && c.WeeklyDays.HasValue && c.WeeklyDays > 0)
+ {
+ arbeitstageProWoche = c.WeeklyDays.Value;
+ }
+
+
+ if (arbeitstageProWoche <= 5 && (start.DayOfWeek == DayOfWeek.Saturday || start.DayOfWeek == DayOfWeek.Sunday))
+ {
+ maxDays -= 1;
+ }
+ else if (arbeitstageProWoche == 6 && start.DayOfWeek == DayOfWeek.Sunday)
+ {
+ maxDays -= 1;
+ }
+ else
+ {
+ if (berechneAnhandArbeitszeiten && arbeitstageProWoche < 5)
+ {
+ if (GetSollNachArbeitszeitenFuerTag(emp, start) == 0)
+ {
+ maxDays--; // Kein Arbeitstag, also Anzahl um eins verringern
+ }
+ }
+ else if (!BerechneFeiertageAlsArbeitszeit())
+ {
+ maxDays -= GetFeiertagsFaktor(start);
+ }
+ }
+ start = start.AddDays(1);
+ }
+ return maxDays;
+
+ }
+
+ public override Contract GetContractForDate(Employee emp, DateTime start, DateTime ende)
+ {
+ Contract c = emp.GetValidContractForDate(start);
+ if (c == null) // Kein Vertrag vom 1. gefunden, gucken, ob es einen gibt, der mitten im Monat beginnt.
+ {
+ var c2 = GetNextValidContractForDate(start, emp);
+ if (c2 != null && c2.EntryDate.HasValue && c2.EntryDate.Value < ende)
+ c = c2;
+ }
+ return c;
+ }
+
+ //gibt 1 zurück wenn ganzer Feiertag und z.B. 0.5 bei halben etc.
+ public override decimal GetFeiertagsFaktor(DateTime start)
+ {
+ //CB 26.02.2024: Darf nicht über den VacationService gehen, sonst werden die im CustomMitarbeiterstundenkontoBerechnung hinterlegten Feiertage ignoriert
+ //return vacationService.GetFeiertagsFaktor(start);
+ if (IstFeiertag(start))
+ {
+ return 1;
+ }
+ return 0;
+ }
+
+ public override bool IstFeiertag(DateTime start)
+ {
+ // Claude: HashSet-Lookup statt linearer Suche; pro Jahr nur einmal aufgebaut
+ if (_feiertageSetCache == null)
+ {
+ _feiertageSetCache = new Dictionary>();
+ }
+ HashSet set;
+ if (!_feiertageSetCache.TryGetValue(start.Year, out set))
+ {
+ set = new HashSet(GetFeiertage(start.Year));
+ _feiertageSetCache[start.Year] = set;
+ }
+ return set.Contains(start.Date);
+ }
+
+ public override List GetFeiertageDCs(int year)
+ {
+ // Hier muss das Bundesland überprüft werden und für z.B. Hessen Allerheiligen raus
+ return vacationService.GetFeiertage(year, vacationService.Bundesland);
+ }
+
+ public override List GetFeiertage(int year)
+ {
+ // Claude: Feiertagsliste pro Jahr cachen, damit vacationService.GetFeiertage nicht wiederholt aufgerufen wird
+ if (_feiertageListeCache == null)
+ {
+ _feiertageListeCache = new Dictionary>();
+ }
+ List list;
+ if (!_feiertageListeCache.TryGetValue(year, out list))
+ {
+ list = vacationService.GetFeiertage(year);
+ _feiertageListeCache[year] = list;
+ }
+ return list;
+ }
+
+
+ protected DateTime GetOsterSonntag(int year)
+ {
+ return VacationService.GetOsterSonntag(year);
+ }
+
+ public override bool IsServiceRecordFehlzeit(ServiceRecord sr)
+ {
+ if (sr.ServiceDescription != null && sr.ServiceDescription.ServiceCategory != null && !String.IsNullOrEmpty(sr.ServiceDescription.ServiceCategory.Name) && sr.ServiceDescription.ServiceCategory.Name.ToLower().IndexOf("fehlzeit") >= 0)
+ return true;
+ return false;
+ }
+
+ public override bool BerechneFeiertageAlsArbeitszeit()
+ {
+ return false;
+ }
+
+ public override bool AbsenceTimeAufIstStundenAddieren(AbsenceTime at)
+ {
+ return false;
+ }
+
+ public override bool StundenBeiAbwesenheitBerechnen(AbsenceTime at)
+ {
+ return true;
+ }
+
+ public override decimal BerechneGesamtArbeitszeitDurchFeiertage(MitarbeiterstundenkontoRO.EmployeeDetail ed, DateTime start, DateTime ende)
+ {
+ decimal az = 0;
+ if (BerechneFeiertageAlsArbeitszeit() && ed.Employee.IsActive == ActivationTypeId.Active)
+ {
+ DateTime dt = start;
+ while (dt < ende)
+ {
+ if (GetFeiertagsFaktor(dt) > 0)
+ {
+ az += BerechneSollStundenFuerTag(ed, dt);
+ }
+ dt = dt.AddDays(1);
+ }
+ }
+ return az;
+ }
+
+ public AbsenceTime GetAbsenceTimeForDate(MitarbeiterstundenkontoRO.EmployeeDetail ed, DateTime date)
+ {
+ var absenceTimes = ed.Employee.AbsenceTimes;
+
+ if (absenceTimes != null)
+ {
+ foreach (var absenceTime in absenceTimes)
+ {
+ if (absenceTime.Start < date.AddDays(1) && (!absenceTime.End.HasValue || absenceTime.End >= date))
+ {
+ return absenceTime;
+ }
+ }
+ }
+
+ return null;
+ }
+ }
+
+ public class AbsenceTimeDays
+ {
+ public decimal? TageUrlaub;
+ public decimal? TageKrankheit;
+ public decimal? TageGesamt;
+ public decimal TageAlsIst;
+ public decimal? StundenUrlaub;
+ public decimal? StundenKrankheit;
+ public decimal? StundenGesamt;
+ public decimal StundenAlsIst;
+
+ public Dictionary TageProWocheDict;
+ public Dictionary StundenProTagDict;
+ public Dictionary RestStundenProWocheDict;
+ }
+
+}
+
diff --git a/ReportImp/ClaudeStundenkontoTest/Properties/AssemblyInfo.cs b/ReportImp/ClaudeStundenkontoTest/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..930888dd6
--- /dev/null
+++ b/ReportImp/ClaudeStundenkontoTest/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("KundeXyz")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("KundeXyz")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
+[assembly: Guid("c1fed668-adb9-47e0-b589-1389618e1b6e")]
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]