Files
BeWoPlaner/ReportImp/ClaudeStundenkontoTest/CustomMitarbeiterstundenkontoBerechnung.cs

3284 lines
133 KiB
C#

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;
using BeWo.Service.Dienstplanung;
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<int, List<DateTime>> _feiertageListeCache;
// Claude: HashSet pro Jahr für O(1)-Lookup in IstFeiertag statt linearer Suche
private Dictionary<int, HashSet<DateTime>> _feiertageSetCache;
// Claude: Cache für FeiertagDC-Objekt pro Datum (verwendet in CreateDayDetail)
private Dictionary<DateTime, FeiertagDC> _feiertagDCByDateCache;
// Claude: Cache der gesamten DienstEintraege pro Mitarbeiter, einmal pro Report-Lauf geladen
private Dictionary<long, List<DienstEintrag>> _allDienstEintraegeByEmpCache;
// Claude: Cache der gesamten Dienstvertretungen pro Mitarbeiter, einmal pro Report-Lauf geladen
private Dictionary<long, List<Dienstvertretung>> _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<IList<Arbeitszeit>, Dictionary<DateTime, bool>> _hatArbeitszeitImZeitraumCache;
// Claude: Comparer, der ausschließlich Referenz-Gleichheit nutzt (verhindert unbeabsichtigte Treffer bei NHibernate-Collections mit überschriebenem Equals)
private sealed class ReferenceEqualityComparer<T> : IEqualityComparer<T> where T : class
{
public static readonly ReferenceEqualityComparer<T> Default = new ReferenceEqualityComparer<T>();
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<VacationService>();
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<StundenkontoServiceRecord>();
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<StundenkontoServiceRecord> groupFilteredRecords = empDetail.AllServiceRecords;
if (groupFilteredRecords == null)
{
IList<ServiceRecord> recordsAll = DAOFactory.SearchDAO.FindServiceRecords(emp.Oid, null);
recordsAll = AnalysisServiceImp.FilterActiveServiceRecords(recordsAll);
//
groupFilteredRecords = new List<StundenkontoServiceRecord>();
Dictionary<long, bool> groupBookingDict = new Dictionary<long, bool>();
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<DienstEintrag>();
//var alleDienstvertretungen = new List<Dienstvertretung>();
//if (empOidNullable.HasValue)
//{
// List<DienstEintrag> deList;
// if (_allDienstEintraegeByEmpCache.TryGetValue(empOidNullable.Value, out deList))
// {
// alleDiensteintraege.AddRange(deList);
// }
// List<Dienstvertretung> 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<VacationService>();
// 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<DienstEintrag> eintraege, List<Dienstvertretung> 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<string, List<DienstEintrag>> 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<DienstEintrag> entriesThisMonth = new List<DienstEintrag>();
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<long, List<DienstEintrag>>();
foreach (var d in DAOFactory.GenericDAO.GetAllActive<DienstEintrag>())
{
List<DienstEintrag> list;
if (!_allDienstEintraegeByEmpCache.TryGetValue(d.EmployeeOid, out list))
{
list = new List<DienstEintrag>();
_allDienstEintraegeByEmpCache[d.EmployeeOid] = list;
}
list.Add(d);
}
}
if (_allDienstvertretungenByEmpCache == null)
{
_allDienstvertretungenByEmpCache = new Dictionary<long, List<Dienstvertretung>>();
foreach (var d in DAOFactory.GenericDAO.GetAllActive<Dienstvertretung>())
{
List<Dienstvertretung> list;
if (!_allDienstvertretungenByEmpCache.TryGetValue(d.EmployeeOid, out list))
{
list = new List<Dienstvertretung>();
_allDienstvertretungenByEmpCache[d.EmployeeOid] = list;
}
list.Add(d);
}
}
}
private Dictionary<string, List<DienstEintrag>> CreateDiensteintragDictByDate(List<DienstEintrag> eintraege)
{
var dict = new Dictionary<string, List<DienstEintrag>>();
foreach (var item in eintraege)
{
//if (!IsServiceRecordFehlzeit(item))
//{
string key = string.Format("{0:yyyyMM}", item.Datum);
List<DienstEintrag> list;
if (!dict.ContainsKey(key))
{
list = new List<DienstEintrag>();
dict.Add(key, list);
}
else
{
list = dict[key];
}
list.Add(item);
//}
}
return dict;
}
private List<DienstEintrag> VertretungenUmwandeln(List<Dienstvertretung> vertretungen)
{
List<DienstEintrag> eintraege = new List<DienstEintrag>();
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<StundenkontoServiceRecord> 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<Arbeitszeit> 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<Arbeitszeit> 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<IList<Arbeitszeit>, Dictionary<DateTime, bool>>(ReferenceEqualityComparer<IList<Arbeitszeit>>.Default);
}
Dictionary<DateTime, bool> innerCache;
if (!_hatArbeitszeitImZeitraumCache.TryGetValue(arbeitszeiten, out innerCache))
{
innerCache = new Dictionary<DateTime, bool>();
_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<Arbeitszeit> 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.Category.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<StundenkontoServiceRecord> groupFilteredRecords, DateTime berichtsAnfang, DateTime berichtsEnde)
{
decimal flsGesamt = 0;
decimal arbeitszeit = 0;
var sum = new ServiceRecordSummary();
sum.RecordsImBerichtszeitraum = new List<StundenkontoServiceRecord>();
empDetail.ServiceRecordsInSelectedTimeSpan = new List<StundenkontoServiceRecord>();
foreach (var item in groupFilteredRecords)
{
if (!IsServiceRecordFehlzeit(item))
{
if (item.Start >= berichtsAnfang && item.Start < 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.Category != null && item.ServiceDescription.Category.IsBillable)
{
flsGesamt += duration;
}
}
else if (AddServiceRecordToDuration(item))
{
empDetail.ServiceRecordsInSelectedTimeSpan.Add(item);
decimal duration = GetDuration(item, empDetail);
if (item.ServiceDescription != null &&
item.ServiceDescription.Category != null &&
item.ServiceDescription.Category.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.Category.ProzentStundenkonto.HasValue)
{
prozent = item.ServiceDescription.Category.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<StundenkontoServiceRecord> recordsThisMonth)
{
return 0;
}
public override decimal BerechnePauseMinuten(IList<StundenkontoServiceRecord> srList)
{
decimal pausenZeit = 0;
List<StundenkontoServiceRecord> pausen = new List<StundenkontoServiceRecord>();
List<StundenkontoServiceRecord> nichtpausen = new List<StundenkontoServiceRecord>();
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,
EndDateTime = pause.Start.AddMinutes((double)pause.RoundedDuration)
};
var arbeitszeitSpan = new DateTimeSpan
{
StartDateTime = arbeitszeit.Start,
EndDateTime = arbeitszeit.Start.AddMinutes((double)arbeitszeit.RoundedDuration)
};
if (arbeitszeit.GroupOid.HasValue)
{
arbeitszeitSpan.EndDateTime =
arbeitszeit.Start.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<MitarbeiterstundenkontoRO.EmployeeDetail> FilterEmployeeList(List<MitarbeiterstundenkontoRO.EmployeeDetail> employeeDetailList)
{
List<MitarbeiterstundenkontoRO.EmployeeDetail> newEmpList = new List<MitarbeiterstundenkontoRO.EmployeeDetail>();
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<StundenkontoServiceRecord> groupFilteredRecords, Contract contract)
{
//do nothing
}
public override void CorrectOverlappingTimes(IList<StundenkontoServiceRecord> serviceRecords)
{
List<StundenkontoServiceRecord> oldRecords = new List<StundenkontoServiceRecord>();
foreach (var newRecord in serviceRecords)
{
if (!CorrectOverlappingTime(newRecord))
{
continue;
}
if (newRecord.Start.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,
EndDateTime = newRecord.Start.AddMinutes((double)minuten)
};
foreach (var oldRecord in oldRecords)
{
if (!CorrectOverlappingTime(oldRecord, newRecord))
{
continue;
}
var oldSpan = new DateTimeSpan
{
StartDateTime = oldRecord.Start,
EndDateTime = oldRecord.Start.AddMinutes((double)oldRecord.RoundedDuration)
};
if (oldRecord.GroupRoundedDuration.HasValue)
{
oldSpan.EndDateTime = oldRecord.Start.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.Subtract(newRecord.Start).TotalMinutes;
}
else
{
newRecord.GroupRoundedDuration = (decimal)newRecord.End.Subtract(newRecord.Start).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.Subtract(oldRecord.Start).TotalMinutes;
}
else
{
oldRecord.GroupRoundedDuration = (decimal)oldRecord.End.Subtract(oldRecord.Start).TotalMinutes;
}
}
}
}
oldRecords.Add(newRecord);
}
}
public override bool CorrectOverlappingTime(StundenkontoServiceRecord newRecord)
{
return true;
}
public override bool CorrectOverlappingTime(StundenkontoServiceRecord record1, StundenkontoServiceRecord 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, StundenkontoServiceRecord 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<MitarbeiterstundenkontoRO.DayDetail> CreateDayDetails(DateTime berichtStart, DateTime berichtEnde, MitarbeiterstundenkontoRO.EmployeeDetail ed, IList<StundenkontoServiceRecord> recordsAll)
{
if (ShouldCreateDayDetailsForEachServiceRecord())
{
return CreateDayDetailsForEachServiceRecord(berichtStart, berichtEnde, ed, recordsAll);
}
Dictionary<int, MitarbeiterstundenkontoRO.DayDetail> dayToDetail = new Dictionary<int, MitarbeiterstundenkontoRO.DayDetail>();
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 >= berichtStart && sr.Start < berichtEnde)
{
if (sr.Start.Year == 2023 && sr.Start.Month == 10 && sr.Start.Day == 3)
{
int test = 0;
}
var dd = dayToDetail[sr.Start.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<StundenkontoServiceRecord>();
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<DateTimeSpan>();
}
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.Hour == 0 && sr.Start.Second > 0)
{
continue;
}
decimal duration = GetDuration(sr, ed);
DateTime dtEnd = sr.Start.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;
}
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<MitarbeiterstundenkontoRO.DayDetail> CreateDayDetailsForEachServiceRecord(DateTime berichtStart, DateTime berichtEnde, MitarbeiterstundenkontoRO.EmployeeDetail ed, IList<StundenkontoServiceRecord> recordsAll)
{
var details = new List<MitarbeiterstundenkontoRO.DayDetail>();
foreach (var sr in recordsAll)
{
if (sr.Start >= berichtStart && sr.Start < berichtEnde)
{
if (sr.Start.Day == 8)
{
int test = 0;
}
if (!IsServiceRecordFehlzeit(sr) && AddServiceRecordToDuration(sr))
{
var dd = CreateDayDetail(sr.Start, 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<StundenkontoServiceRecord>();
dd.ServiceRecords.Add(sr);
dd.Date = sr.Start;
if (sr.Start.Second == 0)
{
DateTime dtEnd = sr.Start.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(StundenkontoServiceRecord 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.Subtract(sr.Start).TotalMinutes;
}
}
if (UseServiceDescriptionPercentage())
{
decimal prozent = sr.ServiceDescription.Category.ProzentStundenkonto ?? sr.ServiceDescription.Category.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.Category.ProzentStundenkonto.HasValue)
{
prozent = sr.ServiceDescription.Category.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, StundenkontoServiceRecord 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.DayOfWeek == DayOfWeek.Sunday || sr.End.DayOfWeek == DayOfWeek.Sunday)
{
DateTime sonntag = sr.Start.Date;
if (sr.Start.DayOfWeek != DayOfWeek.Sunday && sr.End.DayOfWeek == DayOfWeek.Sunday)
{
sonntag = sr.End.Date;
}
var sonnatgsMinuten = GetOverlappingMinutes(sr.Start, sr.End, 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) || IstFeiertag(sr.End))
{
var feiertagsMinuten = duration;
DateTime feiertag = sr.Start.Date;
if (IstFeiertag(sr.Start) && !IstFeiertag(sr.End))
{
feiertagsMinuten = (decimal)GetOverlappingMinutes(sr.Start, sr.End, feiertag, feiertag.AddDays(1));
}
else if (!IstFeiertag(sr.Start) && IstFeiertag(sr.End))
{
feiertag = sr.End.Date;
feiertagsMinuten = (decimal)GetOverlappingMinutes(sr.Start, sr.End, feiertag, feiertag.AddDays(1));
}
dd.HoursHoliday += feiertagsMinuten / 60;
}
if (sr.Start.Second == 0 && dd.NightEnd.HasValue && dd.NightStart.HasValue)
{
var minuten = CheckNachtzeiten(sr.Start, 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<DateTimeSpan>();
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<DateTime, FeiertagDC>();
}
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<DateTimeSpan, decimal>();
}
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<AbsenceTime>();
}
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(StundenkontoServiceRecord item)
{
if (Settings.ShowWorktime && Settings.EnableArbeitszeiterfassung)
{
return IstArbeitszeit(item);
}
return true;
}
public override bool IstArbeitszeit(StundenkontoServiceRecord item)
{
if (item.ServiceDescription != null && item.ServiceDescription.Category != null)
{
if (item.ServiceDescription.Name.StartsWith("Arbeitszeit") || item.ServiceDescription.Category.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<StundenkontoServiceRecord> 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<String, IList<StundenkontoServiceRecord>> 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<StundenkontoServiceRecord> recordsThisMonth = new List<StundenkontoServiceRecord>();
String key = String.Format("{0:yyyyMM}", start);
if (srDict.ContainsKey(key))
{
foreach (var item in srDict[key])
{
if (item.Start >= start && item.Start < ende)
{
if (AddServiceRecordToDuration(item))
{
decimal duration = GetDuration(item, edOrig);
minutenMonat += duration;
if (item.ServiceDescription != null &&
item.ServiceDescription.Category != null &&
item.ServiceDescription.Category.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<string, IList<StundenkontoServiceRecord>> CreateServiceRecordDictByDate(IList<StundenkontoServiceRecord> records)
{
var dict = new Dictionary<string, IList<StundenkontoServiceRecord>>();
foreach (var item in records)
{
if (!IsServiceRecordFehlzeit(item))
{
String key = String.Format("{0:yyyyMM}", item.Start);
IList<StundenkontoServiceRecord> list;
if (!dict.ContainsKey(key))
{
list = new List<StundenkontoServiceRecord>();
dict.Add(key, list);
}
else
{
list = dict[key];
}
list.Add(item);
}
}
return dict;
}
public override decimal GetAdditionalHours(IList<MitarbeiterstundenkontoRO.DayDetail> days, DateTime berichtStart, DateTime berichtEnde, MitarbeiterstundenkontoRO.EmployeeDetail ed, IList<StundenkontoServiceRecord> 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<StundenkontoServiceRecord> records, DateTime monat)
{
return GetFehlzeiten(records, monat, monat.AddMonths(1));
}
public override decimal? GetFehlzeiten(IList<StundenkontoServiceRecord> 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 >= start && item.Start < ende)
{
if (item.GroupRoundedDuration.HasValue)
{
minuten += item.GroupRoundedDuration.Value;
}
else
{
if (appSettings.ShowRoundedDurationInEmployeeAnalysis)
{
minuten += item.RoundedDuration;
}
else
{
minuten += (decimal)item.End.Subtract(item.Start).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<int, HashSet<DateTime>>();
}
HashSet<DateTime> set;
if (!_feiertageSetCache.TryGetValue(start.Year, out set))
{
set = new HashSet<DateTime>(GetFeiertage(start.Year));
_feiertageSetCache[start.Year] = set;
}
return set.Contains(start.Date);
}
public override List<FeiertagDC> 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<DateTime> GetFeiertage(int year)
{
// Claude: Feiertagsliste pro Jahr cachen, damit vacationService.GetFeiertage nicht wiederholt aufgerufen wird
if (_feiertageListeCache == null)
{
_feiertageListeCache = new Dictionary<int, List<DateTime>>();
}
List<DateTime> 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(StundenkontoServiceRecord sr)
{
if (sr.ServiceDescription != null && sr.ServiceDescription.Category != null && !String.IsNullOrEmpty(sr.ServiceDescription.Category.Name) && sr.ServiceDescription.Category.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<DateTimeSpan, decimal> TageProWocheDict;
public Dictionary<DateTime, decimal> StundenProTagDict;
public Dictionary<DateTimeSpan, decimal> RestStundenProWocheDict;
}
}