using System; using System.Collections.Generic; using System.Linq; using System.Text; using BeWo.Service.Plugins; using BS.Shared; using BS.Shared.Core; using BS.Shared.DataContracts; using BS.Shared.DataContracts.Compact; using BS.Shared.Extensions; using BS.Shared.Services; using BeWo.Data; using BeWo.Data.Access; using BeWo.Data.Entities; using BeWo.Service.Core; using BeWo.Service.DCEntityMapper; namespace BeWo.Service.Invoicing { public class SettlementInvoiceCreation : AbstractIDSpecificDefaultClass { public virtual Settlement2DC GenerateSettlementInvoice(CostBearer2SupportConcept c2s, DateTime? periodStart, DateTime? periodEnd) { Settlement2DC settlementInvoice = new Settlement2DC(); settlementInvoice.CostBearer2SupportConceptOid = c2s.Oid; settlementInvoice.CustomerReferenceNumber = c2s.CustomerReferenceNumber; settlementInvoice.InvoiceDate = DateTime.Now.Date; settlementInvoice.ServiceUnit = MapperFactory.CostRatePeriodDC_CostRatePeriod.MapToNewDCs(c2s.CostBearer.CostRatePeriods) .GetCostRatePeriodForDate(CostRatePeriodType.MinutesPerServiceUnit, DateTime.Now); if (settlementInvoice.ServiceUnit == null) { settlementInvoice.ServiceUnit = new CostRatePeriodDC(); settlementInvoice.ServiceUnit.CostRateType = CostRatePeriodType.MinutesPerServiceUnit; settlementInvoice.ServiceUnit.UnitName = "FLS"; settlementInvoice.ServiceUnit.CostRateValue = 60; } if (String.IsNullOrEmpty(settlementInvoice.InvoiceNumber)) { var inc = PluginLoader.FindClass(); settlementInvoice.InvoiceNumber = inc.GetNextInvoiceNumber(0, settlementInvoice); } if (periodStart.HasValue && periodEnd.HasValue) { settlementInvoice.AccountingPeriodStart = periodStart; settlementInvoice.AccountingPeriodEnd = periodEnd; } else { settlementInvoice.AccountingPeriodStart = c2s.StartDate; settlementInvoice.AccountingPeriodEnd = c2s.EndDate; } CreateSenderInformation(settlementInvoice); CreateRecipientInformation(settlementInvoice, c2s); if (CanCreateInvoiceTheOldWay(settlementInvoice, c2s)) CreateInvoiceDetailsTheOldWay(settlementInvoice, c2s); else CreateInvoiceDetails(settlementInvoice, c2s, periodStart, periodEnd); if (settlementInvoice.InvoiceItems == null || settlementInvoice.InvoiceItems.Count == 0) { settlementInvoice.InvoiceItems = new List(); settlementInvoice.InvoiceItems.Add( new InvoiceItemDC { AmountTotal = settlementInvoice.Claim, ItemPeriodStart = settlementInvoice.AccountingPeriodStart, ItemPeriodEnd = settlementInvoice.AccountingPeriodEnd, ItemDescription = "Spitzabrechnung" }); } return settlementInvoice; } public virtual IList GetBillableServiceRecords(CostBearer2SupportConcept c2s) { return c2s.ServiceRecords.OrderBy(s => s.Start).ToList(); } public virtual bool CanCreateInvoiceTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s) { //Die alte Methode funktioniert nicht mehr wenn //1. Es mehr als drei Stundensätze gibt //2. Es mehr als einen Faktor gibt //3. Mindestens einem Bewilligungszeitraum eine Kategorie zugeordnet ist //4. Wenn sich Bewilligungszeiträume überscheiden //5. ServiceRecords einer ServiceDescription/Category mit Stundensätzen zugeordnet sind //6. Fehlkontakte vorhanden sind //In allen anderen Fällen wird die alte Methode noch verwendet, das sonst alle Kundenrechnungsformulare angepasst werden müssten. var relDC = c2s.MapToNewDC(); DateTime start = si.AccountingPeriodStart.Value.Date; DateTime end = si.AccountingPeriodEnd.Value.Date; if (c2s.SupportConcept.Customer.TerminationDate.HasValue && c2s.SupportConcept.Customer.TerminationDate.Value < end) { end = c2s.SupportConcept.Customer.TerminationDate.Value; } if (end > DateTime.Now) { var srList = c2s.ServiceRecords; var max = srList.Max(s => s.End); if (max.HasValue && max < end) { end = max.Value; } } //Prüfe 1: var hourlyRatesInSpanList = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.HourlyRate, start, end).ToList(); if (hourlyRatesInSpanList.Count > 3) return false; //Prüfe 2: var factorInSpanList = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.RateFactor, start, end).ToList(); if (factorInSpanList.Count > 1) return false; //Prüfe 4 //for (int i = 0; i < c2s.ApprovalPeriodList.Count - 1; i++) //{ // var ap1 = c2s.ApprovalPeriodList[i]; // if (ap1.StartDate.HasValue && ap1.EndDate.HasValue) // { // for (int j = i + 1; j < c2s.ApprovalPeriodList.Count; j++) // { // var ap2 = c2s.ApprovalPeriodList[j]; // if (ap2.StartDate.HasValue && ap2.EndDate.HasValue) // { // if (ap1.EndDate >= ap2.StartDate && ap1.StartDate <= ap2.EndDate) // return false; // } // } // } //} //Prüfe 3 foreach (var ap in c2s.ApprovalPeriodList) { if (ap.ServiceCategory != null && ap.ServiceCategory.IsActive == ActivationTypeId.Active) { if (ap.ServiceCategory.CostRatePeriods != null && ap.ServiceCategory.CostRatePeriods.Count > 0) { foreach (var costRatePeriod in ap.ServiceCategory.CostRatePeriods) { if (costRatePeriod.CostRateValue.HasValue && costRatePeriod.CostRateValue.Value > 0) return false; } } var sdList = DAOFactory.SearchDAO.FindServiceDescriptionForCategory(ap.ServiceCategory.Oid.Value); foreach (var sd in sdList) { if (sd.CostRatePeriods.Count > 0) { foreach (var costRatePeriod in sd.CostRatePeriods) { if (costRatePeriod.CostRateValue.HasValue && costRatePeriod.CostRateValue.Value > 0) return false; } } } } } //Prüfe 5 + 6 foreach (var sr in GetBillableServiceRecords(c2s)) { if (IsServiceRecordBillable(sr) && sr.DurationMinutes > 0) { if (sr.ServiceDescription.Name.ToLower().Contains("fehlkontakt") || (sr.ServiceDescription.ProzentAbrechnung.HasValue && sr.ServiceDescription.ProzentAbrechnung.Value < 100)) { return false; } if (sr.ServiceDescription.CostRatePeriods.Count > 0) { foreach (var costRatePeriod in sr.ServiceDescription.CostRatePeriods) { if (costRatePeriod.CostRateValue.HasValue && costRatePeriod.CostRateValue.Value > 0) return false; } } if (sr.ServiceDescription.ServiceCategory.CostRatePeriods.Count > 0) { foreach (var costRatePeriod in sr.ServiceDescription.ServiceCategory.CostRatePeriods) { if (costRatePeriod.CostRateValue.HasValue && costRatePeriod.CostRateValue.Value > 0) return false; } } } } return true; } public virtual void CreateInvoiceDetailsTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s) { bool isLWL = false; String id = c2s.CostBearer.ID; if (String.IsNullOrEmpty(id)) { if (!String.IsNullOrEmpty(si.RecipientOrganisation) && si.RecipientOrganisation.Trim().ToLower().IndexOf("lwl") == 0) { isLWL = true; id = "LWL"; } } var calc = PluginLoader.FindClass(id) ?? Calculations.GetInstance(id); var relDC = c2s.MapToNewDC(); decimal lDurationInWeeks = calc.GetIntervalCountForDays( Convert.ToDecimal((c2s.EndDate.Value - c2s.StartDate.Value).Days + 1), SupportConceptApprovalInterval.Weekly); decimal lApprovedFLSWeeklyForMaxAmountCalculation = 0; CalculateSettlementInvoiceApprovedAmounts(si, relDC, calc); DateTime periodStart = si.AccountingPeriodStart.Value.Date; DateTime periodEnd = si.AccountingPeriodEnd.Value.Date; //Prüfe ob vorher abgemeldet if (c2s.SupportConcept.Customer.TerminationDate.HasValue && c2s.SupportConcept.Customer.TerminationDate.Value < periodEnd) { periodEnd = c2s.SupportConcept.Customer.TerminationDate.Value; si.ErrorNotice = String.Format( "Achtung: Die Betreuung endete am {0:dd.MM.yyyy}. Alle nachträglich eingegebenen Stunden werden nicht abgerechnet!", periodEnd); } //Prüfe ob Enddatum in Zukunft liegt: if (periodEnd > DateTime.Now) { var srList = c2s.ServiceRecords; var max = srList.Max(s => s.End); if (max.HasValue && max < periodEnd) { periodEnd = max.Value; } } var tolerancesInSpanList = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.ServiceTolerance, periodStart, periodEnd).ToList(); var hourlyRatesInSpanList = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.HourlyRate, periodStart, periodEnd).ToList(); si.DifferentHourlyRateCount = hourlyRatesInSpanList.Count; if (hourlyRatesInSpanList.Count > 1) { if (hourlyRatesInSpanList.Count == 2) { si.WeeksWithOldHourlyRate = calc.GetIntervalCountForDays(Convert.ToDecimal(hourlyRatesInSpanList.First().Value.GetTimeSpan().Days + 1), SupportConceptApprovalInterval.Weekly); si.WeeksWithNewHourlyRate = calc.GetIntervalCountForDays( Convert.ToDecimal((c2s.EndDate.Value - hourlyRatesInSpanList.Last().Value.StartDate).Days + 1), SupportConceptApprovalInterval.Weekly); si.WeeksWithHourlyRate3 = 0; } else if (hourlyRatesInSpanList.Count == 3) { si.WeeksWithHourlyRate3 = calc.GetIntervalCountForDays(Convert.ToDecimal(hourlyRatesInSpanList.First().Value.GetTimeSpan().Days + 1), SupportConceptApprovalInterval.Weekly); si.WeeksWithOldHourlyRate = calc.GetIntervalCountForDays(Convert.ToDecimal(hourlyRatesInSpanList.ElementAt(1).Value.GetTimeSpan().Days + 1), SupportConceptApprovalInterval.Weekly); si.WeeksWithNewHourlyRate = calc.GetIntervalCountForDays( Convert.ToDecimal((c2s.EndDate.Value - hourlyRatesInSpanList.Last().Value.StartDate).Days + 1), SupportConceptApprovalInterval.Weekly); } } else { si.WeeksWithNewHourlyRate = lDurationInWeeks; } decimal currentRateFactor = 0; decimal currentHourlyRate = 0; decimal lastHourlyRate = 0; decimal thirdHourlyRate = 0; var rateFactorsInSpanList = relDC.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.RateFactor, periodStart, periodEnd).ToList(); if (hourlyRatesInSpanList.Count > 0) { var cdc = hourlyRatesInSpanList.Last().Key; if (cdc.CostRateValue.HasValue) currentHourlyRate = cdc.CostRateValue.Value; } if (rateFactorsInSpanList.Count > 0) { var cdc = rateFactorsInSpanList.Last().Key; if (cdc.CostRateValue.HasValue) currentRateFactor = cdc.CostRateValue.Value; } if (hourlyRatesInSpanList.Count > 1) { var cdc = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Key; if (cdc.CostRateValue.HasValue) lastHourlyRate = cdc.CostRateValue.Value; si.HourlyRateOldEndDate = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Value.EndDate; si.HourlyRateNewStartDate = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 1].Value.StartDate; } if (hourlyRatesInSpanList.Count > 2) { var cdc = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 3].Key; if (cdc.CostRateValue.HasValue) thirdHourlyRate = cdc.CostRateValue.Value; si.HourlyRateEndDate3 = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 3].Value.EndDate; si.HourlyRateOldStartDate = hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Value.StartDate; } if (si.ApprovedFLSWeekly == null) { si.ApprovedFLSWeekly = 0; } //decimal maxAmount = 0; //decimal maxFLS = lApprovedFLSWeeklyForMaxAmountCalculation * si.WeeksWithNewHourlyRate; //maxFLS += lApprovedFLSWeeklyForMaxAmountCalculation * si.WeeksWithOldHourlyRate; //maxFLS += lApprovedFLSWeeklyForMaxAmountCalculation * si.WeeksWithHourlyRate3; si.RateFactor = currentRateFactor; si.HourlyRateNew = currentHourlyRate; si.HourlyRateOld = lastHourlyRate; si.HourlyRate3 = thirdHourlyRate; //decimal totalAmount = 0; var records = GetBillableServiceRecords(c2s); records = records.Where(i => i.Start.HasValue && i.Start.Value.InBetween(periodStart, periodEnd, true)).ToList(); var recordDCS = records.MapToNewDCs(); var absenceTimes = MapperFactory.AbsenceTimeDC_AbsenceTime.MapToNewDCs(c2s.SupportConcept.Customer.AbsenceTimes); Dictionary span2Hours = calc.GetAbsencesTimesNotBillable(absenceTimes); si.RecordedBillableFLSNotBillableCustomerAbsent = calc.GetHoursNotBillableDueToAbsenceTimes(relDC, recordDCS, span2Hours, false, false); si.RecordedBillableFLSNotBillableOutOfApprovedSpan = calc.GetHoursNotBillableDueToOutOfSpan(relDC, recordDCS); //calc.GetHoursNotBillableDueToMoreThanApproved(relDC, recordDCS, false, false); Dictionary groupBookingDict = new Dictionary(); decimal flsCleared = 0; records = records.OrderBy(sr => sr.Start.HasValue ? sr.Start.Value : DateTime.MinValue) .ToList(); decimal tolerance = 100; if (tolerancesInSpanList.Count > 0) { if (tolerancesInSpanList[0].Key.CostRateValue.HasValue && tolerancesInSpanList[0].Key.CostRateValue.Value > 0) { tolerance = tolerancesInSpanList[0].Key.CostRateValue ?? 100; } } decimal approved = si.ApprovedFLS ?? 0; if (tolerance != 100) { approved = (approved*tolerance)/100m; } if (!CheckMaxApprovedHours()) { approved = Decimal.MaxValue; } foreach (var iRecord in records) { if (iRecord.ServiceDescription.ServiceCategory.IsBillable && IsServiceRecordBillable(iRecord)) { if (iRecord.GroupOid == null || !groupBookingDict.ContainsKey(iRecord.GroupOid.Value)) { decimal rd = iRecord.RoundedDuration; var prozent = iRecord.ServiceDescription.ServiceCategory.ProzentAbrechnung; if (iRecord.ServiceDescription.ProzentAbrechnung.HasValue) { prozent = iRecord.ServiceDescription.ProzentAbrechnung.Value; } rd = (rd * prozent) / 100; decimal duration = rd/60; if (si.RecordedBillableFLSBrutto == null) { si.RecordedBillableFLSBrutto = 0; } si.RecordedBillableFLSBrutto += duration; si.RecordedBillableFLSNetto += duration; //if (maxAmount == 0) //{ if (flsCleared + duration > approved) { duration = approved - flsCleared; flsCleared = approved; } else { flsCleared += duration; } //} //decimal? hourlyRate = null; //decimal? rateFactor = null; var rates = c2s.CostBearer.CostRatePeriods.MapToNewDCs(); //if (iRecord.Start != null) //{ // hourlyRate = rates.GetValidRateValueForDate(CostRatePeriodType.HourlyRate, iRecord.Start.Value); // rateFactor = rates.GetValidRateValueForDate(CostRatePeriodType.RateFactor, iRecord.Start.Value); //} //else //{ // hourlyRate = rates.GetCurrentlyValidRateValue(CostRatePeriodType.HourlyRate); // rateFactor = rates.GetCurrentlyValidRateValue(CostRatePeriodType.RateFactor); //} //if (!hourlyRate.HasValue) // hourlyRate = 0; //if (!rateFactor.HasValue) // rateFactor = 0; if (hourlyRatesInSpanList.Count > 2 && iRecord.Start != null && iRecord.Start < hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Value.StartDateTime) { si.ApprovedFLSWithHourlyRate3 += duration; } else if (hourlyRatesInSpanList.Count > 1 && iRecord.Start != null && iRecord.Start < hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 1].Value.StartDateTime) { si.ApprovedFLSWithOldHourlyRate += duration; } else { si.ApprovedFLSWithActualHourlyRate += duration; } if (iRecord.GroupOid != null && !groupBookingDict.ContainsKey(iRecord.GroupOid.Value)) { si.HasGroupRecords = true; groupBookingDict.Add(iRecord.GroupOid.Value, true); } } } } si.ApprovedFLSWithActualHourlyRate = Math.Round(si.ApprovedFLSWithActualHourlyRate, 2, MidpointRounding.AwayFromZero); si.ApprovedFLSWithHourlyRate3 = Math.Round(si.ApprovedFLSWithHourlyRate3, 2, MidpointRounding.AwayFromZero); si.ApprovedFLSWithOldHourlyRate = Math.Round(si.ApprovedFLSWithOldHourlyRate, 2, MidpointRounding.AwayFromZero); decimal rf = 1; if (si.RateFactor.HasValue && si.RateFactor.Value != 0) { rf = si.RateFactor.Value; rf = (100 + rf)/100; } decimal roundedNewAmount = 0; decimal amountRateFactor = 0; if (si.HourlyRateNew.HasValue) { roundedNewAmount = Math.Round(si.ApprovedFLSWithActualHourlyRate*si.HourlyRateNew.Value*rf, 2, MidpointRounding.AwayFromZero); amountRateFactor = Math.Round( roundedNewAmount - Math.Round(si.ApprovedFLSWithActualHourlyRate*si.HourlyRateNew.Value, 2, MidpointRounding.AwayFromZero), 2, MidpointRounding.AwayFromZero); } if (si.HourlyRateOld.HasValue) { decimal a2 = Math.Round(si.ApprovedFLSWithOldHourlyRate*si.HourlyRateOld.Value*rf, 2, MidpointRounding.AwayFromZero); roundedNewAmount += a2; amountRateFactor += Math.Round( a2 - Math.Round(si.ApprovedFLSWithOldHourlyRate*si.HourlyRateOld.Value, 2, MidpointRounding.AwayFromZero), 2, MidpointRounding.AwayFromZero); } if (si.HourlyRate3 > 0) { decimal a3 = Math.Round(si.ApprovedFLSWithHourlyRate3*si.HourlyRate3*rf, 2, MidpointRounding.AwayFromZero); roundedNewAmount += a3; amountRateFactor += Math.Round(a3 - Math.Round(si.ApprovedFLSWithHourlyRate3*si.HourlyRate3, 2, MidpointRounding.AwayFromZero), 2, MidpointRounding.AwayFromZero); } si.AmountWithRateFactor += amountRateFactor; var totalAmount = Math.Round(roundedNewAmount, 2, MidpointRounding.AwayFromZero); decimal? maxAmount = CalculateMaxAmount(si); if (maxAmount.HasValue && totalAmount > maxAmount.Value) { totalAmount = maxAmount.Value; } si.AmountBillableTotal = totalAmount; //AdvancedPayments CalculateAdvancedPayments(si, c2s, null, null, hourlyRatesInSpanList); //EquityContribution (Eigenanteil) CalculateEquityContribution(si, c2s.SupportConcept, null, null); si.Claim = si.AmountBillableTotal - (si.AmountAdvancedPayments ?? 0) - si.EquityContribution; } public virtual bool CheckMaxApprovedHours() { return true; } public virtual bool IsServiceRecordBillable(ServiceRecord iRecord) { return true; } public virtual decimal? CalculateMaxAmount(Settlement2DC si) { return null; } public virtual void CreateInvoiceDetails(Settlement2DC si, CostBearer2SupportConcept c2s, DateTime? periodStart, DateTime? periodEnd) { var calc = PluginLoader.FindClass(_SpecificID) ?? Calculations.GetInstance(_SpecificID); var records = GetBillableServiceRecords(c2s); DateTimeSpan span = null; if (periodStart.HasValue && periodEnd.HasValue) { span = new DateTimeSpan(); span.StartDateTime = periodStart.Value; span.EndDateTime = periodEnd.Value; records = records.Where( i => i.Start.HasValue && i.Start.Value.InBetween(periodStart.Value, periodEnd.Value, true)) .ToList(); } foreach (var sr in records) { if (sr.GroupOid.HasValue) { si.HasGroupRecords = true; } } var recordDCS = records.MapToNewDCsCompact(); var relDC = c2s.MapToNewDC(); CalculateSettlementInvoiceApprovedAmounts(si, relDC, calc); var accountingPeriods = calc.GetAccountingPeriods(relDC, recordDCS, span); foreach (var ap in accountingPeriods) { if (ap.SupportConceptApprovalPeriod != null && !ap.SupportConceptApprovalPeriod.ApprovedBETotal.HasValue) { ap.SupportConceptApprovalPeriod.ApprovedBETotal = si.ApprovedFLS; } } si.InvoiceItems = CreateInvoiceItemsForAccountingPeriods(si, accountingPeriods, relDC.CostBearer.CostRatePeriods, calc); if (si.ApprovedFLS.HasValue) { CalculateInvoiceItemAmounts(si); } //CalculateMaxApprovedAmounts(accountingPeriods, relDC.CostBearer.CostRatePeriods, calc); CalculateAdvancedPayments(si, c2s); CalculateEquityContribution(si, c2s.SupportConcept, periodStart, periodEnd); CalculateSettlementInvoiceAmounts(si); } //public virtual void CalculateMaxApprovedAmounts(IList accountingPeriods, List costRatePeriods, Calculations calc) //{ // Dictionary oid2Scap = new Dictionary(); // foreach (var ap in accountingPeriods) // { // var scap = ap.SupportConceptApprovalPeriod; // SupportConceptApprovalPeriodDC scapHelper = null; // if (scap != null) // { // if (oid2Scap.ContainsKey(scap.SupportConceptApprovalPeriodOid.Value)) // scapHelper = oid2Scap[scap.SupportConceptApprovalPeriodOid.Value]; // else // { // scapHelper = new SupportConceptApprovalPeriodDC(); // scapHelper.ApprovedBETotal = scap.ApprovedBETotal; // if (!scapHelper.ApprovedBETotal.HasValue) // scapHelper.ApprovedBETotal = calc.GetApprovedBETotal(scap, costRatePeriods); // } // } // if (scapHelper.ApprovedBETotal.HasValue && scapHelper.ApprovedBETotal > ap.co // } //} public virtual void CalculateSettlementInvoiceAmounts(Settlement2DC si) { decimal maxAmount = 0; if (si.InvoiceItems != null) { si.AmountBillableTotal = si.InvoiceItems.Sum(ii => Math.Round(ii.GrossAmountTotal ?? 0, 2, MidpointRounding.AwayFromZero)); //maxAmount = si.InvoiceItems.Sum(ii => // { // decimal amount = ii.GrossAmountTotal ?? 0; // if (ii.MaxApprovedAmount.HasValue && ii.MaxApprovedAmount.Value > 0 && ii.MaxApprovedAmount < amount) // amount = ii.MaxApprovedAmount.Value; // return amount; // }); if (maxAmount == 0) maxAmount = si.AmountBillableTotal.Value; } si.Claim = maxAmount - (si.AmountAdvancedPayments ?? 0) - (si.EquityContribution ?? 0); } public virtual List CreateInvoiceItemsForAccountingPeriods(Settlement2DC si, List accountingPeriods, List costRatePeriods, Calculations calc) { Dictionary oid2Scap = new Dictionary(); List invoiceItems = new List(); foreach (var ap in accountingPeriods) { InvoiceItemDC ii = CreateInvoiceItemForAccountingPeriod(ap); invoiceItems.Add(ii); var scap = ap.SupportConceptApprovalPeriod; SupportConceptApprovalPeriodDC scapHelper = null; if (scap != null) { if (oid2Scap.ContainsKey(scap.SupportConceptApprovalPeriodOid.Value)) scapHelper = oid2Scap[scap.SupportConceptApprovalPeriodOid.Value]; else { scapHelper = new SupportConceptApprovalPeriodDC(); scapHelper.ApprovedBETotal = scap.ApprovedBETotal; if (!scapHelper.ApprovedBETotal.HasValue) scapHelper.ApprovedBETotal = calc.GetApprovedBETotal(scap, costRatePeriods); oid2Scap[scap.SupportConceptApprovalPeriodOid.Value] = scapHelper; } } if (!si.ApprovedFLS.HasValue) { if (scapHelper.ApprovedBETotal.HasValue && ii.UnitCount.HasValue) { if (scapHelper.ApprovedBETotal >= ii.UnitCount.Value) { scapHelper.ApprovedBETotal -= ii.UnitCount.Value; ii.MaxApprovedUnitCount = null; } else { ii.MaxApprovedUnitCount = scapHelper.ApprovedBETotal; } } CalculateInvoiceItemAmounts(ii); } if (ii.UnitCount.HasValue) { si.RecordedBillableFLSBrutto = (si.RecordedBillableFLSBrutto ?? 0) + ii.UnitCount; si.RecordedBillableFLSNetto = si.RecordedBillableFLSBrutto ?? 0; } var list = CreateAdditionalInvoiceItems(ap); invoiceItems.AddRange(list); } return invoiceItems; } public virtual IList CreateAdditionalInvoiceItems(AccountingPeriod ap) { //only for subclasses return new List(); } public virtual InvoiceItemDC CreateInvoiceItemForAccountingPeriod(AccountingPeriod accountingPeriod) { InvoiceItemDC ii = new InvoiceItemDC(); ii.AccountingInterval = accountingPeriod.AccountingInterval; ii.ItemPeriodStart = accountingPeriod.PeriodStart; ii.ItemPeriodEnd = accountingPeriod.PeriodEnd; ii.RateFactor = accountingPeriod.RateFactor; ii.AmountPerUnit = accountingPeriod.Amount; ii.UnitDescription = accountingPeriod.UnitName; ii.MaxApprovedAmount = accountingPeriod.MaxApprovedAmount; ii.MaxApprovedUnitCount = accountingPeriod.MaxApprovedUnitCount; ii.ApprovedPerUnit = accountingPeriod.ApprovedPerUnit; ii.ApprovalUnit = accountingPeriod.ApprovalUnit; if (accountingPeriod.ServiceCategory != null) ii.ItemDescription = accountingPeriod.ServiceCategory.Name; if (accountingPeriod.AccountingInterval == AccountingIntervalType.FlatRate) { ii.UnitCount = accountingPeriod.ServiceRecordsInPeriod.Count; } else if (accountingPeriod.AccountingInterval == AccountingIntervalType.Hourly) { decimal minutesTotal = 0; decimal minutesTotalBudget = 0; if (accountingPeriod.ServiceRecordsInPeriod != null) { foreach (var ar in accountingPeriod.ServiceRecordsInPeriod) { var prozent = ar.ServiceDescription.Category.Percentage; if (ar.ServiceDescription.ProzentAbrechnung.HasValue) { prozent = ar.ServiceDescription.ProzentAbrechnung.Value; } minutesTotal += (ar.RoundedDuration * prozent) / 100; prozent = ar.ServiceDescription.Category.ProzentBudget ?? 100; if (ar.ServiceDescription.ProzentBudget.HasValue) { prozent = ar.ServiceDescription.ProzentBudget.Value; } minutesTotalBudget += (ar.RoundedDuration * prozent) / 100; } } ii.UnitCount = minutesTotal/60; ii.UnitCountBudget = minutesTotalBudget/60; if (String.IsNullOrEmpty(ii.UnitDescription)) ii.UnitDescription = ii.UnitCount.Value == 1 ? "Stunde" : "Stunden"; } else if (accountingPeriod.PeriodStart > DateTime.MinValue && accountingPeriod.PeriodEnd < DateTime.MaxValue) { DateTime intervalStart = accountingPeriod.PeriodStart; DateTime intervalEnd = intervalStart; int count = 0; while (intervalStart < accountingPeriod.PeriodEnd) { count++; switch (accountingPeriod.AccountingInterval) { 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; } intervalStart = intervalEnd; } ii.UnitCount = count; ii.UnitCountBudget = count; switch (accountingPeriod.AccountingInterval) { case AccountingIntervalType.Daily: ii.UnitDescription = count == 1 ? "Tag" : "Tage"; break; case AccountingIntervalType.Weekly: ii.UnitDescription = count == 1 ? "Woche" : "Wochen"; break; case AccountingIntervalType.Fortnightly: ii.UnitDescription = "2 Wochen"; break; case AccountingIntervalType.Monthly: ii.UnitDescription = count == 1 ? "Monat" : "Monate"; break; case AccountingIntervalType.Quarterly: ii.UnitDescription = count == 1 ? "Quartal" : "Quartale"; break; case AccountingIntervalType.HalfYearly: ii.UnitDescription = "Halbjährlich"; break; case AccountingIntervalType.Yearly: ii.UnitDescription = count == 1 ? "Jahr" : "Jahre"; break; } } return ii; } public virtual void CalculateInvoiceItemAmounts(InvoiceItemDC ii) { if (ii.UnitCount.HasValue) { var uc = ii.UnitCount.Value; if (ii.MaxApprovedUnitCount.HasValue && ii.MaxApprovedUnitCount.Value < uc) uc = ii.MaxApprovedUnitCount.Value; ii.UnitCount = Math.Round(uc, 2, MidpointRounding.AwayFromZero); if (ii.AmountPerUnit.HasValue) { ii.AmountTotal = Math.Round(ii.UnitCount.Value*ii.AmountPerUnit.Value, 2, MidpointRounding.AwayFromZero); if (ii.RateFactor.HasValue && ii.RateFactor.Value != 0) ii.GrossAmountTotal = Math.Round(ii.AmountTotal.Value*((100m + ii.RateFactor.Value)/100m), 2, MidpointRounding.AwayFromZero); else ii.GrossAmountTotal = ii.AmountTotal; } } } public virtual void CalculateInvoiceItemAmounts(Settlement2DC si) { decimal maxApprovedFls = si.ApprovedFLS ?? 0; foreach (var ii in si.InvoiceItems) { if (ii.UnitCount.HasValue) { var unitCountBudget = Math.Round(ii.UnitCountBudget ?? ii.UnitCount ?? 0, 2, MidpointRounding.AwayFromZero); if (maxApprovedFls < unitCountBudget) unitCountBudget = maxApprovedFls; maxApprovedFls -= unitCountBudget; if (unitCountBudget < ii.UnitCount) { ii.UnitCount = unitCountBudget; } if (ii.AmountPerUnit.HasValue) { ii.AmountTotal = ii.UnitCount.Value * ii.AmountPerUnit.Value; if (ii.RateFactor.HasValue && ii.RateFactor.Value != 0) ii.GrossAmountTotal = Math.Round(ii.AmountTotal.Value * ((100m + ii.RateFactor.Value) / 100m), 2, MidpointRounding.AwayFromZero); else ii.GrossAmountTotal = ii.AmountTotal; } if (ii.MaxApprovedUnitCount < ii.UnitCount) { ii.MaxApprovedUnitCount = ii.UnitCount; } } } } public virtual void CalculateSettlementInvoiceApprovedAmounts(Settlement2DC si, SupportConceptCostBearerRelDC relDC, Calculations calc) { decimal flsWeek = calc.GetApprovedHoursPerWeekAverage(relDC, true) ?? 0m; decimal flsTotal = calc.GetApprovedHoursTotal(relDC, true) ?? 0m; flsTotal = Math.Round(flsTotal, 2, MidpointRounding.AwayFromZero); si.ApprovedFLS = flsTotal; si.ApprovedFLSWeekly = flsWeek; } public virtual void CreateRecipientInformation(Settlement2DC settlementInvoice, CostBearer2SupportConcept c2s) { if (c2s.CostBearer != null && c2s.CostBearer.Organisation != null) { settlementInvoice.RecipientOrganisation = c2s.CostBearer.Organisation.Name; settlementInvoice.RecipientDivision = c2s.CostBearer.Organisation.Name2; } if (c2s.CostBearerContactPerson != null) { settlementInvoice.RecipientContactPerson = c2s.CostBearerContactPerson.ToString(); } else if (c2s.CostBearer != null && c2s.CostBearer.Organisation != null) { var lContactPerson = c2s.CostBearer.Organisation.Contacts.SingleOrDefault(c => c.Type == ContactType.ContactPerson); if (lContactPerson != null) { settlementInvoice.RecipientContactPerson = lContactPerson.Value; } } // War die private Rechnungsadresse der Kontaktperson //if (c2s.CostBearerContactPerson != null && c2s.CostBearerContactPerson.InvoiceAddress != null) //{ // settlementInvoice.RecipientPostCode = c2s.CostBearerContactPerson.InvoiceAddress.PostalCode; // settlementInvoice.RecipientStreet = c2s.CostBearerContactPerson.InvoiceAddress.Street; // settlementInvoice.RecipientTown = c2s.CostBearerContactPerson.InvoiceAddress.Town; //} //else if (c2s.CostBearer != null && c2s.CostBearer.Organisation != null) { if (c2s.CostBearer.Organisation.InvoiceAddress != null && !String.IsNullOrEmpty(c2s.CostBearer.Organisation.InvoiceAddress.Street)) { settlementInvoice.RecipientPostCode = c2s.CostBearer.Organisation.InvoiceAddress.PostalCode; settlementInvoice.RecipientStreet = c2s.CostBearer.Organisation.InvoiceAddress.Street; settlementInvoice.RecipientTown = c2s.CostBearer.Organisation.InvoiceAddress.Town; } else if (c2s.CostBearer.Organisation.Address != null) { settlementInvoice.RecipientPostCode = c2s.CostBearer.Organisation.Address.PostalCode; settlementInvoice.RecipientStreet = c2s.CostBearer.Organisation.Address.Street; settlementInvoice.RecipientTown = c2s.CostBearer.Organisation.Address.Town; } settlementInvoice.RecipientPostBox = c2s.CostBearer.Organisation.Contacts.FirstOrDefault(f => f.Type.Equals(ContactType.business_POBox)) != null ? c2s.CostBearer.Organisation.Contacts.FirstOrDefault(f => f.Type.Equals(ContactType.business_POBox)).Value : String.Empty; } } public virtual void CreateSenderInformation(Settlement2DC settlementInvoice) { if (LoggedInUserOperationContextExt.Current != null) { var lLoggedInEmployee = LoggedInUserOperationContextExt.Current.User.Employee; if (lLoggedInEmployee != null) { settlementInvoice.SenderContactPersonFirstName = lLoggedInEmployee.Person.FirstName; settlementInvoice.SenderContactPersonLastName = lLoggedInEmployee.Person.LastName; foreach (var iContact in lLoggedInEmployee.Person.Contacts) { switch (iContact.Type) { case ContactType.business_Fax: settlementInvoice.SenderContactPersonFax = iContact.Value; break; case ContactType.business_Mail: settlementInvoice.SenderContactPersonMail = iContact.Value; break; case ContactType.business_Phone: settlementInvoice.SenderContactPersonPhone = iContact.Value; break; } } settlementInvoice.CreatorFirstName = lLoggedInEmployee.Person.FirstName; settlementInvoice.CreatorLastName = lLoggedInEmployee.Person.LastName; } } } public virtual void CalculateAdvancedPayments(Settlement2DC settlementInvoice, CostBearer2SupportConcept cb2sc) { if (settlementInvoice.InvoiceItems != null && settlementInvoice.InvoiceItems.Count > 0) { decimal amountTotal = 0; var bookingList = cb2sc.AccountingTransactions; foreach (var item in bookingList) { if (item.GueltigkeitsDatum.HasValue) { foreach (var ii in settlementInvoice.InvoiceItems) { if (ii.ItemPeriodStart.HasValue && ii.ItemPeriodEnd.HasValue) { if (item.GueltigkeitsDatum.Value.InBetween(ii.ItemPeriodStart.Value, ii.ItemPeriodEnd.Value, true)) { ii.ReceivedAmount += item.Amount; break; } } } } amountTotal += item.Amount; } settlementInvoice.AmountAdvancedPayments = amountTotal; } } public virtual void CalculateAdvancedPayments(Settlement2DC settlementInvoice, CostBearer2SupportConcept cb2sc, DateTime? periodStart, DateTime? periodEnd, List> hourlyRatesInSpanList) { decimal amountNew = 0; decimal amountOld = 0; decimal amount3 = 0; decimal amountTotal = 0; var bookingList = cb2sc.AccountingTransactions; if (periodStart.HasValue && periodEnd.HasValue) { bookingList = bookingList.Where(i => i.GueltigkeitsDatum.HasValue && i.GueltigkeitsDatum.Value.InBetween(periodStart.Value, periodEnd.Value, true)).ToList(); } foreach (var item in bookingList) { if (hourlyRatesInSpanList.Count > 2 && item.GueltigkeitsDatum != null && item.GueltigkeitsDatum < hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 2].Value.StartDateTime) { amount3 += item.Amount; } else if (hourlyRatesInSpanList.Count > 1 && item.GueltigkeitsDatum != null && item.GueltigkeitsDatum < hourlyRatesInSpanList[hourlyRatesInSpanList.Count - 1].Value.StartDateTime) { amountOld += item.Amount; } else { amountNew += item.Amount; } amountTotal += item.Amount; } settlementInvoice.AmountTotalAdvancedPaidWithNewHourlyRate = amountNew; settlementInvoice.AmountTotalAdvancedPaidWithOldHourlyRate = amountOld; settlementInvoice.AmountTotalAdvancedPaidWithHourlyRate3 = amount3; settlementInvoice.AmountAdvancedPayments = amountTotal; } public virtual void CalculateEquityContribution(Settlement2DC settlementInvoice, SupportConcept sc, DateTime? periodStart, DateTime? periodEnd) { decimal equity = 0; IList invoiceList = DAOFactory.SearchDAO.GetInvoiceBaseByTypeAndSupportConceptOid(InvoiceType.CustomerEquity, sc.Oid.Value); if (periodStart.HasValue && periodEnd.HasValue) { invoiceList = invoiceList.Where(i => i.InvoiceDate.HasValue && i.InvoiceDate.Value.InBetween(periodStart.Value, periodEnd.Value, true)).ToList(); } foreach (InvoiceBase invoice in invoiceList) { foreach (InvoiceItem item in invoice.InvoiceItems) { if (item.AmountTotal != null) { equity += item.AmountTotal.Value; } } } settlementInvoice.EquityContribution = equity; } public virtual IList CreateApprovalPeriods(Settlement2DC si, CostBearer2SupportConcept c2s) { var relDc = c2s.MapToNewDC(); var hourlyRatesInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.HourlyRate, si.AccountingPeriodStart.Value.Date, si.AccountingPeriodEnd.Value.Date).ToList(); List sonstigeAp = new List(); Dictionary dtApDict = new Dictionary(); foreach (var keyValuePair in hourlyRatesInSpanList) { var ap = new ApprovalPeriod(); ap.PeriodStartDate = keyValuePair.Value.StartDate; ap.PeriodEndDate = DateTime.MaxValue; dtApDict.Add(ap.PeriodStartDate, ap); } var rateFactorInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.RateFactor, si.AccountingPeriodStart.Value.Date, si.AccountingPeriodEnd.Value.Date).ToList(); foreach (var keyValuePair in rateFactorInSpanList) { if (!dtApDict.ContainsKey(keyValuePair.Value.StartDate)) { var ap = new ApprovalPeriod(); ap.PeriodStartDate = keyValuePair.Value.StartDate; ap.PeriodEndDate = DateTime.MaxValue; dtApDict.Add(ap.PeriodStartDate, ap); } } foreach (var scap in c2s.ApprovalPeriodList) { if (scap.StartDate.HasValue && scap.EndDate.HasValue) { if (!dtApDict.ContainsKey(scap.StartDate.Value) && scap.ApprovedBEInterval.HasValue && scap.ApprovedBEPerInterval.HasValue) { var ap = new ApprovalPeriod(); ap.PeriodStartDate = scap.StartDate.Value; ap.PeriodEndDate = DateTime.MaxValue; dtApDict.Add(ap.PeriodStartDate, ap); } else if (!dtApDict.ContainsKey(scap.StartDate.Value) && scap.ApprovedBETotal.HasValue) //Bis 09/2022 ohne: !dtApDict.ContainsKey(scap.StartDate.Value) && { var ap = new ApprovalPeriod(); ap.PeriodStartDate = scap.StartDate.Value; ap.PeriodEndDate = scap.EndDate.Value; ap.ApprovedTotal = scap.ApprovedBETotal.Value; ap.ApprovalUnit = AccountingIntervalType.FlatRate; var hrspan = hourlyRatesInSpanList.FirstOrDefault(ispan => ispan.Value.StartDate <= ap.PeriodStartDate && ap.PeriodStartDate <= ispan.Value.EndDate); var rfspan = rateFactorInSpanList.FirstOrDefault(ispan => ispan.Value.StartDate <= ap.PeriodStartDate && ap.PeriodStartDate <= ispan.Value.EndDate); if (hrspan.Key != null && hrspan.Key.CostRateValue.HasValue) { ap.AmountPerUnit = hrspan.Key.CostRateValue.Value; ap.BillingInterval = AccountingIntervalType.Hourly; } if (rfspan.Key != null && rfspan.Key.CostRateValue.HasValue) { ap.RateFactor = rfspan.Key.CostRateValue.Value; } sonstigeAp.Add(ap); } } } foreach (var dt in dtApDict.Keys) { ApprovalPeriod ap = dtApDict[dt]; var hrspan = hourlyRatesInSpanList.FirstOrDefault(ispan => ispan.Value.StartDate <= dt && dt <= ispan.Value.EndDate); var rfspan = rateFactorInSpanList.FirstOrDefault(ispan => ispan.Value.StartDate <= dt && dt <= ispan.Value.EndDate); var scap = c2s.ApprovalPeriodList.FirstOrDefault(iscap => iscap.StartDate <= dt && dt <= iscap.EndDate && !iscap.ApprovedBETotal.HasValue); if (hrspan.Key != null && hrspan.Key.CostRateValue.HasValue) { ap.AmountPerUnit = hrspan.Key.CostRateValue.Value; ap.BillingInterval = AccountingIntervalType.Hourly; } if (hrspan.Value != null && hrspan.Value.EndDate < ap.PeriodEndDate) { ap.PeriodEndDate = hrspan.Value.EndDate; } if (rfspan.Key != null && rfspan.Key.CostRateValue.HasValue) { ap.RateFactor = rfspan.Key.CostRateValue.Value; } if (rfspan.Value != null && rfspan.Value.EndDate < ap.PeriodEndDate) { ap.PeriodEndDate = rfspan.Value.EndDate; } if (scap != null) { if (scap.EndDate.HasValue && scap.EndDate < ap.PeriodEndDate) ap.PeriodEndDate = scap.EndDate.Value; CalculateApprovedAmountForApprovalPeriod(ap, scap); } } var list = dtApDict.Values.OrderBy(ap => ap.PeriodStartDate).ToList(); foreach (var ap in sonstigeAp) { list.Add(ap); } foreach (var approvalPeriod in list) { InitApprovalPeriod(si, approvalPeriod); } return list; } public virtual void CalculateApprovedAmountForApprovalPeriod(ApprovalPeriod ap, SupportConceptApprovalPeriod scap) { if (scap.ApprovedBEPerInterval.HasValue && scap.ApprovedBEInterval.HasValue) { ap.ApprovedPerUnit = scap.ApprovedBEPerInterval.Value; ap.ApprovalUnit = Calculations.ConvertApprovalToAccountingIntervalType(scap.ApprovedBEInterval.Value); } else if (scap.ApprovedBETotal.HasValue) { //Gesamtstunden auf ap Zeitraum umrechnen if (ap.PeriodStartDate == scap.StartDate && ap.PeriodEndDate == scap.EndDate) { ap.ApprovedPerUnit = scap.ApprovedBETotal.Value; } else if (scap.EndDate.HasValue && scap.StartDate.HasValue) { decimal perioddays = (decimal)ap.PeriodEndDate.Subtract(ap.PeriodStartDate).TotalDays + 1; decimal totaldays = (decimal)scap.EndDate.Value.Subtract(scap.StartDate.Value).TotalDays + 1; var approvedPerDay = scap.ApprovedBETotal.Value / totaldays; ap.ApprovedPerUnit = perioddays * approvedPerDay; } else { ap.ApprovedPerUnit = scap.ApprovedBETotal.Value; } ap.ApprovalUnit = AccountingIntervalType.FlatRate; } } public virtual void InitApprovalPeriod(Settlement2DC si, ApprovalPeriod approvalPeriod) { approvalPeriod.Days = approvalPeriod.PeriodEndDate.Subtract(approvalPeriod.PeriodStartDate).Days + 1; approvalPeriod.Weeks = approvalPeriod.Days / 7m; if (approvalPeriod.ApprovalUnit == AccountingIntervalType.FlatRate) { if (approvalPeriod.ApprovedTotal == 0) { approvalPeriod.ApprovedTotal = approvalPeriod.ApprovedPerUnit; } } else if (approvalPeriod.ApprovalUnit == AccountingIntervalType.Monthly) { var months = GetTotalMonths(approvalPeriod.PeriodStartDate, approvalPeriod.PeriodEndDate); approvalPeriod.ApprovedTotal = months * approvalPeriod.ApprovedPerUnit; } else if (approvalPeriod.ApprovalUnit == AccountingIntervalType.Quarterly) { var months = GetTotalQuarters(approvalPeriod.PeriodStartDate, approvalPeriod.PeriodEndDate); approvalPeriod.ApprovedTotal = months * approvalPeriod.ApprovedPerUnit; } else { approvalPeriod.ApprovedTotal = Math.Round(approvalPeriod.Weeks * approvalPeriod.ApprovedPerUnit, 2, MidpointRounding.AwayFromZero); } approvalPeriod.ApprovedAmountTotal = Math.Round(approvalPeriod.ApprovedTotal * approvalPeriod.AmountPerUnit, 2, MidpointRounding.AwayFromZero); } 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; } } }