public async Task <BasicAwardCalculationResponseDTO> PerformBasicAwardCalculationAsync( BasicAwardCalculationRequestModel data, IOptions <ConfigLookupRoot> options) { DateTime date = DateTime.Today; var taxRate = ConfigValueLookupHelper.GetTaxRate(options, date); var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, date); var niUpperThreshold = ConfigValueLookupHelper.GetNIUpperThreshold(options, date); var niRate = ConfigValueLookupHelper.GetNIRate(options, date); var niUpperRate = ConfigValueLookupHelper.GetNIUpperRate(options, date); decimal taxDeducted = Math.Round(await data.BasicAwardAmount.GetTaxDeducted(taxRate, data.IsTaxable), 2); decimal niDeducted = Math.Round(await data.BasicAwardAmount.GetNIDeducted(niThreshold, niUpperThreshold, niRate, niUpperRate, data.IsTaxable), 2); var response = new BasicAwardCalculationResponseDTO() { GrossEntitlement = Math.Round(data.BasicAwardAmount, 2), IsTaxable = data.IsTaxable, TaxDeducted = taxDeducted, NIDeducted = niDeducted, NetEntitlement = Math.Round(data.BasicAwardAmount, 2) - taxDeducted - niDeducted, PreferentialClaim = 0m, NonPreferentialClaim = Math.Round(data.BasicAwardAmount, 2) }; return(await Task.FromResult(response)); }
public void GetBenefitsWaitingDays_ThrowsException_WhenNoConfigForDate() { var date = new DateTime(2010, 1, 1); //Act Exception ex = Assert.Throws <MissingConfigurationException>(() => ConfigValueLookupHelper.GetBenefitsWaitingDays(_configOptions, date)); }
public void GetStatutaryMax_ThrowsException_WhenNoConfigForDate() { var date = new DateTime(2010, 1, 1); //Act Exception ex = Assert.Throws <MissingConfigurationException>(() => ConfigValueLookupHelper.GetStatutoryMax(_configOptions, date)); }
public async Task <ApportionmentCalculationResponseDTO> PerformApportionmentCalculationAsync( ApportionmentCalculationRequestModel data, IOptions <ConfigLookupRoot> options) { decimal preferentialClaim = 0.0m; decimal preferentialLimit = ConfigValueLookupHelper.GetPreferentialLimit(options, DateTime.Now); decimal apportionmentPercentage = 1.0m; if (data.TupeStatus) { preferentialClaim = Math.Min(data.GrossPaidInFourMonth, preferentialLimit); } if (data.TupeStatus == false && data.TotalClaimedInFourMonth > 0.0m) { apportionmentPercentage = (data.TotalClaimedInFourMonth <= preferentialLimit) ? 1m : data.GrossPaidInFourMonth / data.TotalClaimedInFourMonth; preferentialClaim = (data.TotalClaimedInFourMonth > preferentialLimit ? preferentialLimit * apportionmentPercentage : data.GrossPaidInFourMonth * apportionmentPercentage); } apportionmentPercentage = Math.Round(apportionmentPercentage * 100, 4); var result = new ApportionmentCalculationResponseDTO() { ApportionmentPercentage = apportionmentPercentage, PrefClaim = Math.Round(preferentialClaim, 2), NonPrefClaim = Math.Round((data.GrossEntitlement - preferentialClaim), 2), TupeStatus = data.TupeStatus }; return(await Task.FromResult(result)); }
public void GetTaxRate_ThrowsException_WhenNoConfigForDate() { var date = new DateTime(1899, 1, 1); //Act Exception ex = Assert.Throws <MissingConfigurationException>(() => ConfigValueLookupHelper.GetTaxRate(_configOptions, date)); }
public void GetNotionalBenefitsWeeklyRate_ThrowsException_WhenNoConfigForDate() { var date = new DateTime(1899, 1, 1); var age = 26; //Act Exception ex = Assert.Throws <MissingConfigurationException>(() => ConfigValueLookupHelper.GetNotionalBenefitsWeeklyRate(_configOptions, date, age)); }
public void GetStatutaryMax_Returns_CorrectStatMax_ForGiven_InsolvencyDate(DateTime insolvencyDate, decimal expectedStatutoryMax) { //Arrange //Act var result = ConfigValueLookupHelper.GetStatutoryMax(_configOptions, insolvencyDate); //Assert result.Should().Be(expectedStatutoryMax); }
public void GetPreferentialLimit_Returns_CorrectPreferentialLimit_ForGiven_date( DateTime someDate, decimal expectedPreferntialLimit) { //Arrange //Act var result = ConfigValueLookupHelper.GetPreferentialLimit(_configOptions, someDate); //Assert result.Should().Be(expectedPreferntialLimit); }
public void GetBenefitsWaitingDays_Returns_CorrectBenefitsWaitingDays_ForGiven_benefitsStartDate( DateTime benefitsStartDate, decimal expectedBenefitsWaitingDays) { //Arrange //Act var result = ConfigValueLookupHelper.GetBenefitsWaitingDays(_configOptions, benefitsStartDate); //Assert result.Should().Be(expectedBenefitsWaitingDays); }
public void GetNIRate_Returns_CorrectNIRate_ForGiven_date( DateTime someDate, decimal expectedNIRate) { //Arrange //Act var result = ConfigValueLookupHelper.GetNIRate(_configOptions, someDate); //Assert result.Should().Be(expectedNIRate); }
public void GetNotionalBenefitsWeeklyRate_Returns_CorrectNotionalBenefitsWeeklyRate_ForGivenDateAndAge( DateTime someDate, int age, decimal expectedWeeklyRate) { //Arrange //Act var result = ConfigValueLookupHelper.GetNotionalBenefitsWeeklyRate(_configOptions, someDate, age); //Assert result.Should().Be(expectedWeeklyRate); }
public static async Task <NoticeWorkedNotPaidResponseDTO> MergePayWeeks(this NoticeWorkedNotPaidResponseDTO nwnpList, IOptions <ConfigLookupRoot> options) { var weeksToMerge = nwnpList.WeeklyResult.GroupBy(x => x.PayDate).Where(x => x.Count() > 1).ToList(); foreach (var week in weeksToMerge) { var payDate = week.Key; var mergedWeek = new NoticeWorkedNotPaidWeeklyResult(); mergedWeek.PayDate = week.Select(x => x.PayDate).FirstOrDefault(); mergedWeek.MaximumDays = week.Max(x => x.MaximumDays); mergedWeek.EmploymentDays = week.Sum(x => x.EmploymentDays); mergedWeek.MaximumEntitlement = week.Max(x => x.MaximumEntitlement); mergedWeek.EmployerEntitlement = week.Sum(x => x.EmployerEntitlement); mergedWeek.GrossEntitlement = Math.Min(mergedWeek.MaximumEntitlement, mergedWeek.EmployerEntitlement); mergedWeek.IsTaxable = week.Select(x => x.IsTaxable).FirstOrDefault(); var taxRate = ConfigValueLookupHelper.GetTaxRate(options, DateTime.Now); mergedWeek.TaxDeducted = Math.Round(await mergedWeek.GrossEntitlement.GetTaxDeducted(taxRate, mergedWeek.IsTaxable), 2); var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, DateTime.Now); var niRate = ConfigValueLookupHelper.GetNIRate(options, DateTime.Now); mergedWeek.NiDeducted = Math.Round(await mergedWeek.GrossEntitlement.GetNIDeducted(niThreshold, niRate, mergedWeek.IsTaxable), 2); mergedWeek.NetEntitlement = await mergedWeek.GrossEntitlement.GetNetLiability(mergedWeek.TaxDeducted, mergedWeek.NiDeducted); mergedWeek.MaximumEntitlementIn4MonthPeriod = week.Max(x => x.MaximumEntitlementIn4MonthPeriod); mergedWeek.EmployerEntitlementIn4MonthPeriod = week.Sum(x => x.EmployerEntitlementIn4MonthPeriod); mergedWeek.GrossEntitlementIn4Months = Math.Min(mergedWeek.MaximumEntitlementIn4MonthPeriod, mergedWeek.EmployerEntitlementIn4MonthPeriod); mergedWeek.IsSelected = false; //remove the weeks with the same payDate from the selected list. nwnpList.WeeklyResult.RemoveAll(x => x.PayDate == payDate); //add the merged week for that paydate nwnpList.WeeklyResult.Add(mergedWeek); } //update weeknumber on the merged list var i = 1; nwnpList.WeeklyResult = nwnpList.WeeklyResult.OrderBy(x => x.PayDate).ToList(); nwnpList.WeeklyResult.ForEach(x => x.WeekNumber = i++); return(nwnpList); }
public async Task ReturnsCorrectPrefAndNonPrefClaimValues_WhenTupeAndExceedsLimit() { // Arrange var request = new ApportionmentCalculationRequestModel() { GrossPaidInFourMonth = 898.00m, GrossEntitlement = 1002.24m, TotalClaimedInFourMonth = 4698.00m, TupeStatus = true }; // Act var result = await _service.PerformApportionmentCalculationAsync(request, _options); // Assert Math.Round(result.PrefClaim, 2).Should() .Be(ConfigValueLookupHelper.GetPreferentialLimit(_options, DateTime.Now)); Math.Round(result.NonPrefClaim, 2).Should() .Be(Math.Round(request.GrossEntitlement, 2) - Math.Round(result.PrefClaim, 2)); result.TupeStatus.Should().Be(request.TupeStatus); result.ApportionmentPercentage.Should().Be(100.0m); }
public static async Task <ArrearsOfPayResponseDTO> MergeWeeklyResults(this List <ArrearsOfPayResponseDTO> list, string inputSource, IOptions <ConfigLookupRoot> options) { var weekList = list.SelectMany(x => x.WeeklyResult).ToList(); var weeksToMerge = weekList.GroupBy(x => x.PayDate) .Where(x => x.Count() > 1) .ToList(); foreach (var week in weeksToMerge) { var payDate = week.Key; var mergedWeek = new ArrearsOfPayWeeklyResult(); mergedWeek.PayDate = week.First().PayDate; mergedWeek.ApPayRate = week.Max(x => x.ApPayRate); mergedWeek.MaximumEntitlement = week.Max(x => x.MaximumEntitlement); mergedWeek.EmployerEntitlement = week.Sum(x => x.EmployerEntitlement); mergedWeek.MaximumDays = week.Max(x => x.MaximumDays); mergedWeek.EmploymentDays = week.Sum(x => x.EmploymentDays); mergedWeek.MaximumEntitlementIn4MonthPeriod = week.Max(x => x.MaximumEntitlementIn4MonthPeriod); mergedWeek.EmployerEntitlementIn4MonthPeriod = week.Sum(x => x.EmployerEntitlementIn4MonthPeriod); mergedWeek.GrossEntitlementIn4Months = Math.Min(mergedWeek.MaximumEntitlementIn4MonthPeriod, mergedWeek.EmployerEntitlementIn4MonthPeriod); mergedWeek.GrossEntitlement = Math.Min(mergedWeek.MaximumEntitlement, mergedWeek.EmployerEntitlement); mergedWeek.IsTaxable = week.Select(x => x.IsTaxable).FirstOrDefault(); var taxRate = ConfigValueLookupHelper.GetTaxRate(options, DateTime.Now); mergedWeek.TaxDeducted = Math.Round(await mergedWeek.GrossEntitlement.GetTaxDeducted(taxRate, mergedWeek.IsTaxable), 2); var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, DateTime.Now); var niRate = ConfigValueLookupHelper.GetNIRate(options, DateTime.Now); mergedWeek.NIDeducted = Math.Round(await mergedWeek.GrossEntitlement.GetNIDeducted(niThreshold, niRate, mergedWeek.IsTaxable), 2); mergedWeek.NetEntitlement = await mergedWeek.GrossEntitlement.GetNetLiability(mergedWeek.TaxDeducted, mergedWeek.NIDeducted); //remove the weeks with the same payDate from the selected list. weekList.RemoveAll(x => x.PayDate == payDate); weekList.Add(mergedWeek); } // update weeknumber on the merged list var i = 1; weekList = weekList.OrderBy(x => x.PayDate).ToList(); weekList.ForEach(x => x.WeekNumber = i++); if (list.Any()) { return(new ArrearsOfPayResponseDTO() { StatutoryMax = list.First().StatutoryMax, InputSource = inputSource, DngApplied = list.Any(x => x.DngApplied), RunNWNP = list.Any(x => x.RunNWNP), WeeklyResult = weekList }); } else { return(new ArrearsOfPayResponseDTO() { StatutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, DateTime.Today), InputSource = inputSource, DngApplied = false, RunNWNP = false, }); } }
public async Task <ArrearsOfPayResponseDTO> PerformCalculationAsync( ArrearsOfPayCalculationRequestModel data, IOptions <ConfigLookupRoot> options, TraceInfo traceInfo = null) { var calculationResult = new ArrearsOfPayResponseDTO(); var weeklyresult = new List <ArrearsOfPayWeeklyResult>(); int weekNumber = 1; var totalDays = 0.00m; var statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, data.DismissalDate); var relevantNoticeDate = await data.DateNoticeGiven.GetRelevantNoticeDate(data.DismissalDate); var noticeEntitlementWeeks = await data.EmploymentStartDate.GetNoticeEntitlementWeeks(relevantNoticeDate); //not adjusted start date var projectedNoticeEndDate = await relevantNoticeDate.GetProjectedNoticeDate(noticeEntitlementWeeks); var adjustedPeriodFrom = await data.UnpaidPeriodFrom.Date.GetAdjustedPeriodFromAsync(data.InsolvencyDate.Date); var adjustedPeriodTo = await data.UnpaidPeriodTo.Date.GetAdjustedPeriodToAsync(data.InsolvencyDate.Date, data.DismissalDate.Date); DateTime extendedAdjustedPeriodTo = adjustedPeriodTo; if (extendedAdjustedPeriodTo.DayOfWeek != (DayOfWeek)data.PayDay) { extendedAdjustedPeriodTo = adjustedPeriodTo.AddDays(7); } var payDays = await adjustedPeriodFrom.Date.GetDaysInRange(extendedAdjustedPeriodTo.Date, (DayOfWeek)data.PayDay); decimal adjustedWeeklyWage = await data.WeeklyWage.GetAdjustedWeeklyWageAsync(data.ShiftPattern, data.UnpaidPeriodFrom, data.UnpaidPeriodTo, data.ApClaimAmount); decimal WeeklyWageBetweenNoticeGivenAndNoticeEnd = decimal.Zero; DateTime prefPeriodStartDate = data.InsolvencyDate.Date.AddMonths(-4); prefPeriodStartDate = (prefPeriodStartDate <= data.EmploymentStartDate.Date) ? data.EmploymentStartDate.Date : prefPeriodStartDate.Date; DateTime prefPeriodEndDate = (data.DismissalDate < data.InsolvencyDate) ? data.DismissalDate.Date : data.InsolvencyDate.Date; //step through paydaysCollection foreach (var payWeekEnd in payDays) { var employmentDays = 0; var employmentDaysBetweenNoticeGivenAndNoticeEndDate = 0; var maximumEntitlement = 0.0m; var employmentDaysInPrefPeriod = 0; var employmentDaysInPrefPeriodPostDNG = 0; var maximumEntitlementInPrefPeriod = 0.0m; var employerEntitlementInPrefPeriod = 0.0m; for (int j = 6; j >= 0; j--) { DateTime date = payWeekEnd.AddDays(-j); //is this day a working day? if (await date.IsEmploymentDay(data.ShiftPattern)) { if (date >= adjustedPeriodFrom.Date && date <= adjustedPeriodTo.Date) { if (date > data.DateNoticeGiven.Date && date <= projectedNoticeEndDate.Date) { employmentDaysBetweenNoticeGivenAndNoticeEndDate++; if (date >= prefPeriodStartDate && date <= prefPeriodEndDate) { employmentDaysInPrefPeriodPostDNG++; } } else { employmentDays++; if (date >= prefPeriodStartDate && date <= prefPeriodEndDate) { employmentDaysInPrefPeriod++; } } } } } var weekStartDate = payWeekEnd.AddDays(-6); var maximumDays = await weekStartDate.GetNumDaysInIntersectionOfTwoRangesWithLimit(payWeekEnd, data.EmploymentStartDate.Date, data.DismissalDate.Date, data.DateNoticeGiven.Date, data.InsolvencyDate.Date); var maximumDaysInPrefPeriod = await weekStartDate.GetNumDaysInIntersectionOfTwoRangesWithLimit(payWeekEnd, prefPeriodStartDate, prefPeriodEndDate, data.DateNoticeGiven.Date, data.InsolvencyDate.Date); //calculate Employer Liability for week var employerEntitlement = adjustedWeeklyWage / data.ShiftPattern.Count * employmentDays + WeeklyWageBetweenNoticeGivenAndNoticeEnd / data.ShiftPattern.Count * employmentDaysBetweenNoticeGivenAndNoticeEndDate; employerEntitlementInPrefPeriod = adjustedWeeklyWage / data.ShiftPattern.Count * employmentDaysInPrefPeriod; employerEntitlementInPrefPeriod = Math.Round(employerEntitlementInPrefPeriod, 2); //calculate Statutory Maximum liability for week maximumEntitlementInPrefPeriod = Math.Round((statutoryMax / 7 * maximumDaysInPrefPeriod), 2); maximumEntitlement = statutoryMax / 7 * maximumDays; 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); weeklyresult.Add(new ArrearsOfPayWeeklyResult { WeekNumber = weekNumber++, PayDate = payWeekEnd, ApPayRate = Math.Round(adjustedWeeklyWage, 2), EmployerEntitlement = Math.Round(employerEntitlement, 2), MaximumEntitlement = Math.Round(maximumEntitlement, 2), GrossEntitlement = grossEntitlement, IsTaxable = data.IsTaxable, TaxDeducted = taxDeducated, NIDeducted = niDeducted, NetEntitlement = netLiability, MaximumDays = maximumDays, EmploymentDays = employmentDays + employmentDaysInPrefPeriodPostDNG, MaximumEntitlementIn4MonthPeriod = maximumEntitlementInPrefPeriod, EmployerEntitlementIn4MonthPeriod = employerEntitlementInPrefPeriod, GrossEntitlementIn4Months = Math.Min(maximumEntitlementInPrefPeriod, employerEntitlementInPrefPeriod) }); totalDays += employmentDays + employmentDaysInPrefPeriodPostDNG; } //outter loop for paydays collection calculationResult.InputSource = data.InputSource; calculationResult.StatutoryMax = Math.Round(statutoryMax, 2); calculationResult.DngApplied = (adjustedPeriodTo > data.DateNoticeGiven.Date); calculationResult.RunNWNP = (adjustedPeriodTo > data.DateNoticeGiven.Date); calculationResult.WeeklyResult = weeklyresult; traceInfo?.Dates.Add(new TraceInfoDate { StartDate = data.UnpaidPeriodFrom, EndDate = data.UnpaidPeriodTo }); if (traceInfo != null) { traceInfo.NumberOfDays = totalDays; } return(await Task.FromResult(calculationResult)); }
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 <RedundancyPaymentResponseDto> PerformRedundancyPayCalculationAsync( RedundancyPaymentCalculationRequestModel data, IOptions <ConfigLookupRoot> options) { var calculationResult = new RedundancyPaymentResponseDto(); var adjStartDate = await data.EmploymentStartDate.GetAdjustedEmploymentStartDate(data.EmploymentBreaks); var relevantNoticeDate = await data.DateNoticeGiven.GetRelevantNoticeDate(data.DismissalDate); var noticeEntitlementWeeks = await adjStartDate.GetNoticeEntitlementWeeks(relevantNoticeDate); var projectedNoticeDate = await relevantNoticeDate.GetProjectedNoticeDate(noticeEntitlementWeeks); var relevantDismissalDate = await data.DismissalDate.GetRelevantDismissalDate(projectedNoticeDate); var statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, relevantDismissalDate); var totalYearsOfService = await adjStartDate.GetServiceYearsAsync(relevantDismissalDate); //limit maximum service years to 20 if (totalYearsOfService > 20) { totalYearsOfService = 20; } var appliedRateOfPay = Math.Min(statutoryMax, data.WeeklyWage); int yearsOfServiceUpto21 = 0, yearsOfService22To41 = 0; var yearsOfServiceOver41 = await data.DateOfBirth.GetYearsOfServiceOver41(adjStartDate, relevantDismissalDate); if (yearsOfServiceOver41 < totalYearsOfService) { yearsOfService22To41 = await data.DateOfBirth.GetYearsOfServiceFrom22To41(adjStartDate, relevantDismissalDate); if (yearsOfServiceOver41 + yearsOfService22To41 > totalYearsOfService) { yearsOfService22To41 = totalYearsOfService - yearsOfServiceOver41; } } if (yearsOfServiceOver41 + yearsOfService22To41 < totalYearsOfService) { yearsOfServiceUpto21 = totalYearsOfService - (yearsOfService22To41 + yearsOfServiceOver41); } var redundancyPayWeeks = decimal.Multiply(yearsOfServiceUpto21, 0.5m) + yearsOfService22To41 + decimal.Multiply(yearsOfServiceOver41, 1.5m); var grossEntitlement = redundancyPayWeeks * appliedRateOfPay; var grossEntitlementFinal = Math.Max(0m, Math.Round(grossEntitlement, 2)); var employerPartPaymentFinal = Math.Round(data.EmployerPartPayment, 2); var netEntitlementFinal = Math.Max(0m, (grossEntitlementFinal - employerPartPaymentFinal)); calculationResult.AdjEmploymentStartDate = adjStartDate; calculationResult.NoticeDateForRedundancyPay = relevantDismissalDate; calculationResult.NoticeEntitlementWeeks = noticeEntitlementWeeks; calculationResult.RedundancyPayWeeks = Math.Round(redundancyPayWeeks, 4); calculationResult.YearsOfServiceUpto21 = yearsOfServiceUpto21; calculationResult.YearsOfServiceFrom22To41 = yearsOfService22To41; calculationResult.YearsServiceOver41 = yearsOfServiceOver41; //calculationResult.GrossEntitlement = grossEntitlementFinal; calculationResult.GrossEntitlement = netEntitlementFinal; //TEMP CHANGE: Gross entitlement set to Net entitlement as quick fix for CMS message to BW calculationResult.EmployerPartPayment = employerPartPaymentFinal; calculationResult.NetEntitlement = netEntitlementFinal; calculationResult.PreferentialClaim = 0m; calculationResult.NonPreferentialClaim = netEntitlementFinal; calculationResult.StatutoryMax = statutoryMax; return(calculationResult); }
public async Task <HolidayPayAccruedResponseDTO> PerformHolidayPayAccruedCalculationAsync( HolidayPayAccruedCalculationRequestModel data, IOptions <ConfigLookupRoot> options) { var response = new ResponseDto <HolidayPayAccruedResponseDTO>(); var calculationResult = new HolidayPayAccruedResponseDTO(); var shiftPattern = data.ShiftPattern; var twelveMonthsPrior = data.InsolvencyDate.Date.AddMonths(-12).AddDays(1); var adjHolYearStart = await data.GetHolidayYearStart(); if (adjHolYearStart < twelveMonthsPrior) { adjHolYearStart = twelveMonthsPrior; } var holYearEndDate = await data.GetHolidayYearEnd(); var statMaxWeeklyPay = ConfigValueLookupHelper.GetStatutoryMax(options, data.DismissalDate); // Calculate totals for holiday pay accrued int totalBusinessDaysInClaim = await data.HolidayYearStart.GetNumBusinessDaysInRange(holYearEndDate, shiftPattern); int totalWorkingDaysInClaim = 0; totalWorkingDaysInClaim = await totalWorkingDaysInClaim.GetTotalWorkingDaysInHolidayClaim( data.ShiftPattern, adjHolYearStart, holYearEndDate.Date, data.DismissalDate.Date, data.InsolvencyDate.Date, data.EmpStartDate.Date); decimal limitedDaysCFwd = 0.00m; limitedDaysCFwd = await limitedDaysCFwd.GetLimitedDaysCFwd(shiftPattern, data.DaysCFwd.GetValueOrDefault()); decimal statHolEntitlement = 0.00m; statHolEntitlement = await statHolEntitlement.GetStatutoryHolidayEntitlement(shiftPattern); decimal adjHolidayEntitlement = await statHolEntitlement.GetAdjustedHolidayEntitlement(data.ContractedHolEntitlement.GetValueOrDefault()); decimal proRataAccruedDays = 0.00m; proRataAccruedDays = await proRataAccruedDays.GetProRataAccruedDays((decimal)adjHolidayEntitlement, totalBusinessDaysInClaim, totalWorkingDaysInClaim, limitedDaysCFwd, data.DaysTaken.GetValueOrDefault(), shiftPattern, data.IpConfirmedDays); calculationResult.BusinessDaysInClaim = totalBusinessDaysInClaim; calculationResult.StatutoryMax = Math.Round(statMaxWeeklyPay, 2); calculationResult.WorkingDaysInClaim = totalWorkingDaysInClaim; calculationResult.HolidaysOwed = Math.Round(adjHolidayEntitlement, 4); calculationResult.ProRataAccruedDays = Math.Round(proRataAccruedDays, 4); // Calculate weekly breakdown of holiday pay accrued var taxRate = ConfigValueLookupHelper.GetTaxRate(options, DateTime.Now); var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, DateTime.Now); var niRate = ConfigValueLookupHelper.GetNIRate(options, DateTime.Now); decimal entitlementWeeks = (proRataAccruedDays / data.ShiftPattern.Count); int wholeWeeks = (int)Math.Floor(entitlementWeeks); decimal partWeekDays = (entitlementWeeks - wholeWeeks) * shiftPattern.Count; decimal maximumEntitlement = 0.00m; decimal employerEntitlement = 0.00m; var weeklyResults = new List <HolidayPayAccruedWeeklyResult>(); decimal maxDays; decimal empDays; // Loop through each holiday entitlement week for (var weekNumber = 1; weekNumber <= Math.Ceiling(entitlementWeeks); weekNumber++) { var weeklyResult = new HolidayPayAccruedWeeklyResult(); maxDays = 0; empDays = 0; if (weekNumber <= wholeWeeks) { maximumEntitlement = statMaxWeeklyPay; employerEntitlement = data.WeeklyWage.GetValueOrDefault(); } if (weekNumber > wholeWeeks) { var weekDayNumber = data.PayDay; // This must be the last (partial) week so loop through each day, checking for working days for (var day = 0; day <= 6; day++) { weekDayNumber++; if (weekDayNumber > (int)DayOfWeek.Saturday) { weekDayNumber = (int)DayOfWeek.Sunday; } if (shiftPattern.Contains(weekDayNumber.ToString())) { if (partWeekDays > 1) { partWeekDays--; maxDays++; empDays++; } else if (partWeekDays > 0) { maxDays = maxDays + partWeekDays; empDays = empDays + partWeekDays; partWeekDays = 0; } } else if (partWeekDays > 0) { maxDays++; } if (day == 6) { maximumEntitlement = (maxDays * statMaxWeeklyPay / 7); employerEntitlement = (empDays * data.WeeklyWage.GetValueOrDefault() / shiftPattern.Count); } } } decimal grossEntitlement = 0.00m; grossEntitlement = await grossEntitlement.GetGrossEntitlement(maximumEntitlement, employerEntitlement); decimal taxDeducted = Math.Round(await grossEntitlement.GetTaxDeducted(taxRate, (bool)data.IsTaxable), 2); decimal niDeducted = Math.Round(await grossEntitlement.GetNIDeducted(niThreshold, niRate, (bool)data.IsTaxable), 2); grossEntitlement = Math.Round(grossEntitlement, 2); weeklyResult.WeekNumber = weekNumber; weeklyResult.MaximumEntitlement = Math.Round(maximumEntitlement, 2); weeklyResult.EmployerEntitlement = Math.Round(employerEntitlement, 2); weeklyResult.GrossEntitlement = grossEntitlement; weeklyResult.IsTaxable = (bool)data.IsTaxable; weeklyResult.TaxDeducted = taxDeducted; weeklyResult.NiDeducted = niDeducted; weeklyResult.NetEntitlement = grossEntitlement - taxDeducted - niDeducted; weeklyResult.PreferentialClaim = grossEntitlement; weeklyResult.NonPreferentialClaim = 0m; weeklyResults.Add(weeklyResult); } calculationResult.WeeklyResults = weeklyResults; return(await Task.FromResult(calculationResult)); }
public async Task <HolidayTakenNotPaidResponseDTO> PerformCalculationAsync( List <HolidayTakenNotPaidCalculationRequestModel> data, string inputSource, decimal maxDaysInCurrentHolidayYear, decimal maxDaysInTotal, DateTime?holidayYearStart, IOptions <ConfigLookupRoot> options, TraceInfo traceInfo = null) { var statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, data.First().DismissalDate); var calculationResult = new HolidayTakenNotPaidResponseDTO(); calculationResult.StatutoryMax = Math.Round(statutoryMax, 2); calculationResult.InputSource = inputSource; var firstRequest = data.FirstOrDefault(r => r.InputSource == inputSource); if (firstRequest != null) { var tweleveMonthsPrior = firstRequest.InsolvencyDate.Date.AddMonths(-12).AddDays(1); var htnpEndDate = firstRequest.DismissalDate.Date < firstRequest.InsolvencyDate.Date ? firstRequest.DismissalDate.Date : firstRequest.InsolvencyDate.Date; var weeks = new List <Week>(); if (holidayYearStart.HasValue) { var htnpDaysInCurrentHolidayYear = await data.GetHTNPDays(inputSource, holidayYearStart.Value.Date, htnpEndDate); var htnpDaysIn12MonthsPrior = await data.GetHTNPDays(inputSource, tweleveMonthsPrior, holidayYearStart.Value.Date.AddDays(-1)); await SelectDaysAndConvertToWeeks(weeks, htnpDaysInCurrentHolidayYear, firstRequest, maxDaysInCurrentHolidayYear); var numHtnpDaysSelected = weeks.Where(x => x.IsSelected).Sum(x => x.EmploymentDays); await SelectDaysAndConvertToWeeks(weeks, htnpDaysIn12MonthsPrior, firstRequest, maxDaysInTotal - numHtnpDaysSelected); } else { var htnpDays = await data.GetHTNPDays(inputSource, tweleveMonthsPrior, htnpEndDate); await SelectDaysAndConvertToWeeks(weeks, htnpDays, firstRequest, maxDaysInTotal); } DateTime prefPeriodStartDate = firstRequest.InsolvencyDate.Date.AddMonths(-4); DateTime prefPeriodEndDate = (firstRequest.DismissalDate < firstRequest.InsolvencyDate) ? firstRequest.DismissalDate.Date : firstRequest.InsolvencyDate.Date; // generate the output weeks int weekNum = 1; decimal total_days = 0.00m; foreach (var week in weeks.OrderBy(x => x.PayDate).ThenBy(x => x.IsSelected)) { var weekStartDate = week.PayDate.AddDays(-6); var maximumDays = await weekStartDate.GetNumDaysInIntersectionOfTwoRanges(week.PayDate, DateTime.MinValue.Date, firstRequest.DismissalDate.Date); var maximumDaysInPrefPeriod = await weekStartDate.GetNumDaysInIntersectionOfTwoRanges(week.PayDate, prefPeriodStartDate, prefPeriodEndDate); //calculate Employer Liability for week var employerEntitlement = firstRequest.WeeklyWage / firstRequest.ShiftPattern.Count * week.EmploymentDays; var employerEntitlementInPrefPeriod = firstRequest.WeeklyWage / firstRequest.ShiftPattern.Count * week.EmploymentDaysInPrefPeriod; var maximumEntitlement = statutoryMax / 7 * maximumDays; var maximumEntitlementInPrefPeriod = statutoryMax / 7 * maximumDaysInPrefPeriod; var grossEntitlement = Math.Min(maximumEntitlement, employerEntitlement); var taxRate = ConfigValueLookupHelper.GetTaxRate(options, DateTime.Now); var taxDeducated = Math.Round(await grossEntitlement.GetTaxDeducted(taxRate, firstRequest.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, firstRequest.IsTaxable), 2); grossEntitlement = Math.Round(grossEntitlement, 2); var netLiability = await grossEntitlement.GetNetLiability(taxDeducated, niDeducted); total_days += week.EmploymentDays; calculationResult.WeeklyResult.Add(new HolidayTakenNotPaidWeeklyResult() { WeekNumber = weekNum++, PayDate = week.PayDate, IsSelected = week.IsSelected, MaximumEntitlement = Math.Round(maximumEntitlement, 2), EmployerEntitlement = Math.Round(employerEntitlement, 2), GrossEntitlement = grossEntitlement, IsTaxable = firstRequest.IsTaxable, TaxDeducted = taxDeducated, NiDeducted = niDeducted, NetEntitlement = netLiability, MaximumDays = maximumDays, EmploymentDays = Math.Round(week.EmploymentDays, 4), MaximumEntitlementIn4MonthPeriod = Math.Round(maximumEntitlementInPrefPeriod, 2), EmployerEntitlementIn4MonthPeriod = Math.Round(employerEntitlementInPrefPeriod, 2), GrossEntitlementIn4Months = Math.Round(Math.Min(maximumEntitlementInPrefPeriod, employerEntitlementInPrefPeriod), 2), }); } foreach (var req in data.Where(x => x.InputSource == inputSource)) { traceInfo?.Dates.Add(new TraceInfoDate { StartDate = req.UnpaidPeriodFrom, EndDate = req.UnpaidPeriodTo, }); } if (traceInfo != null) { traceInfo.NumberOfDays = total_days; } } return(await Task.FromResult(calculationResult)); }
public async Task <ProtectiveAwardResponseDTO> PerformProtectiveAwardCalculationAsync( ProtectiveAwardCalculationRequestModel data, IOptions <ConfigLookupRoot> options) { var statMaxDate = data.DismissalDate; if (data.InsolvencyDate.Date > statMaxDate) { statMaxDate = data.InsolvencyDate.Date; } if (data.TribunalAwardDate.Date > statMaxDate) { statMaxDate = data.TribunalAwardDate.Date; } var statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, statMaxDate); var taxRate = ConfigValueLookupHelper.GetTaxRate(options, data.DismissalDate); var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, DateTime.Now); var niRate = ConfigValueLookupHelper.GetNIRate(options, DateTime.Now); DayOfWeek payDay = (DayOfWeek)data.PayDay; bool isTaxable = (await data.DismissalDate.GetTaxYear() == await data.TribunalAwardDate.GetTaxYear()); //calculate paAwardEndDate e.g. paAwardStartDate + 30 days var protectiveAwardEndDate = data.ProtectiveAwardStartDate.Date.AddDays(data.ProtectiveAwardDays.Value - 1); var paBenefitsEndDate = protectiveAwardEndDate; // get pay weeks in PA Award var endDate = protectiveAwardEndDate; if (endDate.DayOfWeek != payDay) { endDate = endDate.AddDays(7); } // set pref period start date DateTime prefPeriodStartDate = data.InsolvencyDate.Date.AddMonths(-4); var payDays = await data.ProtectiveAwardStartDate.Date.GetDaysInRange(endDate, payDay); //calculate daily benefit rate if benefit amount is provided decimal benefitDailyRate = decimal.Zero; if (data.paBenefitAmount > decimal.Zero) { benefitDailyRate = await data.paBenefitAmount.GetDailyAmount( data.paBenefitStartDate.Date, paBenefitsEndDate.Date); } //step through payWeek var counter = 1; var payLines = new List <ProtectiveAwardPayLine>(); foreach (var payWeekEnd in payDays) { var employmentDays = 0; var maximumDays = 0; var benefitClaimedAmount = 0.00m; var employmentDaysInPrefPeriod = 0; var maximumDaysInPrefPeriod = 0; for (int j = 6; j >= 0; j--) { DateTime day = payWeekEnd.AddDays(-j); if (day >= data.ProtectiveAwardStartDate.Date && day <= protectiveAwardEndDate.Date) { //is this day in shift paattern (a working day)? if (await data.ShiftPattern.ContainsDayWeek(day.DayOfWeek)) { employmentDays++; if (day >= prefPeriodStartDate && day <= data.InsolvencyDate) { employmentDaysInPrefPeriod++; } } maximumDays++; if (day >= prefPeriodStartDate && day <= data.InsolvencyDate) { maximumDaysInPrefPeriod++; } } if (day >= data.paBenefitStartDate.Date && day <= paBenefitsEndDate.Date) { benefitClaimedAmount += benefitDailyRate; } } // calculate Employer Liability for week decimal empLiability = data.WeeklyWage / data.ShiftPattern.Count * employmentDays; decimal employerEntitlementInPrefPeriod = data.WeeklyWage / data.ShiftPattern.Count * employmentDaysInPrefPeriod; //get Maximum Liability decimal maxLiability = statutoryMax; decimal maximumEntitlementInPrefPeriod = statutoryMax / 7 * maximumDaysInPrefPeriod; //if first week if (payWeekEnd == payDays.First()) { //if there is only one week OR claimant did start employment in the first week of claim if ((payDays.Count() == 1) || data.EmploymentStartDate.Date >= payDays.First().AddDays(-7)) { maxLiability = statutoryMax / 7M * maximumDays; } } // if it is the last week else if (payWeekEnd == payDays.Last()) { if (payWeekEnd > protectiveAwardEndDate) { maxLiability = statutoryMax / 7 * maximumDays; } } decimal grossEntitlement = Math.Min(empLiability, maxLiability) - benefitClaimedAmount; grossEntitlement = Math.Max(grossEntitlement, 0m); decimal taxDeducted = Math.Round(await grossEntitlement.GetTaxDeducted(taxRate, isTaxable), 2); decimal niDeducted = Math.Round(await grossEntitlement.GetNIDeducted(niThreshold, niRate, true), 2); grossEntitlement = Math.Round(grossEntitlement, 2); decimal netEntitlement = grossEntitlement - taxDeducted - niDeducted; payLines.Add(new ProtectiveAwardPayLine() { WeekNumber = counter++, PayDate = payWeekEnd, BenefitsClaimed = Math.Round(benefitClaimedAmount, 2), GrossEntitlement = grossEntitlement, TaxDeducted = taxDeducted, NIDeducted = niDeducted, NetEntitlement = netEntitlement, IsSelected = false, MaximumEntitlementIn4MonthPeriod = Math.Round(maximumEntitlementInPrefPeriod, 2), EmployerEntitlementIn4MonthPeriod = Math.Round(employerEntitlementInPrefPeriod, 2), GrossEntitlementIn4Months = Math.Round(Math.Min(maximumEntitlementInPrefPeriod, employerEntitlementInPrefPeriod), 2) }); } ; var result = new ProtectiveAwardResponseDTO() { IsTaxable = isTaxable, StatutoryMax = statutoryMax, PayLines = payLines }; return(await Task.FromResult(result)); }
public async Task <CompensatoryNoticePayCalculationResponseDTO> PerformCompensatoryNoticePayCalculationAsync( CompensatoryNoticePayCalculationRequestModel request, IOptions <ConfigLookupRoot> options) { //set notice start date var noticeStartDate = request.DateNoticeGiven.Date.AddDays(1); //calculate days notice worked var dismissalDate = request.DismissalDate.Date; var insolvencyDate = request.InsolvencyDate.Date; //set paydays_day.number means the integer index of the day of the week 0 = Sunday var payDay = noticeStartDate.AddDays(-1).DayOfWeek; //calculate years of service between employment start date and dismissal date int yearsOfService = await request.InsolvencyEmploymentStartDate.Date.GetServiceYearsAsync(dismissalDate); yearsOfService = Math.Max(yearsOfService, 1); yearsOfService = Math.Min(yearsOfService, 12); var statutaryMax = ConfigValueLookupHelper.GetStatutoryMax(options, dismissalDate); var taxRate = ConfigValueLookupHelper.GetTaxRate(options, dismissalDate); var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, DateTime.Now); var niRate = ConfigValueLookupHelper.GetNIRate(options, DateTime.Now); var waitingDays = ConfigValueLookupHelper.GetBenefitsWaitingDays(options, dismissalDate); var notionalBenefitWeeklyRate = ConfigValueLookupHelper.GetNotionalBenefitsWeeklyRate(options, dismissalDate, await request.DateOfBirth.Date.GetAge(dismissalDate)); decimal notionalBenefitDailyRate = notionalBenefitWeeklyRate / 7; var projectedNoticeDate = noticeStartDate.AddDays(yearsOfService * 7 - 1); //calculate daily rates decimal dailyPayRate = request.WeeklyWage / request.ShiftPattern.Count; decimal dailyStatutoryMaximum = statutaryMax / 7m; // set claim boudaries var claimStartDate = dismissalDate.AddDays(1); var claimEndDate = projectedNoticeDate; if (request.DeceasedDate.HasValue && request.DeceasedDate.Value.Date < claimEndDate) { claimEndDate = request.DeceasedDate.Value.Date; } var adjClaimEndDate = claimEndDate; if (adjClaimEndDate.DayOfWeek != payDay) { adjClaimEndDate = adjClaimEndDate.AddDays(7); } int daysInClaim = await claimStartDate.GetNumBusinessDaysInRange(claimEndDate, request.ShiftPattern); // find paydays in claim period var payDays = await claimStartDate.GetDaysInRange(adjClaimEndDate, payDay); var weekIndex = 1; var notionalDaysCount = 0; List <CompensatoryNoticePayResult> weeklyResults = new List <CompensatoryNoticePayResult>(); foreach (var payWeekEnd in payDays) { var totalBenefitAmount = 0m; var totalNewEmploymentAmount = 0m; var totalWageIncreaseAmount = 0m; var notionalBenefitAmount = 0m; var maximumDays = 0; var employerDays = 0; // step through days in each pay week for (int j = 6; j >= 0; j--) { // set date to be checked to consecxutive days in pay week var claimDay = payWeekEnd.AddDays(-j); //check if claimday is in the claimperiod if (claimDay >= claimStartDate && claimDay <= claimEndDate) { bool benefitOrIncomeReceived = false; foreach (var benefit in request.Benefits) { var endDate = benefit.BenefitEndDate ?? claimEndDate; if (claimDay >= benefit.BenefitStartDate.Date && claimDay <= endDate) { benefitOrIncomeReceived = true; totalBenefitAmount += await benefit.BenefitAmount.GetDailyAmount(benefit.BenefitStartDate, endDate); } } foreach (var newEmployment in request.NewEmployments) { var endDate = newEmployment.NewEmploymentEndDate ?? claimEndDate; if (claimDay >= newEmployment.NewEmploymentStartDate.Date && claimDay <= endDate) { benefitOrIncomeReceived = true; var newEmploymentEarnings = newEmployment.NewEmploymentWage; if (newEmployment.NewEmploymentWeeklyWage.HasValue) { newEmploymentEarnings = Math.Max(newEmploymentEarnings, await newEmployment.NewEmploymentWeeklyWage.Value.GetEarnings(newEmployment.NewEmploymentStartDate, endDate)); } totalNewEmploymentAmount += await newEmploymentEarnings.GetDailyAmount(newEmployment.NewEmploymentStartDate, endDate); } } foreach (var wageIncrease in request.WageIncreases) { var endDate = wageIncrease.WageIncreaseEndDate ?? claimEndDate; if (claimDay >= wageIncrease.WageIncreaseStartDate.Date && claimDay <= endDate) { benefitOrIncomeReceived = true; totalWageIncreaseAmount += await wageIncrease.WageIncreaseAmount.GetDailyAmount(wageIncrease.WageIncreaseStartDate, endDate); } } foreach (var benefitOverride in request.NotionalBenefitOverrides) { if (claimDay >= benefitOverride.NotionalBenefitOverrideStartDate.Date && claimDay <= benefitOverride.NotionalBenefitOverrideEndDate.Date) { benefitOrIncomeReceived = true; } } if (benefitOrIncomeReceived) { notionalDaysCount = 0; } else { notionalDaysCount++; if (notionalDaysCount > waitingDays) { notionalBenefitAmount += notionalBenefitDailyRate; } } if (await request.ShiftPattern.ContainsDayWeek(claimDay.DayOfWeek)) { employerDays++; } maximumDays++; } } //apply mitigation decimal employerEntitlement = employerDays * dailyPayRate; decimal maximumEntitlement = maximumDays * dailyStatutoryMaximum; decimal mitigation = totalBenefitAmount + totalNewEmploymentAmount + totalWageIncreaseAmount + notionalBenefitAmount; decimal grossEntitlement = Math.Max(employerEntitlement - mitigation, 0); decimal notationalTaxDeducted = 0m; decimal taxDeducted = 0m; decimal niDeducted = 0m; decimal netEntitlement = 0m; bool isTaxable = false; if (dismissalDate <= CutOverDate) { notationalTaxDeducted = Math.Round(await grossEntitlement.GetTaxDeducted(taxRate, request.IsTaxable), 2); grossEntitlement = Math.Round(Math.Min(grossEntitlement - notationalTaxDeducted, maximumEntitlement), 2); netEntitlement = grossEntitlement; } else { isTaxable = true; //Apply StatutoryMaximum grossEntitlement = Math.Min(grossEntitlement, maximumEntitlement); taxDeducted = Math.Round(await grossEntitlement.GetTaxDeducted(taxRate, request.IsTaxable), 2); niDeducted = Math.Round(await grossEntitlement.GetNIDeducted(niThreshold, niRate, request.IsTaxable), 2); grossEntitlement = Math.Round(grossEntitlement, 2); netEntitlement = Math.Min(grossEntitlement - taxDeducted - niDeducted, Math.Round(maximumEntitlement, 2)); } weeklyResults.Add(new CompensatoryNoticePayResult() { WeekNumber = weekIndex, EmployerEntitlement = Math.Round(employerEntitlement, 2), BenefitsDeducted = Math.Round(totalBenefitAmount, 2), NewEmploymentDeducted = Math.Round(totalNewEmploymentAmount, 2), WageIncreaseDeducted = Math.Round(totalWageIncreaseAmount, 2), NotionalBenefitDeducted = Math.Round(notionalBenefitAmount, 2), GrossEntitlement = grossEntitlement, IsTaxable = isTaxable, NotionalTaxDeducted = notationalTaxDeducted, TaxDeducted = taxDeducted, NIDeducted = niDeducted, NetEntitlement = netEntitlement, PreferentialClaim = 0m, NonPreferentialClaim = grossEntitlement }); weekIndex++; } var response = new CompensatoryNoticePayCalculationResponseDTO() { NoticeWeeksDue = payDays.Count, StatutoryMax = Math.Round(statutaryMax, 2), MaxCNPEntitlement = Math.Round(payDays.Count * Math.Min(statutaryMax, request.WeeklyWage), 2), NoticeStartDate = noticeStartDate, ProjectedNoticeDate = projectedNoticeDate, CompensationEndDate = claimEndDate, DaysInClaim = daysInClaim, WeeklyResults = weeklyResults }; return(await Task.FromResult(response)); }
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); }