using System; using System.Collections.Generic; using System.Linq; using System.Text; using BeWo.Data.Entities; using BeWo.Service.DCEntityMapper; using BS.Shared; using BS.Shared.Core; using BS.Shared.DataContracts; using BS.Shared.Extensions; using BS.Shared.Services; using DevExpress.Utils; namespace BeWo.Service.Invoicing { [IDSpecificClass(Identifier = "LWLNEU")] public class LWLSettlementInvoiceCreation : SettlementInvoiceCreation { public override bool CanCreateInvoiceTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s) { return true; } public override void CreateInvoiceDetailsTheOldWay(Settlement2DC si, CostBearer2SupportConcept c2s) { String id = c2s.CostBearer.ID; var calc = Calculations.GetInstance(id); var relDc = c2s.MapToNewDC(); var apListFehlkontakt = new List(); var apListFLS = CreateApprovalPeriods(si, c2s); foreach (var ap in apListFLS) { var fkAp = new ApprovalPeriod(); fkAp.PeriodStartDate = ap.PeriodStartDate; fkAp.PeriodEndDate = ap.PeriodEndDate; fkAp.AmountPerUnit = ap.AmountPerUnit; fkAp.BillingInterval = ap.BillingInterval; fkAp.RateFactor = 0; fkAp.Notice = "Fehlkontakte"; apListFehlkontakt.Add(fkAp); } si.ApprovedFLS = CalculateApprovedTotalFLS(apListFLS); 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); } var hourlyRatesInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.HourlyRate, periodStart, periodEnd).ToList(); si.DifferentHourlyRateCount = hourlyRatesInSpanList.Count; if (si.DifferentHourlyRateCount == 0) { si.DifferentHourlyRateCount = 1; } var rateFactorsInSpanList = relDc.CostBearer.CostRatePeriods.GetSpans(CostRatePeriodType.RateFactor, periodStart, periodEnd).ToList(); if (rateFactorsInSpanList.Count > 0) { var cdc = rateFactorsInSpanList.Last().Key; if (cdc.CostRateValue.HasValue) si.RateFactor = cdc.CostRateValue.Value; } decimal rf = GetRateFactor(si); var records = c2s.ServiceRecords; 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 flmCleared = 0; //decimal approvedMinutes = ((si.ApprovedFLS ?? 0) * 60) / rf; records = records.OrderBy(sr => sr.Start.HasValue ? sr.Start.Value : DateTime.MinValue).ToList(); foreach (var iRecord in records) { if (iRecord.ServiceDescription.ServiceCategory.IsBillable) { if (iRecord.GroupOid == null || !groupBookingDict.ContainsKey(iRecord.GroupOid.Value)) { decimal dauerAbrechenbar = iRecord.RoundedDuration; decimal dauerBudget = iRecord.RoundedDuration; //var prozent = iRecord.ServiceDescription.ServiceCategory.ProzentAbrechnung; //if (iRecord.ServiceDescription.ProzentAbrechnung.HasValue) //{ // prozent = iRecord.ServiceDescription.ProzentAbrechnung.Value; //} //dauerAbrechenbar *= prozent / 100; var prozent = iRecord.ServiceDescription.ServiceCategory.ProzentBudget ?? 100; if (iRecord.ServiceDescription.ProzentBudget.HasValue) { prozent = iRecord.ServiceDescription.ProzentBudget.Value; } dauerBudget *= prozent / 100; var apList = apListFLS; if (iRecord.ServiceDescription.Name.ToLower().Contains("fehlkontakt") || (iRecord.ServiceDescription.ProzentAbrechnung.HasValue && iRecord.ServiceDescription.ProzentAbrechnung.Value < 100)) { apList = apListFehlkontakt; } var ap = apList.FirstOrDefault( iap => { return iap.PeriodStartDate <= iRecord.Start && (iap.PeriodEndDate >= DateTime.MaxValue.AddDays(-1) || iRecord.Start < iap.PeriodEndDate.AddDays(1)); }); if (ap != null) { ap.RecordedMinutes += dauerAbrechenbar; ap.BudgetMinutes += dauerBudget; } if (iRecord.GroupOid != null && !groupBookingDict.ContainsKey(iRecord.GroupOid.Value)) { si.HasGroupRecords = true; groupBookingDict.Add(iRecord.GroupOid.Value, true); } } } } //decimal? maxAmount = CalculateMaxAmount(si, apList); var gesamtListe = new List(); gesamtListe.AddRange(apListFLS.Where(a => a.RecordedMinutes > 0)); gesamtListe.AddRange(apListFehlkontakt.Where(a => a.RecordedMinutes > 0)); si.InvoiceItems = CreateInvoiceItems(si, gesamtListe); decimal totalAmount = 0; decimal rateFactorAmount = 0; foreach (var ii in si.InvoiceItems) { decimal grossAmount = ii.GrossAmountTotal ?? 0; totalAmount += grossAmount; if (ii.RateFactor.HasValue && ii.RateFactor.Value > 0) { rateFactorAmount += (grossAmount - (grossAmount / ((100 + ii.RateFactor.Value) / 100))); } } if (!si.RecordedBillableFLSBrutto.HasValue) { si.RecordedBillableFLSNetto = si.InvoiceItems.Sum(ii => (ii.UnitCount ?? 0)); // * rf; //eigentlich falsch dass netto = brutto ist. si.RecordedBillableFLSBrutto = si.RecordedBillableFLSNetto; } si.AmountBillableTotal = totalAmount; si.AmountWithRateFactor = rateFactorAmount; //AdvancedPayments CalculateAdvancedPayments(si, c2s, null, null, hourlyRatesInSpanList); //EquityContribution (Eigenanteil) CalculateEquityContribution(si, c2s.SupportConcept, null, null); si.Claim = si.AmountBillableTotal - (si.AmountAdvancedPayments ?? 0) - si.EquityContribution; } private decimal GetRateFactor(Settlement2DC si) { decimal rf = 1; if (si.RateFactor.HasValue && si.RateFactor.Value != 0) { rf = si.RateFactor.Value; rf = (100 + rf) / 100; } return rf; } public virtual List CreateInvoiceItems(Settlement2DC si, IList apList) { return CreateInvoiceItems(si, apList, false); } public virtual List CreateInvoiceItems(Settlement2DC si, IList apList, bool schneideBeiMaxAb) { List iiList = new List(); var apsByHourlyRate = new Dictionary>(); foreach (var ap in apList) { IList list = null; String key = String.Format("{0}_{1}", ap.AmountPerUnit, ap.RateFactor); if (apsByHourlyRate.ContainsKey(key)) { list = apsByHourlyRate[key]; } else { list = new List(); apsByHourlyRate[key] = list; } list.Add(ap); } decimal maxApprovedFls = si.ApprovedFLS ?? 0; decimal totalHours = 0; foreach (var key in apsByHourlyRate.Keys) { IList list = apsByHourlyRate[key]; var ii = new InvoiceItemDC(); decimal minutesTotal = 0; decimal minutesTotalBudget = 0; DateTime start = DateTime.MaxValue; DateTime end = DateTime.MinValue; decimal rf = si.RateFactor ?? 0; decimal hr = 0; foreach (var approvalPeriod in list) { minutesTotal += approvalPeriod.RecordedMinutes; minutesTotalBudget += approvalPeriod.BudgetMinutes; if (approvalPeriod.PeriodStartDate < start) start = approvalPeriod.PeriodStartDate; if (approvalPeriod.PeriodEndDate > end) end = approvalPeriod.PeriodEndDate; hr = approvalPeriod.AmountPerUnit; if (approvalPeriod.RateFactor.HasValue) { rf = approvalPeriod.RateFactor.Value; } if (!String.IsNullOrEmpty(approvalPeriod.Notice) && approvalPeriod.Notice == "Fehlkontakte") { hr *= 0.8m; } } ii.ItemPeriodStart = start; ii.ItemPeriodEnd = end; ii.RateFactor = rf; ii.AmountPerUnit = hr; decimal hours = Math.Round(minutesTotal / 60m, 2, MidpointRounding.AwayFromZero); hours *= (100 + rf) / 100; hours = Math.Round(hours, 2, MidpointRounding.AwayFromZero); decimal hoursBudget = Math.Round(minutesTotalBudget / 60m, 2, MidpointRounding.AwayFromZero); hoursBudget *= (100 + rf) / 100; hoursBudget = Math.Round(hoursBudget, 2, MidpointRounding.AwayFromZero); if (schneideBeiMaxAb) { if (totalHours + hoursBudget > maxApprovedFls) { hoursBudget = maxApprovedFls - totalHours; if (hoursBudget < 0) { hoursBudget = 0; } if (hours > hoursBudget) { hours = hoursBudget; } } } totalHours += hoursBudget; ii.UnitCount = hours; ii.UnitCountBudget = hoursBudget; ii.AmountTotal = Math.Round(hr * hours, 2, MidpointRounding.AwayFromZero); ii.GrossAmountTotal = ii.AmountTotal; iiList.Add(ii); } return iiList.OrderBy(ii => ii.ItemPeriodStart).ToList(); } private decimal? CalculateApprovedTotalFLS(IList apList) { decimal flsTotal = 0; foreach (var ap in apList) { flsTotal += ap.ApprovedTotal; } return flsTotal; } //private decimal? CalculateMaxAmount(Settlement2DC si, IList apList) //{ // var apsByHourlyRate = new Dictionary>(); // foreach (var ap in apList) // { // IList list = null; // if (apsByHourlyRate.ContainsKey(ap.AmountPerUnit)) // { // list = apsByHourlyRate[ap.AmountPerUnit]; // } // else // { // list = new List(); // apsByHourlyRate[ap.AmountPerUnit] = list; // } // list.Add(ap); // } // decimal amount = 0; // foreach (var hr in apsByHourlyRate.Keys) // { // IList list = apsByHourlyRate[hr]; // decimal flsTotal = 0; // foreach (var approvalPeriod in list) // { // var fls = Math.Round(approvalPeriod.Weeks * approvalPeriod.ApprovedPerUnit, 2); // flsTotal += fls; // } // decimal a = Math.Round(flsTotal, 2) * hr; // amount += Math.Round(a, 2); // } // return amount; //} public override void InitApprovalPeriod(Settlement2DC si, ApprovalPeriod approvalPeriod) { base.InitApprovalPeriod(si, approvalPeriod); approvalPeriod.Weeks = Math.Round(approvalPeriod.Days*0.1427m, 2); if (approvalPeriod.ApprovalUnit == AccountingIntervalType.Weekly) { approvalPeriod.ApprovedTotal = Math.Round(approvalPeriod.Weeks*approvalPeriod.ApprovedPerUnit, 2); } approvalPeriod.ApprovedAmountTotal = Math.Round(approvalPeriod.ApprovedTotal * approvalPeriod.AmountPerUnit, 2); } } }