public async Task <NoticeWorkedNotPaidResponseDTO> PerformNwnpCalculationAsync(NoticeWorkedNotPaidCalculationRequestModel data, IOptions <ConfigLookupRoot> options, TraceInfoDate traceInfoDate = null) { var calculationResult = new NoticeWorkedNotPaidResponseDTO(); var weeklyResult = new List <NoticeWorkedNotPaidWeeklyResult>(); decimal statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, data.InsolvencyDate.Date); int yearsOfService = await data.EmploymentStartDate.Date.GetServiceYearsAsync(data.DismissalDate.Date); yearsOfService = Math.Max(yearsOfService, 1); yearsOfService = Math.Min(yearsOfService, 12); //calculate last date they can claim var noticeStartDate = data.DateNoticeGiven.Date.AddDays(1); var adjUnpaidPeriodFrom = (data.UnpaidPeriodFrom.Date > noticeStartDate) ? data.UnpaidPeriodFrom.Date : noticeStartDate; var entitlementEndDate = noticeStartDate.AddDays(yearsOfService * 7).AddDays(-1); //The maximum end date is notice start date + number of entitlement weeks var adjUnpaidPeriodTo = (data.UnpaidPeriodTo.Date < entitlementEndDate) ? data.UnpaidPeriodTo.Date : entitlementEndDate; adjUnpaidPeriodTo = (data.DismissalDate.Date < adjUnpaidPeriodTo) ? data.DismissalDate.Date : adjUnpaidPeriodTo; // Extend adjusted period by 7 days to capture last working week var extendedAdjustedPeriodTo = adjUnpaidPeriodTo; if (extendedAdjustedPeriodTo.DayOfWeek != data.DateNoticeGiven.DayOfWeek) { extendedAdjustedPeriodTo = extendedAdjustedPeriodTo.AddDays(7); } var payDays = await adjUnpaidPeriodFrom .AddDays(1) .GetDaysInRange(extendedAdjustedPeriodTo, data.DateNoticeGiven.DayOfWeek); // set pref period start date DateTime prefPeriodStartDate = data.InsolvencyDate.Date.AddMonths(-4); //step through paydaysCollection for (var paydaysCollectionIndex = 0; paydaysCollectionIndex < payDays.Count; paydaysCollectionIndex++) { var employmentDaysInPrefPeriod = 0; var maximumDaysInPrefPeriod = 0; var employmentDays = 0; var maximumDays = 0; var maximumEntitlement = 0.0m; // Debug.WriteLine($"Week start = {pDay.Start}"); //step through days of week for this payday var weekDates = await payDays[paydaysCollectionIndex].Date.GetWeekDatesAsync(); var weekDatesListIndex = 6; while (weekDatesListIndex >= 0) { var date = weekDates[weekDatesListIndex]; var dayNum = (int)date.DayOfWeek; //is this day a working day? if (data.ShiftPattern.Contains(dayNum.ToString())) { if (date >= adjUnpaidPeriodFrom.Date && date <= adjUnpaidPeriodTo.Date) { employmentDays++; if (date >= prefPeriodStartDate && date <= data.InsolvencyDate) { employmentDaysInPrefPeriod++; } } } //maxDays does not rely on working days if (date <= extendedAdjustedPeriodTo.Date && date <= adjUnpaidPeriodTo.Date) { maximumDays++; if (date >= prefPeriodStartDate && date <= data.InsolvencyDate) { maximumDaysInPrefPeriod++; } } weekDatesListIndex--; } decimal adjustedWeeklyWage = await data.WeeklyWage.GetAdjustedWeeklyWageAsync(data.ShiftPattern, data.UnpaidPeriodFrom, data.UnpaidPeriodTo, data.ApClaimAmount); //calculate Employer Liability for week var employerEntitlement = adjustedWeeklyWage / data.ShiftPattern.Count * employmentDays; var employerEntitlementInPrefPeriod = adjustedWeeklyWage / data.ShiftPattern.Count * employmentDaysInPrefPeriod; var maximumEntitlementInPrefPeriod = statutoryMax / 7 * maximumDaysInPrefPeriod; //calculate Statutory Maximum liability for week // if it is the last week if (paydaysCollectionIndex == payDays.Count - 1) { //if last payday is after dismissal date if (payDays[paydaysCollectionIndex].Date > data.DismissalDate.Date) { maximumEntitlement = statutoryMax / 7m * maximumDays; } else { maximumEntitlement = statutoryMax; maximumDays = 7; if (payDays[paydaysCollectionIndex].Date < data.DismissalDate.Date && payDays[paydaysCollectionIndex].Date < data.InsolvencyDate) { maximumEntitlementInPrefPeriod = statutoryMax; } } } else { maximumEntitlement = statutoryMax; } var grossEntitlement = Math.Min(maximumEntitlement, employerEntitlement); var taxRate = ConfigValueLookupHelper.GetTaxRate(options, DateTime.Now); var taxDeducated = Math.Round(await grossEntitlement.GetTaxDeducted(taxRate, data.IsTaxable), 2); var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, DateTime.Now); var niRate = ConfigValueLookupHelper.GetNIRate(options, DateTime.Now); var niDeducted = Math.Round(await grossEntitlement.GetNIDeducted(niThreshold, niRate, data.IsTaxable), 2); grossEntitlement = Math.Round(grossEntitlement, 2); var netLiability = await grossEntitlement.GetNetLiability(taxDeducated, niDeducted); Debug.WriteLine( $"week : {payDays[paydaysCollectionIndex]} MaxEntitlement : {maximumEntitlement} || EmpEntitlement = {employerEntitlement} || Min : {Math.Min(maximumEntitlement, employerEntitlement)} "); weeklyResult.Add(new NoticeWorkedNotPaidWeeklyResult { WeekNumber = paydaysCollectionIndex + 1, PayDate = payDays[paydaysCollectionIndex].Date, MaximumEntitlement = Math.Round(maximumEntitlement, 2), EmployerEntitlement = Math.Round(employerEntitlement, 2), GrossEntitlement = grossEntitlement, IsTaxable = data.IsTaxable, TaxDeducted = taxDeducated, NiDeducted = niDeducted, NetEntitlement = netLiability, MaximumDays = maximumDays, EmploymentDays = employmentDays, MaximumEntitlementIn4MonthPeriod = Math.Round(maximumEntitlementInPrefPeriod, 2), EmployerEntitlementIn4MonthPeriod = Math.Round(employerEntitlementInPrefPeriod, 2), GrossEntitlementIn4Months = Math.Round(Math.Min(maximumEntitlementInPrefPeriod, employerEntitlementInPrefPeriod), 2) }); } //outter loop payday collection calculationResult.InputSource = data.InputSource; calculationResult.StatutoryMax = statutoryMax; calculationResult.WeeklyResult = weeklyResult; if (traceInfoDate != null) { traceInfoDate.StartDate = data.UnpaidPeriodFrom; traceInfoDate.EndDate = data.UnpaidPeriodTo; } return(await Task.FromResult(calculationResult)); }
public async Task <NoticePayCompositeCalculationResponseDTO> PerformNoticePayCompositeCalculationAsync(NoticePayCompositeCalculationRequestModel data, IOptions <ConfigLookupRoot> options) { var result = new NoticePayCompositeCalculationResponseDTO(); var cnpOutput = new CompensatoryNoticePayCalculationResponseDTO(); var nwnpOutput = new NoticeWorkedNotPaidCompositeOutput(); var rp1TraceInfo = new TraceInfo(); var rp14TraceInfo = new TraceInfo(); //CNP calculation if (data.Cnp != null) { cnpOutput = await _cnpService.PerformCompensatoryNoticePayCalculationAsync(data.Cnp, options); cnpOutput.WeeklyResults.ForEach(x => x.IsSelected = true); } //NWNP calculation if (data.Nwnp != null && data.Nwnp.Any()) { var statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, data.Nwnp.FirstOrDefault().InsolvencyDate); foreach (var nwnp in data.Nwnp) { var traceDate = new TraceInfoDate(); var res = await _nwnpService.PerformNwnpCalculationAsync(nwnp, options, traceDate); if (res.InputSource == InputSource.Rp1) { //nwnpOutput.nwnpResults.rP1ResultsList.Add(res); nwnpOutput.rp1Results.WeeklyResult.AddRange(res.WeeklyResult); rp1TraceInfo.Dates.Add(traceDate); rp1TraceInfo.NumberOfDays = 0.00m; foreach (var week in res.WeeklyResult) { rp1TraceInfo.NumberOfDays += week.EmploymentDays; } } else if (res.InputSource == InputSource.Rp14a) { //nwnpOutput.nwnpResults.rP14aResultsList.Add(res); nwnpOutput.rp14aResults.WeeklyResult.AddRange(res.WeeklyResult); rp14TraceInfo.Dates.Add(traceDate); rp14TraceInfo.NumberOfDays = 0.00m; foreach (var week in res.WeeklyResult) { rp14TraceInfo.NumberOfDays += week.EmploymentDays; } } } var rp1Total = 0m; var rp14aTotal = 0m; //merge pay weeks nwnpOutput.rp1Results = await nwnpOutput.rp1Results.MergePayWeeks(options); nwnpOutput.rp1Results.InputSource = InputSource.Rp1; nwnpOutput.rp1Results.StatutoryMax = statutoryMax; rp1Total = nwnpOutput.rp1Results.WeeklyResult.Sum(x => x.NetEntitlement); //merge pay weeks nwnpOutput.rp14aResults = await nwnpOutput.rp14aResults.MergePayWeeks(options); nwnpOutput.rp14aResults.InputSource = InputSource.Rp14a; nwnpOutput.rp14aResults.StatutoryMax = statutoryMax; rp14aTotal = nwnpOutput.rp14aResults.WeeklyResult.Sum(x => x.NetEntitlement); //select the input source // Choose RP1 list or RP14a list with the lowest total NetEntitlement if ((rp1Total < rp14aTotal && rp14aTotal != 0 && rp1Total != 0) || (rp1Total > 0 && rp14aTotal == 0)) { nwnpOutput.SelectedInputSource = InputSource.Rp1; nwnpOutput.rp1Results.WeeklyResult.ForEach(x => x.IsSelected = true); nwnpOutput.rp14aResults.WeeklyResult.ForEach(x => x.IsSelected = false); nwnpOutput.TraceInfo = await rp1TraceInfo.ConvertToJson(); } else { nwnpOutput.SelectedInputSource = InputSource.Rp14a; nwnpOutput.rp1Results.WeeklyResult.ForEach(x => x.IsSelected = false); nwnpOutput.rp14aResults.WeeklyResult.ForEach(x => x.IsSelected = true); nwnpOutput.TraceInfo = await rp14TraceInfo.ConvertToJson(); } } result.Cnp = cnpOutput; result.Nwnp = nwnpOutput; return(result); }