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 PostAsync_FailsWithBadRequest_WhenThereIsAAPValidationError(ArrearsOfPayCalculationRequestModel apRequest, string expectedErrorMessage) { //Arrange var request = new APPACalculationRequestModel() { Ap = new List <ArrearsOfPayCalculationRequestModel>() { apRequest } }; var controller = new APPAController(_service.Object, _mockLogger.Object, _confOptions); //Act var result = await controller.PostAsync(request); //Assert var badRequestObjectRequest = result.Should().BeOfType <BadRequestObjectResult>().Subject; badRequestObjectRequest.StatusCode.Should().Be((int)System.Net.HttpStatusCode.BadRequest); _mockLogger.Verify(x => x.Log( LogLevel.Error, It.IsAny <EventId>(), It.Is <object>(v => v.ToString().Contains(expectedErrorMessage)), null, It.IsAny <Func <object, Exception, string> >() )); }