using System; using System.Collections; using System.Collections.Generic; using System.Linq; using BS.Shared.Core; using BS.Shared.DataContracts; using BS.Shared.Extensions; namespace BS.Shared.Services { public class Calculations : AbstractIDSpecificDefaultClass { #region Public Methods public virtual decimal GetIntervalCountForDays(decimal totalDays, SupportConceptApprovalInterval interval) { return ConvertPerIntervall2PerDay(totalDays, interval); } public virtual decimal ConvertPerIntervall2PerDay(decimal pPerInterval, SupportConceptApprovalInterval pInterval) { switch (pInterval) { case SupportConceptApprovalInterval.Daily: return pPerInterval; case SupportConceptApprovalInterval.Weekly: return pPerInterval / 7m; case SupportConceptApprovalInterval.Fortnightly: return pPerInterval / 14m; case SupportConceptApprovalInterval.Monthly: return pPerInterval / (365m / 12m); case SupportConceptApprovalInterval.Quarterly: return pPerInterval / (365m / 4m); case SupportConceptApprovalInterval.HalfYearly: return pPerInterval / (365m / 2m); case SupportConceptApprovalInterval.Yearly: return pPerInterval / 365m; case SupportConceptApprovalInterval.Hourly: return pPerInterval * 24; default: return 0; } } public virtual decimal? GetApprovedAmountForPeriod(SupportConceptCostBearerRelDC c2sDc, DateTime? start, DateTime? end, ServiceCategoryDC cat) { // Start- und Enddate sowie Leistungskategorie sind KannFelder var dtStart = c2sDc.StartDate; var dtEnd = c2sDc.EndDate; if (start.HasValue && start > dtStart) { dtStart = start; } if (end.HasValue && end < dtEnd) { dtEnd = end; } decimal summe = 0; var orgCrList = c2sDc.CostBearer.CostRatePeriods; if (cat != null) // Im Fall, dass nur eine bestimmte Leistungskategorie berücksichtigt werden soll { foreach (var scap in c2sDc.ApprovalPeriodList) { if (scap.StartDate <= dtEnd && scap.EndDate >= dtStart && scap.ServiceCategory != null && scap.ServiceCategory.ServiceCategoryOid == cat.ServiceCategoryOid) { if (scap.ServiceCategory.CostRatePeriods != null && scap.ServiceCategory.CostRatePeriods.Count > 0) { summe += GetApprovedAmountDefaultHourlyRateForPeriod(scap, scap.ServiceCategory.CostRatePeriods, dtStart, dtEnd) ?? 0; } else { summe += GetApprovedAmountDefaultHourlyRateForPeriod(scap, orgCrList, dtStart, dtEnd) ?? 0; } } } } else { foreach (var scap in c2sDc.ApprovalPeriodList) { if (scap.StartDate <= dtEnd && scap.EndDate >= dtStart) { if (scap.ServiceCategory != null && scap.ServiceCategory.CostRatePeriods != null && scap.ServiceCategory.CostRatePeriods.Count > 0) { summe += GetApprovedAmountDefaultHourlyRateForPeriod(scap, scap.ServiceCategory.CostRatePeriods, dtStart, dtEnd) ?? 0; } else { summe += GetApprovedAmountDefaultHourlyRateForPeriod(scap, orgCrList, dtStart, dtEnd) ?? 0; } } } } return summe; } public virtual decimal? GetApprovedAmount(SupportConceptApprovalPeriodDC scap, List costRatePeriods) { decimal t1; decimal t2; if (scap.ApprovedFixedAmount.HasValue) return this.GetApprovedFixedAmount(scap, out t1, out t2); if (scap.ServiceCategory != null && scap.ServiceCategory.CostRatePeriods != null && scap.ServiceCategory.CostRatePeriods.Count > 0) { return this.GetApprovedAmountDefaultHourlyRate(scap, scap.ServiceCategory.CostRatePeriods); } return this.GetApprovedAmountDefaultHourlyRate(scap, costRatePeriods); } public virtual decimal? GetApprovedAmountDefaultHourlyRate(SupportConceptApprovalPeriodDC scap, List costRatePeriods) { return this.GetApprovedAmountDefaultHourlyRateForPeriod(scap, costRatePeriods, scap.StartDate, scap.EndDate); } public virtual decimal? GetApprovedAmountDefaultHourlyRateForPeriod(SupportConceptApprovalPeriodDC scap, List costRatePeriods, DateTime? start, DateTime? end) { if (!start.HasValue || !end.HasValue) { return null; } if (scap.ApprovedBEInterval.HasValue && scap.ApprovedBEPerInterval.HasValue) { if (scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Monthly && !scap.IsApprovedBEShifting) { var totalMonths = GetTotalMonths(start.Value, end.Value); var approvedHours = totalMonths * scap.ApprovedBEPerInterval.Value; var hourlyRate = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate, start.Value); var rateFactor = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, start.Value); decimal amount = 0; if (hourlyRate != null && hourlyRate.CostRateValue.HasValue) amount = approvedHours * hourlyRate.CostRateValue.Value; if (rateFactor != null && rateFactor.CostRateValue.HasValue) { amount = amount + (amount * rateFactor.CostRateValue.Value / 100); } return amount; } } var approvedHoursPerDay = this.GetApprovedHoursPerDay(scap, costRatePeriods); if (!approvedHoursPerDay.HasValue) { return null; } decimal? result = 0m; var day = start.Value.Date; if (end.Value.Date == DateTime.MaxValue.Date) { end = end.Value.AddDays(-1); } do { var hourlyRate = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate, day); var rateFactor = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, day); if (hourlyRate == null && rateFactor == null) { hourlyRate = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney, day); } decimal? amountThisDay = 0; if (hourlyRate != null && hourlyRate.CostRateValue.HasValue && approvedHoursPerDay.HasValue) amountThisDay = approvedHoursPerDay.Value * hourlyRate.CostRateValue.Value; if (rateFactor != null && rateFactor.CostRateValue.HasValue) { amountThisDay = amountThisDay + (amountThisDay * rateFactor.CostRateValue / 100); } result += amountThisDay; } while ((day = day.AddDays(1)) <= end.Value.Date); return result; } public virtual decimal? GetApprovedAmountPerMonthAverage(SupportConceptCostBearerRelDC relDC) { if (relDC.MonthlyPayment.HasValue) { return relDC.MonthlyPayment; } if (relDC.ApprovedStartDate.HasValue && relDC.ApprovedEndDate.HasValue) { return this.GetApprovedAmountTotal(relDC) / this.GetMonthCount(relDC.ApprovedStartDate.Value, relDC.ApprovedEndDate.Value); } return 0; } public virtual decimal? GetApprovedAmountPerMonth(SupportConceptCostBearerRelDC relDC, DateTime dt) { if (relDC.MonthlyPayment.HasValue) { return relDC.MonthlyPayment; } var scap = FindApprovalPeriodForDateAndCategory(relDC, dt, null); if (scap == null && relDC.ApprovalPeriodList != null) { //letze Periode nehmen if (relDC.ApprovalPeriodList.Count > 0) scap = relDC.ApprovalPeriodList[0]; foreach (var ap in relDC.ApprovalPeriodList) { if (!ap.EndDate.HasValue) scap = ap; else if (scap.EndDate.HasValue && ap.EndDate > scap.EndDate) scap = ap; } } if (scap != null && scap.MonthlyPayment.HasValue && scap.MonthlyPayment.Value != 0) return scap.MonthlyPayment; else { if (relDC.ApprovedStartDate.HasValue && relDC.ApprovedEndDate.HasValue) { decimal ratefactor = 1; if (relDC.CostBearer != null && relDC.CostBearer.CostRatePeriods != null) { decimal? factor = relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRateValue(CostRatePeriodType.RateFactor); if (factor.HasValue) ratefactor = (100 + factor.Value) / 100; } var monate = this.GetMonthCount(relDC.ApprovedStartDate.Value, relDC.ApprovedEndDate.Value); if (monate != 0) { return (this.GetApprovedAmountTotal(relDC) / monate) / ratefactor; } } } return 0; } public virtual SupportConceptApprovalPeriodDC FindApprovalPeriodForDateAndCategory(SupportConceptCostBearerRelDC relDC, DateTime dt, ServiceCategoryDC category) { SupportConceptApprovalPeriodDC scap = null; if (relDC.ApprovalPeriodList != null) { if (category != null) { scap = relDC.ApprovalPeriodList.FirstOrDefault( i => dt.InBetween(i.Span, true) && i.ServiceCategory != null && i.ServiceCategory.ServiceCategoryOid == category.ServiceCategoryOid); } if (scap == null) scap = relDC.ApprovalPeriodList.FirstOrDefault(i => dt.InBetween(i.Span, true)); } return scap; } public virtual decimal? GetApprovedAmountTotal(SupportConceptCostBearerRelDC relDC) { if (relDC.ApprovalPeriodList == null || !relDC.GetApprovedDuration().HasValue) { return null; } return relDC.ApprovalPeriodList.Sum(i => this.GetApprovedAmount(i, relDC.CostBearer.CostRatePeriods)); } public virtual decimal? GetApprovedBEPerWeek(SupportConceptApprovalPeriodDC scap, List costRatePeriods) { return costRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit) .GetHoursInBE(this.GetApprovedHoursPerWeek(scap, costRatePeriods)); } public virtual decimal? GetApprovedBEPerWeek(SupportConceptCostBearerRelDC relDC, DateTime date) { if (!relDC.GetApprovedDuration().HasValue) { return null; } return relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit) .GetHoursInBE(this.GetApprovedHoursPerWeek(relDC, date)); } public virtual decimal? GetApprovedBEPerWeekAverage(SupportConceptCostBearerRelDC relDC, bool useIsCalculationWithFactor) { if (!relDC.GetApprovedDuration().HasValue) { return null; } return relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit) .GetHoursInBE(this.GetApprovedHoursPerWeekAverage(relDC, useIsCalculationWithFactor)); } private decimal? GetApprovedBETotal(SupportConceptCostBearerRelDC relDC, bool useIsCalculationWithFactor) { if (!relDC.GetApprovedDuration().HasValue) { return null; } return relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit) .GetHoursInBE(this.GetApprovedHoursTotal(relDC, useIsCalculationWithFactor)); } public virtual decimal? GetApprovedBETotal(SupportConceptApprovalPeriodDC scap, List costRatePeriods) { return costRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit) .GetHoursInBE(this.GetApprovedHoursTotal(scap, costRatePeriods)); } public virtual decimal? GetApprovedFixedAmount(SupportConceptApprovalPeriodDC scap, out decimal amountPerUnit, out decimal unitCount) { return this.GetApprovedFixedAmountForPeriod(scap, scap.StartDate, scap.EndDate, out amountPerUnit, out unitCount); } public virtual decimal? GetApprovedFixedAmountForPeriod(SupportConceptApprovalPeriodDC scap, DateTime? pStart, DateTime? pEnd, out decimal amountPerUnit, out decimal unitCount) { amountPerUnit = 0; unitCount = 0; if (!pStart.HasValue || !pEnd.HasValue || !scap.ApprovedFixedAmount.HasValue || !scap.ApprovedFixedAmountInterval.HasValue) { return null; } decimal amount = 0; TimeUnit tu = TimeUnit.Day; switch (scap.ApprovedFixedAmountInterval.Value) { case SupportConceptApprovalInterval.Weekly: tu = TimeUnit.Week; break; case SupportConceptApprovalInterval.Fortnightly: tu = TimeUnit.Fortnightly; break; case SupportConceptApprovalInterval.Monthly: tu = TimeUnit.Month; break; case SupportConceptApprovalInterval.Quarterly: tu = TimeUnit.Quarter; break; case SupportConceptApprovalInterval.HalfYearly: tu = TimeUnit.Halfyear; break; case SupportConceptApprovalInterval.Yearly: tu = TimeUnit.Year; break; case SupportConceptApprovalInterval.Hourly: tu = TimeUnit.Hour; break; } DateTimeUnit du = pStart.Value.Date.GetDateTimeUnit(pEnd.Value.Date, tu); amount = scap.ApprovedFixedAmount.Value * du.UnitCount; amountPerUnit = scap.ApprovedFixedAmount.Value; unitCount = du.UnitCount; if (du.RestDays > 0) { decimal restamountperday = ConvertPerIntervall2PerDay(scap.ApprovedFixedAmount.Value, scap.ApprovedFixedAmountInterval.Value); amount += (restamountperday * du.RestDays); } return amount; } public virtual decimal GetApprovedHoursForPeriod(SupportConceptCostBearerRelDC relDC, DateTime pStart, DateTime pEnd) { var costRatePeriods = relDC.CostBearer.CostRatePeriods; decimal approvedHours = 0; DateTime start = pStart; DateTime end = pEnd; while (start <= end) { var scap = relDC.ApprovalPeriodList.FirstOrDefault(i => start.InBetween(i.Span, true)); if (scap != null) { if (scap.EndDate.HasValue && scap.EndDate.Value < end) end = scap.EndDate.Value; var periodInDays = end.Subtract(start).Days + 1; approvedHours += (this.GetApprovedHoursPerDay(scap, costRatePeriods) ?? 0) * periodInDays; } else { DateTime minStart = DateTime.MaxValue; foreach (var period in relDC.ApprovalPeriodList) { if (period.StartDate.HasValue && period.EndDate.HasValue) { if (period.StartDate.Value > start && period.StartDate.Value <= pEnd && period.StartDate.Value < minStart) { minStart = period.StartDate.Value; } } } end = minStart.AddDays(-1); } start = end.AddDays(1); end = pEnd; } return approvedHours; } public virtual decimal? GetApprovedHoursForPeriod(SupportConceptCostBearerRelDC relDC, SupportConceptApprovalPeriodDC scap, List costRatePeriods, DateTime? pStart, DateTime? pEnd, bool useIsCalculationWithFactor) { IList splittedPeriods = SplitPeriodsByRateFactors(scap, costRatePeriods, pStart, pEnd); decimal? approvedTotal = null; foreach (var ap in splittedPeriods) { decimal? approvedHours = GetApprovedHoursForPeriod(ap.SupportConceptApprovalPeriod, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd); if (approvedHours.HasValue && useIsCalculationWithFactor) { DateTime dt = DateTime.Now; if (ap.PeriodStart < dt) { dt = ap.PeriodStart; } approvedHours = ApplyIsCalculationWithFactor(relDC, approvedHours, dt); } if (approvedHours.HasValue) { if (!approvedTotal.HasValue) { approvedTotal = 0; } approvedTotal += approvedHours; } } return approvedTotal; } public virtual IList SplitPeriodsByRateFactors(SupportConceptApprovalPeriodDC scap, List costRatePeriods, DateTime? start, DateTime? end) { List list = new List(); if (start.HasValue && end.HasValue) { var rateFactorList = costRatePeriods.GetSpans(CostRatePeriodType.RateFactor, start.Value, end.Value).ToList(); if (rateFactorList.Count > 1) { DateTime startTemp = start.Value; DateTime endTemp = end.Value; for (int i = 0; i < rateFactorList.Count; i++) { if (startTemp <= end.Value) { var crp = rateFactorList.ElementAt(i).Key; if (crp.EndDate.HasValue && crp.EndDate.Value < endTemp) endTemp = crp.EndDate.Value.Date; var ap = CreateDefaultPeriod(scap, costRatePeriods, startTemp, endTemp); if (crp.CostRateValue.HasValue) ap.RateFactor = crp.CostRateValue.Value; startTemp = endTemp.AddDays(1); endTemp = end.Value; list.Add(ap); } } } } if (list.Count == 0) { list.Add(CreateDefaultPeriod(scap, costRatePeriods, start, end)); } return list; } private AccountingPeriod CreateDefaultPeriod(SupportConceptApprovalPeriodDC scap, List costRatePeriods, DateTime? pStart, DateTime? pEnd) { var ap = new AccountingPeriod() { SupportConceptApprovalPeriod = scap, CostRatesInPeriod = costRatePeriods }; if (pStart.HasValue) ap.PeriodStart = pStart.Value; if (pEnd.HasValue) ap.PeriodEnd = pEnd.Value; return ap; } //Diese Methode überschreiben, wenn die bewilligten Stunden anders als Standard berechnet werden sollen (z.B. pro Monat 4.33 * Wochen) public virtual decimal? GetApprovedHoursForPeriod(SupportConceptApprovalPeriodDC scap, List costRatePeriods, DateTime? pStart, DateTime? pEnd) { if (!pStart.HasValue || !pEnd.HasValue) { return null; } if (scap.ApprovedBEInterval.HasValue && scap.ApprovedBEPerInterval.HasValue) { if (scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Monthly) { var months = GetTotalMonths(pStart.Value, pEnd.Value); return months * scap.ApprovedBEPerInterval.Value; } else if (scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Quarterly) { var qs = GetTotalQuarters(pStart.Value, pEnd.Value); return qs * scap.ApprovedBEPerInterval.Value; } } var periodInDays = (pEnd.Value.Date - pStart.Value.Date).Days + 1; return this.GetApprovedHoursPerDay(scap, costRatePeriods) * periodInDays; } public virtual decimal GetTotalMonths(DateTime start, DateTime end) { var am = DateTimeUtils.GetTotalMonths(start, end); return am.AnzahlMonateGesamt; } public virtual decimal GetTotalQuarters(DateTime start, DateTime end) { int restTageStart = 0; int restTageEnd = 0; DateTime startDt = start; DateTime endDt = end; if (startDt.Day > 1) { var endMonat = new DateTime(startDt.Year, startDt.Month, 1).AddMonths(1).AddDays(-1); if (endDt < endMonat) { endMonat = endDt; } restTageStart = endMonat.Subtract(startDt).Days + 1; startDt = endMonat.AddDays(1); } DateTime startTemp = startDt; int fullQuarters = 0; while (startDt <= endDt) { startDt = startDt.AddMonths(3); if (startDt <= endDt.AddDays(1)) { startTemp = startDt; fullQuarters++; } } if (startTemp < endDt.AddDays(1)) { restTageEnd = endDt.Subtract(startTemp).Days + 1; } decimal quartersTotal = fullQuarters; if (restTageStart > 0) { quartersTotal += restTageStart / 90m; } if (restTageEnd > 0) { quartersTotal += restTageEnd / 90m; } return quartersTotal; } public virtual decimal? GetApprovedHoursPerDay(SupportConceptApprovalPeriodDC scap, List costRatePeriods) { if (!scap.EndDate.HasValue || !scap.StartDate.HasValue) // || scap.ApprovedFixedAmount.HasValue) { return null; } var serviceUnit = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, scap.EndDate.Value); if (scap.ApprovedBETotal.HasValue) { var scapDurationInDays = (scap.EndDate.Value - scap.StartDate.Value).Days + 1; decimal approvedBEPerDay = 0; if (scapDurationInDays != 0) approvedBEPerDay = scap.ApprovedBETotal.Value / scapDurationInDays; return serviceUnit.GetBEInMinutes(approvedBEPerDay) / 60m; } else if (scap.ApprovedBEPerInterval.HasValue) { decimal approvedBEPerDay = 0; if (scap.ApprovedBEInterval.HasValue) { if (scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Yearly) { var totalDays = (scap.EndDate.Value - scap.StartDate.Value).Days + 1; int totalYears = 0; DateTime start = scap.StartDate.Value.AddYears(1); //Anzahl Jahre ausrechnen. Nur wenn das Enddatum genau auf das Datum eines Jahres fällt while (start <= scap.EndDate.Value.AddDays(1)) { totalYears++; start = start.AddYears(1); if (start > scap.EndDate.Value.AddDays(1) && start != scap.EndDate.Value.AddDays(1).AddYears(1)) totalYears = 0; } if (totalYears > 0) { approvedBEPerDay = (scap.ApprovedBEPerInterval.Value * totalYears) / totalDays; return serviceUnit.GetBEInMinutes(approvedBEPerDay) / 60m; } } approvedBEPerDay = this.ConvertPerIntervall2PerDay(scap.ApprovedBEPerInterval.Value, scap.ApprovedBEInterval.Value); } return serviceUnit.GetBEInMinutes(approvedBEPerDay) / 60m; } return null; } public virtual decimal? GetApprovedHoursPerDay(SupportConceptCostBearerRelDC relDC, DateTime date) { if (!relDC.GetApprovedDuration().HasValue) { return null; } var scap = relDC.ApprovalPeriodList.FirstOrDefault(i => date.InBetween(i.Span, true)); return scap != null ? this.GetApprovedHoursPerDay(scap, relDC.CostBearer.CostRatePeriods) : null; } public virtual decimal? GetApprovedHoursPerWeek(SupportConceptApprovalPeriodDC scap, List costRatePeriods) { if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue && scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Weekly) { DateTime dt = DateTime.Now; if (scap.StartDate.HasValue && scap.StartDate.Value > dt) { dt = scap.StartDate.Value; } if (scap.EndDate.HasValue && scap.EndDate.Value < dt) { dt = scap.EndDate.Value; } var serviceUnit = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, dt); if (serviceUnit != null) return serviceUnit.GetBEInHours(scap.ApprovedBEPerInterval); } return this.GetApprovedHoursPerDay(scap, costRatePeriods) * 7; } public virtual decimal? GetApprovedHoursPerWeek(SupportConceptApprovalPeriodDC scap, List costRatePeriods, bool useIsCalculationWithFactor) { if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue && scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Weekly) { var serviceUnit = costRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, DateTime.Now); if (serviceUnit != null) return serviceUnit.GetBEInHours(scap.ApprovedBEPerInterval); } return this.GetApprovedHoursPerDay(scap, costRatePeriods) * 7; } public virtual decimal? GetApprovedHoursPerWeek(SupportConceptCostBearerRelDC relDC, SupportConceptApprovalPeriodDC scap, List costRatePeriods, bool useIsCalculationWithFactor) { decimal? hoursPerWeek = GetApprovedHoursPerWeek(scap, costRatePeriods); if (hoursPerWeek.HasValue && useIsCalculationWithFactor) hoursPerWeek = ApplyIsCalculationWithFactor(relDC, scap, hoursPerWeek); return hoursPerWeek; } public virtual decimal? GetApprovedHoursPerWeek(SupportConceptCostBearerRelDC relDC, DateTime date) { return GetApprovedHoursPerWeek(relDC, date, true); } public virtual decimal? GetApprovedHoursPerWeek(SupportConceptCostBearerRelDC relDC, DateTime date, bool useIsCalculationWithFactor) { if (!relDC.GetApprovedDuration().HasValue) { return null; } var scap = relDC.ApprovalPeriodList.FirstOrDefault(i => date.InBetween(i.Span, true)); decimal? hoursPerWeek = null; if (scap != null) { hoursPerWeek = this.GetApprovedHoursPerWeek(scap, relDC.CostBearer.CostRatePeriods); if (useIsCalculationWithFactor) hoursPerWeek = ApplyIsCalculationWithFactor(relDC, hoursPerWeek); } return hoursPerWeek; } public virtual decimal? ApplyIsCalculationWithFactor(SupportConceptCostBearerRelDC relDC, SupportConceptApprovalPeriodDC scap, decimal? valueToApply) { DateTime dt = DateTime.Now; if (scap.StartDate.HasValue && scap.StartDate.Value > dt) { dt = scap.StartDate.Value; } if (scap.EndDate.HasValue && scap.EndDate.Value < dt) { dt = scap.EndDate.Value; } return ApplyIsCalculationWithFactor(relDC, valueToApply, dt); } public virtual decimal? ApplyIsCalculationWithFactor(SupportConceptCostBearerRelDC relDC, decimal? valueToApply) { return ApplyIsCalculationWithFactor(relDC, valueToApply, DateTime.Now); } public virtual decimal? ApplyIsCalculationWithFactor(SupportConceptCostBearerRelDC relDC, decimal? valueToApply, DateTime date) { if (valueToApply.HasValue && relDC.CostBearer != null && relDC.CostBearer.IsCalculatingWithFactor) { if (relDC.CostBearer.CostRatePeriods != null) { decimal? rateFactor = relDC.CostBearer.CostRatePeriods.GetValidRateValueForDate(CostRatePeriodType.RateFactor, date); if (rateFactor.HasValue && rateFactor.Value > 0) valueToApply = (valueToApply.Value * 100) / (rateFactor.Value + 100); } } return valueToApply; } public virtual decimal? GetApprovedHoursPerWeekAverage(SupportConceptCostBearerRelDC relDC, bool useIsCalculationWithFactor) { var duration = relDC.GetApprovedDuration(); if (!duration.HasValue) { return null; } if (relDC.ApprovalPeriodList != null && relDC.ApprovalPeriodList.Count == 1) { decimal? hoursPerWeek = GetApprovedHoursPerWeek(relDC.ApprovalPeriodList[0], relDC.CostBearer.CostRatePeriods); if (useIsCalculationWithFactor) hoursPerWeek = ApplyIsCalculationWithFactor(relDC, hoursPerWeek); return hoursPerWeek; } if (duration.Value.Days != 0) { var averagePerDay = this.GetApprovedHoursTotal(relDC, useIsCalculationWithFactor) / duration.Value.Days; return averagePerDay * 7; } return null; } public virtual decimal? GetApprovedHoursTotal(SupportConceptCostBearerRelDC relDC, bool useIsCalculationWithFactor) { if (relDC.ApprovalPeriodList == null || !relDC.GetApprovedDuration().HasValue) { return null; } decimal? total = null; foreach (var scap in relDC.ApprovalPeriodList) { IList splittedPeriods = SplitPeriodsByRateFactors(scap, relDC.CostBearer.CostRatePeriods, scap.StartDate, scap.EndDate); foreach (var ap in splittedPeriods) { decimal? hours = this.GetApprovedHoursForPeriod(ap.SupportConceptApprovalPeriod, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd); if (useIsCalculationWithFactor) { DateTime dt = DateTime.Now; if (ap.PeriodStart < dt) { dt = ap.PeriodStart; } hours = ApplyIsCalculationWithFactor(relDC, hours, dt); } if (hours.HasValue) { if (!total.HasValue) { total = 0; } total += hours; } } } //decimal? hoursTotal = relDC.ApprovalPeriodList.Sum(i => this.GetApprovedHoursTotal(i, relDC.CostBearer.CostRatePeriods)); //if (useIsCalculationWithFactor) // hoursTotal = ApplyIsCalculationWithFactor(relDC, hoursTotal); if (total.HasValue) total = Math.Round(total.Value, 10, MidpointRounding.AwayFromZero); return total; } public virtual decimal? GetApprovedHoursTotal(SupportConceptApprovalPeriodDC scap, List costRatePeriods) { IList splittedPeriods = SplitPeriodsByRateFactors(scap, costRatePeriods, scap.StartDate, scap.EndDate); decimal? total = null; foreach (var ap in splittedPeriods) { decimal? hours = this.GetApprovedHoursForPeriod(ap.SupportConceptApprovalPeriod, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd); if (hours.HasValue) { if (!total.HasValue) { total = 0; } total += hours; } } return total; } public virtual BillingData GetBillingData(ServiceRecordDC record) { BillingData data = new BillingData(); data.BillingPeriodStart = record.Start; data.BillingPeriodEnd = record.End; CostRatePeriodDC hourlyRate = null; CostRatePeriodDC flatRate = null; bool useRatefactor = true; if (record.ServiceDescription.Category.IsBillable) { var crpList = record.ServiceDescription.CostRatePeriods; var ai = record.ServiceDescription.AccountingInterval; if (crpList.Count(c => c.CostRateValue.HasValue) == 0) { crpList = record.ServiceDescription.Category.CostRatePeriods; ai = record.ServiceDescription.Category.AccountingInterval; } if (ai == null) { ai = AccountingIntervalType.Hourly; } if (crpList.Count(c => c.CostRateValue.HasValue) > 0) { CostRatePeriodDC crp = crpList.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney, record.Start.Value); if (ai.HasValue) { if (ai.Value == AccountingIntervalType.Hourly) hourlyRate = crp; else if (ai.Value == AccountingIntervalType.FlatRate) flatRate = crp; else if (ai.Value == AccountingIntervalType.Daily) { double days = 0; if (data.BillingPeriodStart.HasValue && data.BillingPeriodEnd.HasValue) { days = data.BillingPeriodEnd.Value.Subtract(data.BillingPeriodStart.Value).Days + 1; } data.UnitCount = (decimal)days; data.AmountPerUnit = crp.CostRateValue ?? 0; data.AmountTotal = data.UnitCount * data.AmountPerUnit; data.AccountingInterval = AccountingIntervalType.Daily; data.GrossAmountTotal = data.AmountTotal; return data; } else if (ai.Value == AccountingIntervalType.Monthly) { AnzahlMonate am = null; data.UnitCount = 1; if (data.BillingPeriodStart.HasValue && data.BillingPeriodEnd.HasValue) { am = DateTimeUtils.GetTotalMonths(data.BillingPeriodStart.Value, data.BillingPeriodEnd.Value); decimal monate = Math.Ceiling(am.AnzahlMonateGesamt); if (monate > data.UnitCount) { data.UnitCount = monate; } } data.AmountPerUnit = crp.CostRateValue ?? 0; data.AmountTotal = data.UnitCount * data.AmountPerUnit; data.AccountingInterval = AccountingIntervalType.Monthly; data.GrossAmountTotal = data.AmountTotal; return data; } } } } if (hourlyRate != null || flatRate != null) useRatefactor = false; if (flatRate != null) { if (record.DistanceInMeterDecimal.HasValue && record.DistanceInMeterDecimal.Value > 0) { data.UnitCount = record.DistanceInMeterDecimal.Value; } else { data.UnitCount = 1; } data.AmountPerUnit = flatRate.CostRateValue; data.AmountTotal = data.UnitCount * data.AmountPerUnit; data.AccountingInterval = AccountingIntervalType.FlatRate; } else { if (hourlyRate == null) hourlyRate = this.GetHourlyRatePeriodForServiceRecord(record); decimal rdMinutes = this.GetBillableDurationInMinutes(record, false); data.UnitCount = rdMinutes / 60m; if (hourlyRate != null) { var prozent = record.ServiceDescription.Category.Percentage; if (record.ServiceDescription.ProzentAbrechnung.HasValue) { prozent = record.ServiceDescription.ProzentAbrechnung.Value; } data.AmountPerUnit = hourlyRate.CostRateValue * prozent / 100 ; } data.AmountTotal = data.AmountPerUnit * data.UnitCount; data.AccountingInterval = AccountingIntervalType.Hourly; } if (useRatefactor) { var rateFactor = record.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, record.Start.Value); if (rateFactor != null && rateFactor.CostRateValue.HasValue) { data.RateFactor = rateFactor.CostRateValue; data.GrossAmountTotal = data.AmountTotal + (data.AmountTotal * rateFactor.CostRateValue / 100); } } if (!data.GrossAmountTotal.HasValue) data.GrossAmountTotal = data.AmountTotal; return data; } public virtual decimal? GetBillableAmount(ServiceRecordDC record) { var rdMinutes = this.GetBillableDurationInMinutes(record, false); var hourlyRate = this.GetHourlyRatePeriodForServiceRecord(record); decimal amount = 0; if (hourlyRate != null && hourlyRate.CostRateValue.HasValue) amount = (rdMinutes / 60m) * hourlyRate.CostRateValue.Value; var rateFactor = record.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, record.Start.Value); if (rateFactor != null && rateFactor.CostRateValue.HasValue) { return amount + (amount * rateFactor.CostRateValue / 100); } return amount; } public virtual decimal GetBillableDurationInMinutes(ServiceRecordDC record) { return GetBillableDurationInMinutes(record, false); } public virtual decimal GetBillableDurationInMinutes(ServiceRecordDC record, bool useProzentAbrechenbar) { if (!record.ServiceDescription.Category.IsBillable) { return 0; } decimal rdMinutes = record.RoundedDuration; //var prozent = record.ServiceDescription.Category.ProzentBudget ?? record.ServiceDescription.Category.Percentage; //if (record.ServiceDescription.ProzentBudget.HasValue) //{ // prozent = record.ServiceDescription.ProzentBudget.Value; //} decimal prozent = 100; if (useProzentAbrechenbar) { if (record.ServiceDescription.ProzentAbrechnung.HasValue) { prozent = record.ServiceDescription.ProzentAbrechnung.Value; } else { prozent = record.ServiceDescription.Category.Percentage; } } rdMinutes *= (prozent / 100); if (record.GroupEmployeeCount.HasValue) rdMinutes /= record.GroupEmployeeCount.Value; return rdMinutes; } public virtual decimal GetDocumentedDurationInMinutes(ServiceRecordDC record, bool useProzentBudget) { if (!record.ServiceDescription.Category.IsBillable) { return 0; } decimal rdMinutes = record.RoundedDuration; decimal prozent = 100; if (useProzentBudget) { if (record.ServiceDescription.ProzentBudget.HasValue) { prozent = record.ServiceDescription.ProzentBudget.Value; } else { prozent = record.ServiceDescription.Category.Percentage; } } rdMinutes *= (prozent / 100); if (record.GroupEmployeeCount.HasValue) rdMinutes /= record.GroupEmployeeCount.Value; return rdMinutes; } //public virtual decimal GetBillableDurationInMinutes(ServiceRecordDC record) //{ // if (!record.ServiceDescription.Category.IsBillable) // { // return 0; // } // decimal rdMinutes = record.RoundedDuration; // var prozent = record.ServiceDescription.Category.ProzentBudget ?? record.ServiceDescription.Category.Percentage; // if (record.ServiceDescription.ProzentBudget.HasValue) // { // prozent = record.ServiceDescription.ProzentBudget.Value; // } // rdMinutes *= (prozent / 100); // if (record.GroupEmployeeCount.HasValue) // rdMinutes /= record.GroupEmployeeCount.Value; // return rdMinutes; //} //public virtual decimal GetBillableDurationInMinutes(ServiceRecordDC record) //{ // if (!record.ServiceDescription.Category.IsBillable) // { // return 0; // } // decimal rdMinutes = record.RoundedDuration; // decimal prozent = record.ServiceDescription.Category.ProzentBudget ?? record.ServiceDescription.Category.Percentage; // if (record.ServiceDescription.ProzentBudget.HasValue) // { // prozent = record.ServiceDescription.ProzentBudget.Value; // } // rdMinutes *= (prozent / 100); // if (record.GroupEmployeeCount.HasValue) // rdMinutes /= record.GroupEmployeeCount.Value; // return rdMinutes; //} public virtual decimal GetBillableDurationInMinutes(List records) { return records.Sum(i => GetBillableDurationInMinutes(i, true)); } public virtual decimal GetDocumentedDurationInMinutes(List records) // Selbe wie GetBillableDurationInMinutes() aber mit ProzentBudget { return records.Sum(i => GetDocumentedDurationInMinutes(i, true)); } public virtual decimal GetDurationInMinutesForBillableAmount(ServiceRecordDC record, decimal amount) { var hourlyRate = this.GetHourlyRatePeriodForServiceRecord(record); var rateFactor = record.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, record.Start.Value); if (rateFactor != null && rateFactor.CostRateValue.HasValue) { amount = (amount / (100 + rateFactor.CostRateValue.Value)) * 100; } if (hourlyRate != null && hourlyRate.CostRateValue.HasValue) return (amount / hourlyRate.CostRateValue.Value) * 60m; return 0; } public virtual CostRatePeriodDC GetHourlyRatePeriodForServiceRecord(ServiceRecordDC record) { var qualificationOIDs = record.Employee.ValueListEntries .Where(i => i.Value == ValueListEntryType.StaffQualificationsType) .Select(i => i.Key) .ToList(); var qualificationHourlyRates = record.CostBearer.CostRatePeriods.Where(i => i.CostRateType == CostRatePeriodType.HourlyRate && i.ValueListEntry != null && i.ValueListEntry.Type == ValueListEntryType.StaffQualificationsType); var matchingQualifications = qualificationHourlyRates .Where(i => qualificationOIDs.Contains(i.ValueListEntry.ValueListEntryOid.Value)) .ToList(); var applyingQualificationRates = matchingQualifications .GetCostRatePeriodForDateGroupedByValueListEntryOid(CostRatePeriodType.HourlyRate, record.Start.Value); if (applyingQualificationRates.Count > 0) { var maxAmount = applyingQualificationRates.Max(i => i.Value.CostRateValue); return applyingQualificationRates.First(i => i.Value.CostRateValue.Equals(maxAmount)).Value; } return record.CostBearer.CostRatePeriods .GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate, record.Start.Value); } public virtual decimal GetHoursNotBillableDueToAbsenceTimes(SupportConceptCostBearerRelDC sc2cb, List records, Dictionary absenceTimes, bool doRemoveFromRecordsList, bool doCrop) { return sc2cb.ApprovalPeriodList.Sum(i => this.GetHoursNotBillableDueToAbsenceTimes(i, sc2cb.CostBearer.CostRatePeriods, records, absenceTimes, doRemoveFromRecordsList, doCrop)); } public virtual decimal GetHoursNotBillableDueToAbsenceTimes(SupportConceptApprovalPeriodDC scap, List costRates, List records, Dictionary absenceTimes, bool doRemoveFromRecordsList, bool doCrop) { if (scap.ApprovedFixedAmount.HasValue) { return 0m; } var hoursNotBillableAbsence = 0m; foreach (var iRecord in records.ToList()) { // falls mehrere Bewilligungen bestehen, dürfen Zeiterfassungeinträge nur einmal berücksichtigt werden! if (!iRecord.Start.Value.InBetween(scap.Span, true)) { continue; } var absenceSpan = absenceTimes.FirstOrDefault(i => iRecord.Start.Value.InBetween(i.Key, true)).Key; if (absenceSpan != null) { var rd = this.GetBillableDurationInMinutes(iRecord, false); if (absenceTimes[absenceSpan] == 0) { if (doRemoveFromRecordsList) { records.Remove(iRecord); } hoursNotBillableAbsence += rd / 60m; continue; } if (absenceTimes[absenceSpan] - rd >= 0) { absenceTimes[absenceSpan] -= rd; continue; } hoursNotBillableAbsence += (rd - absenceTimes[absenceSpan]) / 60; if (!doCrop) { if (doRemoveFromRecordsList) { records.Remove(iRecord); } continue; } iRecord.End = iRecord.Start.Value.AddMinutes(Convert.ToDouble(absenceTimes[absenceSpan])); var minuteInterval = costRates.GetCostRatePeriodForDate(CostRatePeriodType.MinutesIntervall, iRecord.Start.Value); var duration = Convert.ToDecimal((iRecord.End - iRecord.Start).Value.TotalMinutes); //###CB 5min if (minuteInterval != null && minuteInterval.CostRateValue.HasValue && minuteInterval.CostRateValue.Value != 0) { iRecord.RoundedDuration = GetRoundedDuration((int)minuteInterval.CostRateValue.Value, duration); } else { iRecord.RoundedDuration = duration; } absenceTimes[absenceSpan] = 0; } } return hoursNotBillableAbsence; } public virtual decimal GetHoursNotBillableDueToMoreThanApproved(SupportConceptCostBearerRelDC sc2cb, List records, bool doRemoveFromRecordsList, bool doCrop) { return sc2cb.ApprovalPeriodList.Sum(i => this.GetHoursNotBillableDueToMoreThanApproved(i, sc2cb.CostBearer.CostRatePeriods, records, doRemoveFromRecordsList, doCrop)); } public virtual decimal GetHoursNotBillableDueToMoreThanApproved(SupportConceptApprovalPeriodDC scap, List costRates, List records, bool doRemoveFromList, bool doCrop) { if (scap.ApprovedFixedAmount.HasValue) { return 0m; } var approvedAmountCounter = this.GetApprovedAmountDefaultHourlyRate(scap, costRates) ?? 0m; var hoursNotBillableNotApproved = 0m; foreach (var iRecord in records.ToList()) { if (!iRecord.Start.Value.InBetween(scap.Span, true)) { if (doRemoveFromList) { records.Remove(iRecord); } continue; } var amount = this.GetBillableAmount(iRecord) ?? 0m; if (approvedAmountCounter - amount >= 0) { approvedAmountCounter -= amount; continue; } if (approvedAmountCounter == 0 || !doCrop) { if (doRemoveFromList) { records.Remove(iRecord); } hoursNotBillableNotApproved += this.GetBillableDurationInMinutes(iRecord, false) / 60m; continue; } var oldDuration = this.GetBillableDurationInMinutes(iRecord, false); iRecord.RoundedDuration = this.GetDurationInMinutesForBillableAmount(iRecord, approvedAmountCounter); iRecord.End = iRecord.Start.Value.AddMinutes(Convert.ToInt32(iRecord.RoundedDuration)); hoursNotBillableNotApproved += (oldDuration - iRecord.RoundedDuration) / 60m; approvedAmountCounter = 0; } return hoursNotBillableNotApproved; } public virtual decimal GetHoursNotBillableDueToOutOfSpan(SupportConceptCostBearerRelDC relDC, List records) { decimal hoursOutOfSpan = 0m; DateTimeSpan span = new DateTimeSpan(); span.StartDate = relDC.StartDate ?? DateTime.MinValue; span.EndDate = relDC.EndDate ?? DateTime.MaxValue; foreach (var iRecord in records) { if (!iRecord.Start.Value.InBetween(span, true)) { hoursOutOfSpan += this.GetBillableDurationInMinutes(iRecord, true) / 60m; } } return hoursOutOfSpan; } public virtual decimal GetMonthCount(DateTime startDate, DateTime endDate) { var am = DateTimeUtils.GetTotalMonths(startDate, endDate); return am.AnzahlMonateGesamt; } public virtual decimal? GetApprovedHoursTillNow(SupportConceptCostBearerRelDC relDC, DateTime? appointedDate) { decimal? totalApproved = null; if (relDC.ApprovalPeriodList != null) { if (relDC.ApprovalPeriodList.Count == 1) { decimal? flsTotal = GetApprovedHoursTotal(relDC, true); decimal durationInWeeks = GetDurationInWeeks(relDC) ?? 0; decimal durationInWeeksTillNow = GetDurationInWeeksTillNow(relDC, appointedDate) ?? 0; if (flsTotal.HasValue && durationInWeeks != 0) { if (Math.Round(durationInWeeks, 10) == Math.Round(durationInWeeksTillNow, 10)) return flsTotal.Value; totalApproved = flsTotal.Value * ((durationInWeeksTillNow + (1m / 7m)) / durationInWeeks); } else { var approvedPerWeek = GetApprovedHoursPerWeekAverage(relDC, true) ?? 0m; totalApproved = approvedPerWeek * (durationInWeeksTillNow + (1m / 7m)); } } else if (relDC.ApprovalPeriodList.Count > 1) { DateTime calcDate = (appointedDate.HasValue && appointedDate.Value < DateTime.Now.Date) ? appointedDate.Value : DateTime.Now.Date; totalApproved = 0; foreach (var period in relDC.ApprovalPeriodList) { if (period.StartDate.HasValue && period.EndDate.HasValue && calcDate >= period.StartDate) { var approvedHoursPerDay = GetApprovedHoursPerDay(period, relDC.CostBearer.CostRatePeriods) ?? 0; DateTime endDate = calcDate; if (period.EndDate < endDate) { endDate = period.EndDate.Value; } decimal days = Convert.ToDecimal((endDate - period.StartDate.Value).Days + 1); if (relDC.CostBearer.IsCalculatingWithFactor && relDC.CostBearer.CostRatePeriods != null) { var rateFactorsInSpan = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.RateFactor, period.StartDate.Value.Date, endDate).ToList(); if (rateFactorsInSpan.Count > 1) { days = 0; //Verhindere weitere Addition unten foreach (var keyValuePair in rateFactorsInSpan) { var span = keyValuePair.Value; var rf = keyValuePair.Key; if (rf.CostRateValue.HasValue) { var spanDays = (span.EndDate - span.StartDate).Days + 1; var approvedHoursPerDayRf = (approvedHoursPerDay * 100) / (rf.CostRateValue.Value + 100); totalApproved += (spanDays * approvedHoursPerDayRf); } } } else if (rateFactorsInSpan.Count == 1) { decimal? rateFactor = rateFactorsInSpan[0].Key.CostRateValue; if (rateFactor.HasValue && rateFactor.Value > 0) approvedHoursPerDay = (approvedHoursPerDay * 100) / (rateFactor.Value + 100); } //decimal? rateFactor = // relDC.CostBearer.CostRatePeriods.GetCurrentlyValidRateValue(CostRatePeriodType.RateFactor); //if (rateFactor.HasValue && rateFactor.Value > 0) // approvedHoursPerDay = (approvedHoursPerDay * 100) / (rateFactor.Value + 100); } totalApproved += (days * approvedHoursPerDay); } } } } return totalApproved; } public virtual decimal? GetApprovedHoursTillNow(SupportConceptApprovalPeriodDC scap, List costRates, DateTime? appointedDate, bool excludeRateFactor) { decimal? totalApproved = null; DateTime calcDate = (appointedDate.HasValue && appointedDate.Value < DateTime.Now.Date) ? appointedDate.Value : DateTime.Now.Date; if (scap.StartDate.HasValue && scap.EndDate.HasValue && calcDate >= scap.StartDate) { decimal days; var approvedHoursPerDay = GetApprovedHoursPerDay(scap, costRates) ?? 0; if (excludeRateFactor) { decimal? rateFactor = costRates.GetCurrentlyValidRateValue(CostRatePeriodType.RateFactor); if (rateFactor.HasValue && rateFactor.Value > 0) approvedHoursPerDay = (approvedHoursPerDay * 100) / (rateFactor.Value + 100); } if (calcDate >= scap.EndDate.Value) { days = Convert.ToDecimal((scap.EndDate.Value - scap.StartDate.Value).Days + 1); } else { days = Convert.ToDecimal((calcDate - scap.StartDate.Value).Days + 1); } totalApproved = (days * approvedHoursPerDay); } return totalApproved; } #endregion public virtual decimal? GetDurationInWeeks(SupportConceptCostBearerRelDC relDC) { if (relDC.StartDate != null && relDC.EndDate != null) { return Convert.ToDecimal((relDC.EndDate.Value - relDC.StartDate.Value).Days + 1) / 7; } return null; } public virtual decimal? GetDurationInWeeksTillNow(SupportConceptCostBearerRelDC relDC, DateTime? appointedDate) { if (relDC.StartDate != null && relDC.EndDate != null) { DateTime lCalcDate = (appointedDate.HasValue && appointedDate.Value < DateTime.Now.Date) ? appointedDate.Value : DateTime.Now.Date; lCalcDate = lCalcDate > relDC.EndDate ? relDC.EndDate.Value.Date.AddDays(1) : lCalcDate; return Convert.ToDecimal((lCalcDate - relDC.StartDate.Value).Days) / 7m; } return null; } public virtual IList GetFixedBillingData(SupportConceptApprovalPeriodDC supportConceptApprovalPeriod, DateTime? start, DateTime? end) { IList billingDatas = new List(); DateTime? dtStart = start ?? supportConceptApprovalPeriod.StartDate; DateTime? dtEnd = end ?? supportConceptApprovalPeriod.EndDate; if (dtStart.HasValue && dtEnd.HasValue) { decimal amountPerUnit; decimal unitCount; decimal? approvalPeriodFixedAmount = GetApprovedFixedAmountForPeriod(supportConceptApprovalPeriod, dtStart, dtEnd, out amountPerUnit, out unitCount); if (approvalPeriodFixedAmount.HasValue) { billingDatas.Add(new BillingData() { AmountTotal = approvalPeriodFixedAmount, GrossAmountTotal = approvalPeriodFixedAmount, BillingPeriodStart = dtStart, BillingPeriodEnd = dtEnd, AccountingInterval = AccountingIntervalType.FlatRate, AmountPerUnit = amountPerUnit, UnitCount = unitCount }); } else if (supportConceptApprovalPeriod.ServiceCategory != null) { var crpList = supportConceptApprovalPeriod.ServiceCategory.CostRatePeriods; var ai = supportConceptApprovalPeriod.ServiceCategory.AccountingInterval; if (ai.HasValue) { DateTime intervalStart = dtStart.Value; DateTime intervalEnd = dtStart.Value; while (intervalStart <= dtEnd) { switch (ai.Value) { case AccountingIntervalType.Daily: intervalEnd = intervalStart.AddDays(1); break; case AccountingIntervalType.Weekly: intervalEnd = intervalStart.AddDays(7); break; case AccountingIntervalType.Fortnightly: intervalEnd = intervalStart.AddDays(14); break; case AccountingIntervalType.Monthly: intervalEnd = intervalStart.AddMonths(1); break; case AccountingIntervalType.Quarterly: intervalEnd = intervalStart.AddMonths(3); break; case AccountingIntervalType.HalfYearly: intervalEnd = intervalStart.AddMonths(6); break; case AccountingIntervalType.Yearly: intervalEnd = intervalStart.AddYears(1); break; case AccountingIntervalType.FlatRate: intervalEnd = dtEnd.Value.AddDays(1); break; default: intervalEnd = dtEnd.Value.AddDays(1); break; } CostRatePeriodDC crp = crpList.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney, intervalStart); if (crp != null && crp.CostRateValue.HasValue) { billingDatas.Add(new BillingData() { AmountTotal = crp.CostRateValue, GrossAmountTotal = crp.CostRateValue, BillingPeriodStart = intervalStart, BillingPeriodEnd = intervalEnd.AddDays(-1), AccountingInterval = ai }); } intervalStart = intervalEnd; } } } } return billingDatas; } public virtual List GetAccountingPeriods(SupportConceptCostBearerRelDC relDC, List recordDCS, DateTimeSpan span) { Dictionary groupBookingDict = new Dictionary(); List apList = new List(); Dictionary ap2apDict = new Dictionary(); foreach (var sr in recordDCS) { if (sr.ServiceDescription.Category.IsBillable && sr.Start.HasValue) { if (sr.GroupOid == null || !groupBookingDict.ContainsKey(sr.GroupOid.Value)) { AccountingPeriod ap = GetAccountingPeriodForServiceRecord(relDC, sr, span); if (ap != null) { if (ap2apDict.ContainsKey(ap)) { ap2apDict[ap].ServiceRecordsInPeriod.Add(sr); } else { ap2apDict.Add(ap, ap); apList.Add(ap); } } if (sr.GroupOid != null && !groupBookingDict.ContainsKey(sr.GroupOid.Value)) { groupBookingDict.Add(sr.GroupOid.Value, true); } } } } return apList; } public virtual AccountingPeriod GetAccountingPeriodForServiceRecord(SupportConceptCostBearerRelDC relDC, ServiceRecordDC sr, DateTimeSpan span) { CostRatePeriodDC stundensatzCrp = null; CostRatePeriodDC previousStundensatzCrp = null; AccountingIntervalType intervalType = AccountingIntervalType.Hourly; ServiceCategoryDC category = null; var scap = FindApprovalPeriodForDateAndCategory(relDC, sr.Start.Value, sr.ServiceDescription.Category); if (scap != null) { category = scap.ServiceCategory; stundensatzCrp = sr.ServiceDescription.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney, sr.Start.Value); if (stundensatzCrp != null) { previousStundensatzCrp = GetPreviousCrp(stundensatzCrp, sr.ServiceDescription.CostRatePeriods); intervalType = sr.ServiceDescription.AccountingInterval ?? AccountingIntervalType.Hourly; category = sr.ServiceDescription.Category; } else { stundensatzCrp = sr.ServiceDescription.Category.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.AmountOfMoney, sr.Start.Value); if (stundensatzCrp != null) { previousStundensatzCrp = GetPreviousCrp(stundensatzCrp, sr.ServiceDescription.Category.CostRatePeriods); intervalType = sr.ServiceDescription.Category.AccountingInterval ?? AccountingIntervalType.Hourly; category = sr.ServiceDescription.Category; } } if (stundensatzCrp == null) { if (scap.ApprovedFixedAmount.HasValue && scap.ApprovedFixedAmountInterval.HasValue) { stundensatzCrp = new CostRatePeriodDC(); stundensatzCrp.CostRateValue = scap.ApprovedFixedAmount.Value; stundensatzCrp.CostRateType = CostRatePeriodType.AmountOfMoney; intervalType = ConvertApprovalToAccountingIntervalType(scap.ApprovedFixedAmountInterval.Value); } } if (stundensatzCrp == null) { stundensatzCrp = relDC.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.HourlyRate, sr.Start.Value); previousStundensatzCrp = GetPreviousCrp(stundensatzCrp, relDC.CostBearer.CostRatePeriods); } if (stundensatzCrp != null) { CostRatePeriodDC rateFactor = relDC.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.RateFactor, sr.Start.Value); CostRatePeriodDC previousRateFactor = null; if (rateFactor != null) { previousRateFactor = GetPreviousCrp(rateFactor, relDC.CostBearer.CostRatePeriods); } CostRatePeriodDC unit = relDC.CostBearer.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, sr.Start.Value); AccountingPeriod ap = new AccountingPeriod(); ap.PeriodStart = scap.StartDate ?? DateTime.MinValue; ap.PeriodEnd = scap.EndDate ?? DateTime.MaxValue; if (span != null) { if (span.StartDate > ap.PeriodStart) ap.PeriodStart = span.StartDate; if (span.EndDate < ap.PeriodEnd) ap.PeriodEnd = span.EndDate; } ap.Amount = stundensatzCrp.CostRateValue ?? 0; ap.AccountingInterval = intervalType; ap.ServiceCategory = category; ap.ServiceRecordsInPeriod.Add(sr); ap.CostRatesInPeriod = new List(); ap.CostRatesInPeriod.Add(stundensatzCrp); if (stundensatzCrp.EndDate.HasValue && stundensatzCrp.EndDate.Value.Date < ap.PeriodEnd.Date) ap.PeriodEnd = stundensatzCrp.EndDate.Value; if (rateFactor != null && rateFactor.EndDate.HasValue && rateFactor.EndDate.Value.Date < ap.PeriodEnd.Date) ap.PeriodEnd = rateFactor.EndDate.Value; if (previousStundensatzCrp != null && previousStundensatzCrp.EndDate.HasValue && previousStundensatzCrp.EndDate.Value.Date >= ap.PeriodStart.Date) { ap.PeriodStart = previousStundensatzCrp.EndDate.Value.Date.AddDays(1); } if (previousRateFactor != null && previousRateFactor.EndDate.HasValue && previousRateFactor.EndDate.Value.Date >= ap.PeriodStart.Date) { ap.PeriodStart = previousRateFactor.EndDate.Value.Date.AddDays(1); } if (rateFactor != null) { ap.CostRatesInPeriod.Add(rateFactor); ap.RateFactor = rateFactor.CostRateValue ?? 0; } if (unit != null) { ap.CostRatesInPeriod.Add(unit); ap.UnitName = unit.UnitName; } CalculateApprovedAmounts(ap, scap); if (sr.ServiceDescription.Category.Name.ToLower().Contains("fehlkontakt") || sr.ServiceDescription.Name.ToLower().Contains("fehlkontakt") || (sr.ServiceDescription.ProzentAbrechnung.HasValue && sr.ServiceDescription.ProzentAbrechnung.Value < 100 && sr.ServiceDescription.ProzentAbrechnung.Value > 0)) { ap.Notice = "Fehlkontakte"; ap.RateFactor = 0; } return ap; } } return null; } private CostRatePeriodDC GetPreviousCrp(CostRatePeriodDC crp, List allCrpList) { if (allCrpList == null || crp == null) { return null; } var oldList = allCrpList.GetOldRates(crp.CostRateType).OrderBy(i => i.EndDate); if (!crp.EndDate.HasValue) return oldList.LastOrDefault(c => c.EndDate.HasValue); else return oldList.LastOrDefault(c => c.EndDate < crp.EndDate); } public virtual void CalculateApprovedAmounts(AccountingPeriod ap, SupportConceptApprovalPeriodDC scap) { ap.SupportConceptApprovalPeriod = scap; if (scap.ApprovedBETotal.HasValue) { if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue && scap.ApprovedBEInterval.Value == SupportConceptApprovalInterval.Weekly) { ap.ApprovedPerUnit = scap.ApprovedBEPerInterval.Value; ap.ApprovalUnit = AccountingIntervalType.Weekly; } else { ap.ApprovedPerUnit = scap.ApprovedBETotal.Value; ap.ApprovalUnit = AccountingIntervalType.FlatRate; } } else if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue) { ap.ApprovedPerUnit = scap.ApprovedBEPerInterval.Value; switch (scap.ApprovedBEInterval.Value) { case SupportConceptApprovalInterval.Daily: ap.ApprovalUnit = AccountingIntervalType.Daily; break; case SupportConceptApprovalInterval.Weekly: ap.ApprovalUnit = AccountingIntervalType.Weekly; break; case SupportConceptApprovalInterval.Fortnightly: ap.ApprovalUnit = AccountingIntervalType.Fortnightly; break; case SupportConceptApprovalInterval.Monthly: ap.ApprovalUnit = AccountingIntervalType.Monthly; break; case SupportConceptApprovalInterval.Quarterly: ap.ApprovalUnit = AccountingIntervalType.Quarterly; break; case SupportConceptApprovalInterval.HalfYearly: ap.ApprovalUnit = AccountingIntervalType.HalfYearly; break; case SupportConceptApprovalInterval.Yearly: ap.ApprovalUnit = AccountingIntervalType.Yearly; break; case SupportConceptApprovalInterval.Hourly: ap.ApprovalUnit = AccountingIntervalType.Hourly; break; } } //ap.MaxApprovedAmount = GetApprovedAmount(ap, scap); ap.MaxApprovedUnitCount = GetApprovedBETotalInPeriod(scap, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd); //var test2 = GetApprovedAmountDefaultHourlyRate(scap, ap.CostRatesInPeriod); //var test3 = GetApprovedAmountDefaultHourlyRateForPeriod(scap, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd); //var test4 = GetApprovedFixedAmount(scap); //var test5 = GetApprovedFixedAmountForPeriod(scap, ap.PeriodStart, ap.PeriodEnd); if (scap.ApprovedFixedAmount.HasValue) { ap.MaxApprovedAmount = scap.ApprovedFixedAmount.Value; } } public virtual decimal? GetApprovedAmount(AccountingPeriod ap, SupportConceptApprovalPeriodDC scap) { decimal t1, t2; return scap.ApprovedFixedAmount.HasValue ? this.GetApprovedFixedAmountForPeriod(scap, ap.PeriodStart, ap.PeriodEnd, out t1, out t2) : this.GetApprovedAmountDefaultHourlyRateForPeriod(scap, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd); } public virtual decimal? GetApprovedBETotalInPeriod(SupportConceptApprovalPeriodDC scap, List costRatePeriods, DateTime start, DateTime end) { IList splittedPeriods = SplitPeriodsByRateFactors(scap, costRatePeriods, start, end); decimal? approvedTotal = null; foreach (var ap in splittedPeriods) { decimal? approvedHours = GetApprovedHoursForPeriod(ap.SupportConceptApprovalPeriod, ap.CostRatesInPeriod, ap.PeriodStart, ap.PeriodEnd); if (approvedHours.HasValue) { if (!approvedTotal.HasValue) { approvedTotal = 0; } approvedTotal += approvedHours; } } return costRatePeriods.GetCurrentlyValidRate(CostRatePeriodType.MinutesPerServiceUnit).GetHoursInBE(approvedTotal); } public virtual int GetRoundedDuration(DataContracts.Compact.CompactOrganisationDC org, decimal durationInMinutes) { int minuteInterval = org.ActualMinuteIntervall; return GetRoundedDuration(minuteInterval, durationInMinutes); } public virtual int GetRoundedDuration(DataContracts.Compact.CompactOrganisationDC org, DateTime date, decimal durationInMinutes) { int minuteInterval = org.ActualMinuteIntervall; var crv = org.CostRatePeriods.GetCostRatePeriodForDate(CostRatePeriodType.MinutesIntervall, date); if (crv != null && crv.CostRateValue.HasValue) { minuteInterval = (int)crv.CostRateValue.Value; } return GetRoundedDuration(minuteInterval, durationInMinutes); } public virtual int GetRoundedDuration(int minuteInterval, decimal durationInMinutes) { decimal lRoundedDuration = durationInMinutes; if (minuteInterval > 0) { lRoundedDuration = durationInMinutes % minuteInterval != 0 ? durationInMinutes + (minuteInterval - (durationInMinutes % minuteInterval)) : durationInMinutes; } return (int)Math.Round(lRoundedDuration, 0, MidpointRounding.AwayFromZero); } public static AccountingIntervalType ConvertApprovalToAccountingIntervalType(SupportConceptApprovalInterval supportConceptApprovalInterval) { switch (supportConceptApprovalInterval) { case SupportConceptApprovalInterval.Daily: return AccountingIntervalType.Daily; case SupportConceptApprovalInterval.Weekly: return AccountingIntervalType.Weekly; case SupportConceptApprovalInterval.Fortnightly: return AccountingIntervalType.Fortnightly; case SupportConceptApprovalInterval.Monthly: return AccountingIntervalType.Monthly; case SupportConceptApprovalInterval.Quarterly: return AccountingIntervalType.Quarterly; case SupportConceptApprovalInterval.HalfYearly: return AccountingIntervalType.HalfYearly; case SupportConceptApprovalInterval.Hourly: return AccountingIntervalType.Hourly; default: return AccountingIntervalType.Yearly; } } public virtual Dictionary GetAbsencesTimesNotBillable(IList absenceTimeDcs) { var lResult = new Dictionary(); foreach (AbsenceTimeDC at in absenceTimeDcs) { DateTimeSpan span = new DateTimeSpan(); if (at.Start.HasValue && at.Start.Value <= DateTime.MaxValue.AddDays(-1)) { span.StartDateTime = at.Start.Value.AddDays(1).Date; } if (at.End.HasValue && at.End.Value > DateTime.MinValue.AddDays(1)) { span.EndDateTime = at.End.Value.AddDays(-1).Date; } else { span.EndDateTime = DateTime.MaxValue; ; } IEnumerable weeks = GetAbsenceWeeks(span); foreach (DateTimeSpan weekSpan in weeks) { if (at.Reason.BillableMinutes.HasValue && at.Reason.BillableMinutes.Value > 0 && !lResult.ContainsKey(weekSpan)) { lResult.Add(weekSpan, at.Reason.BillableMinutes.Value); } } } return lResult; } public virtual IEnumerable GetAbsenceWeeks(DateTimeSpan span) { var lResult = new List(); DateTime startDate = span.StartDate; DateTime endDate = span.StartDate; if (span.StartDate < DateTime.MaxValue.AddDays(-6)) { endDate = span.StartDate.AddDays(6); } DateTime totalEnddate = span.EndDate; if (totalEnddate == DateTime.MaxValue.Date && span.StartDate < DateTime.MaxValue.AddYears(-1)) totalEnddate = span.StartDate.AddYears(1); while (startDate <= totalEnddate && startDate < DateTime.MaxValue) { var lCurrentWorkWeek = new DateTimeSpan { StartDateTime = startDate, EndDateTime = endDate }; if (lCurrentWorkWeek.EndDateTime > totalEnddate) { lCurrentWorkWeek.EndDateTime = totalEnddate; } lResult.Add(lCurrentWorkWeek); if (endDate < DateTime.MaxValue.Date.AddDays(-7)) { startDate = endDate.AddDays(1); endDate = startDate.AddDays(6); } else { startDate = DateTime.MaxValue; } } return lResult; } } }