2048 lines
89 KiB
C#
2048 lines
89 KiB
C#
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<Calculations>
|
|
{
|
|
#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<CostRatePeriodDC> 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<CostRatePeriodDC> costRatePeriods)
|
|
{
|
|
return this.GetApprovedAmountDefaultHourlyRateForPeriod(scap, costRatePeriods, scap.StartDate, scap.EndDate);
|
|
}
|
|
|
|
public virtual decimal? GetApprovedAmountDefaultHourlyRateForPeriod(SupportConceptApprovalPeriodDC scap,
|
|
List<CostRatePeriodDC> 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)
|
|
{
|
|
//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 (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<CostRatePeriodDC> 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<CostRatePeriodDC> 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<CostRatePeriodDC> costRatePeriods,
|
|
DateTime? pStart,
|
|
DateTime? pEnd,
|
|
bool useIsCalculationWithFactor)
|
|
{
|
|
IList<AccountingPeriod> 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<AccountingPeriod> SplitPeriodsByRateFactors(SupportConceptApprovalPeriodDC scap, List<CostRatePeriodDC> costRatePeriods, DateTime? start, DateTime? end)
|
|
{
|
|
List<AccountingPeriod> list = new List<AccountingPeriod>();
|
|
|
|
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<CostRatePeriodDC> 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<CostRatePeriodDC> 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<CostRatePeriodDC> 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<CostRatePeriodDC> 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<CostRatePeriodDC> 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<CostRatePeriodDC> 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<AccountingPeriod> 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<CostRatePeriodDC> costRatePeriods)
|
|
{
|
|
IList<AccountingPeriod> 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 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<ServiceRecordDC> records)
|
|
{
|
|
return records.Sum(i => GetBillableDurationInMinutes(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<ServiceRecordDC> records,
|
|
Dictionary<DateTimeSpan, decimal> 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<CostRatePeriodDC> costRates,
|
|
List<ServiceRecordDC> records,
|
|
Dictionary<DateTimeSpan, decimal> 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<ServiceRecordDC> 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<CostRatePeriodDC> costRates,
|
|
List<ServiceRecordDC> 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<ServiceRecordDC> 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<CostRatePeriodDC> 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<BillingData> GetFixedBillingData(SupportConceptApprovalPeriodDC supportConceptApprovalPeriod, DateTime? start, DateTime? end)
|
|
{
|
|
IList<BillingData> billingDatas = new List<BillingData>();
|
|
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<AccountingPeriod> GetAccountingPeriods(SupportConceptCostBearerRelDC relDC, List<ServiceRecordDC> recordDCS, DateTimeSpan span)
|
|
{
|
|
Dictionary<long, bool> groupBookingDict = new Dictionary<long, bool>();
|
|
List<AccountingPeriod> apList = new List<AccountingPeriod>();
|
|
Dictionary<AccountingPeriod, AccountingPeriod> ap2apDict = new Dictionary<AccountingPeriod, AccountingPeriod>();
|
|
|
|
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<CostRatePeriodDC>();
|
|
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))
|
|
{
|
|
ap.Notice = "Fehlkontakte";
|
|
ap.RateFactor = 0;
|
|
}
|
|
return ap;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private CostRatePeriodDC GetPreviousCrp(CostRatePeriodDC crp, List<CostRatePeriodDC> 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<CostRatePeriodDC> costRatePeriods,
|
|
DateTime start, DateTime end)
|
|
{
|
|
IList<AccountingPeriod> 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<DateTimeSpan, decimal> GetAbsencesTimesNotBillable(IList<AbsenceTimeDC> absenceTimeDcs)
|
|
{
|
|
var lResult = new Dictionary<DateTimeSpan, decimal>();
|
|
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<DateTimeSpan> 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<DateTimeSpan> GetAbsenceWeeks(DateTimeSpan span)
|
|
{
|
|
var lResult = new List<DateTimeSpan>();
|
|
|
|
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;
|
|
}
|
|
}
|
|
} |