public async Task PerformCalculationAsync_SelectRp1WhenNoRp14()
        {
            // Arrange
            var shiftPattern = new List <string> {
                "1", "2", "3", "4", "5"
            };

            var apRequests = new List <ArrearsOfPayCalculationRequestModel>()
            {
                new ArrearsOfPayCalculationRequestModel()
                {
                    InputSource         = InputSource.Rp1,
                    InsolvencyDate      = new DateTime(2018, 10, 20),
                    EmploymentStartDate = new DateTime(2016, 04, 06),
                    DismissalDate       = new DateTime(2018, 10, 20),
                    DateNoticeGiven     = new DateTime(2018, 10, 14),
                    UnpaidPeriodFrom    = new DateTime(2018, 10, 10),
                    UnpaidPeriodTo      = new DateTime(2018, 10, 18),
                    ApClaimAmount       = 600M,
                    IsTaxable           = true,
                    PayDay       = 6,
                    ShiftPattern = shiftPattern,
                    WeeklyWage   = 400m
                }
            };

            var apResponseRP1 = new ArrearsOfPayResponseDTO()
            {
                StatutoryMax = 508m,
                InputSource  = InputSource.Rp1,
                DngApplied   = true,
                RunNWNP      = true,
                WeeklyResult = new List <ArrearsOfPayWeeklyResult>()
                {
                    new ArrearsOfPayWeeklyResult(1, new DateTime(2018, 10, 13), 428.57M, 508M, 257.14M, 257.14M, true, 51.43M, 11.42M, 194.29M, 7, 3, 508M, 257.14M, 257.14M),
                    new ArrearsOfPayWeeklyResult(2, new DateTime(2018, 10, 20), 428.57M, 508M, 22.86M, 22.86M, true, 4.57M, 0M, 18.29M, 7, 4, 508M, 22.86M, 22.86M),
                }
            };

            var apResponseRP14a = new ArrearsOfPayResponseDTO()
            {
                StatutoryMax = 508m,
                InputSource  = InputSource.Rp14a,
                DngApplied   = true,
                RunNWNP      = true,
                WeeklyResult = new List <ArrearsOfPayWeeklyResult>()
            };

            var request = new APPACalculationRequestModel()
            {
                Ap = apRequests,
                Pa = null
            };

            _apService.Setup(m => m.PerformCalculationAsync(apRequests, InputSource.Rp1, _options, It.IsAny <TraceInfo>())).ReturnsAsync(apResponseRP1);
            _apService.Setup(m => m.PerformCalculationAsync(apRequests, InputSource.Rp14a, _options, It.IsAny <TraceInfo>())).ReturnsAsync(apResponseRP14a);

            // Act
            var results = await _service.PerformCalculationAsync(request, _options);

            // Assert
            results.Ap.Should().NotBeNull();
            results.Pa.Should().BeNull();

            results.Ap.SelectedInputSource.Should().Be(InputSource.Rp1);
            results.Ap.RP1ResultsList.WeeklyResult.Count.Should().Be(2);
            results.Ap.RP1ResultsList.WeeklyResult.Count(x => x.IsSelected).Should().Be(2);
            results.Ap.RP14aResultsList.WeeklyResult.Count.Should().Be(0);
            results.Ap.RP14aResultsList.WeeklyResult.Count(x => x.IsSelected).Should().Be(0);
        }
        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 PerformCalculationAsync_PerformsAPAndPACalculationsWithRp1Selected()
        {
            // Arrange
            var shiftPattern = new List <string> {
                "1", "2", "3", "4", "5"
            };

            var apRequests = new List <ArrearsOfPayCalculationRequestModel>()
            {
                new ArrearsOfPayCalculationRequestModel()
                {
                    InputSource         = InputSource.Rp1,
                    InsolvencyDate      = new DateTime(2018, 10, 20),
                    EmploymentStartDate = new DateTime(2016, 04, 06),
                    DismissalDate       = new DateTime(2018, 10, 20),
                    DateNoticeGiven     = new DateTime(2018, 10, 6),
                    UnpaidPeriodFrom    = new DateTime(2018, 10, 1),
                    UnpaidPeriodTo      = new DateTime(2018, 10, 9),
                    ApClaimAmount       = 350M,
                    IsTaxable           = true,
                    PayDay       = 6,
                    ShiftPattern = shiftPattern,
                    WeeklyWage   = 200m
                },
                new ArrearsOfPayCalculationRequestModel()
                {
                    InputSource         = InputSource.Rp14a,
                    InsolvencyDate      = new DateTime(2018, 10, 20),
                    EmploymentStartDate = new DateTime(2016, 04, 06),
                    DismissalDate       = new DateTime(2018, 10, 20),
                    DateNoticeGiven     = new DateTime(2018, 10, 14),
                    UnpaidPeriodFrom    = new DateTime(2018, 10, 10),
                    UnpaidPeriodTo      = new DateTime(2018, 10, 18),
                    ApClaimAmount       = 600M,
                    IsTaxable           = true,
                    PayDay       = 6,
                    ShiftPattern = shiftPattern,
                    WeeklyWage   = 400m
                }
            };

            var apResponseRP1 = new ArrearsOfPayResponseDTO()
            {
                StatutoryMax = 508m,
                InputSource  = InputSource.Rp1,
                DngApplied   = true,
                RunNWNP      = false,
                WeeklyResult = new List <ArrearsOfPayWeeklyResult>()
                {
                    new ArrearsOfPayWeeklyResult(1, new DateTime(2018, 10, 6), 250m, 508M, 350, 350M, true, 50M, 30M, 300M, 7, 5, 508M, 300M, 300M),
                    new ArrearsOfPayWeeklyResult(2, new DateTime(2018, 10, 13), 250m, 508M, 20M, 20M, true, 4M, 0M, 16M, 7, 2, 508M, 20M, 20M),
                }
            };

            var apResponseRP14a = new ArrearsOfPayResponseDTO()
            {
                StatutoryMax = 508m,
                InputSource  = InputSource.Rp14a,
                DngApplied   = true,
                RunNWNP      = true,
                WeeklyResult = new List <ArrearsOfPayWeeklyResult>()
                {
                    new ArrearsOfPayWeeklyResult(1, new DateTime(2018, 10, 13), 428.57M, 508M, 257.14M, 357.14M, true, 51.43M, 11.42M, 294.29M, 7, 3, 508M, 357.14M, 357.14M),
                    new ArrearsOfPayWeeklyResult(2, new DateTime(2018, 10, 20), 428.57M, 508M, 22.86M, 22.86M, true, 4.57M, 0M, 28.29M, 7, 4, 508M, 22.86M, 22.86M),
                }
            };

            var paRequest = new ProtectiveAwardCalculationRequestModel()
            {
                InsolvencyDate           = new DateTime(2018, 10, 20),
                EmploymentStartDate      = new DateTime(2016, 4, 6),
                DismissalDate            = new DateTime(2018, 10, 20),
                TribunalAwardDate        = new DateTime(2018, 10, 21),
                ProtectiveAwardStartDate = new DateTime(2018, 10, 22),
                ProtectiveAwardDays      = 90,
                PayDay       = 6,
                WeeklyWage   = 400M,
                ShiftPattern = shiftPattern,
            };

            var paResponse = new ProtectiveAwardResponseDTO()
            {
                IsTaxable    = true,
                StatutoryMax = 508m,
                PayLines     = new List <ProtectiveAwardPayLine>()
                {
                    new ProtectiveAwardPayLine(1, new DateTime(2018, 10, 27), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(2, new DateTime(2018, 11, 3), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(3, new DateTime(2018, 11, 10), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(4, new DateTime(2018, 11, 17), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(5, new DateTime(2018, 11, 24), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(6, new DateTime(2018, 12, 1), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(7, new DateTime(2018, 12, 8), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(8, new DateTime(2018, 12, 15), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(9, new DateTime(2018, 12, 22), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(10, new DateTime(2018, 12, 29), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(11, new DateTime(2019, 1, 5), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(12, new DateTime(2019, 1, 12), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M),
                    new ProtectiveAwardPayLine(13, new DateTime(2019, 1, 12), 0M, 400M, 80M, 28.56M, 291.44M, 0M, 0M, 0M)
                }
            };

            var request = new APPACalculationRequestModel()
            {
                Ap = apRequests,
                Pa = paRequest
            };

            _apService.Setup(m => m.PerformCalculationAsync(apRequests, InputSource.Rp1, _options, It.IsAny <TraceInfo>())).ReturnsAsync(apResponseRP1);
            _apService.Setup(m => m.PerformCalculationAsync(apRequests, InputSource.Rp14a, _options, It.IsAny <TraceInfo>())).ReturnsAsync(apResponseRP14a);
            _paService.Setup(m => m.PerformProtectiveAwardCalculationAsync(paRequest, _options)).ReturnsAsync(paResponse);

            // Act
            var results = await _service.PerformCalculationAsync(request, _options);

            // Assert
            results.Ap.Should().NotBeNull();
            results.Pa.Should().NotBeNull();

            results.Ap.SelectedInputSource.Should().Be(InputSource.Rp1);
            results.Ap.RP1ResultsList.WeeklyResult.Count(x => x.IsSelected).Should().Be(1);
            results.Ap.RP14aResultsList.WeeklyResult.Count(x => x.IsSelected).Should().Be(0);
            results.Pa.PayLines.Count(x => x.IsSelected).Should().Be(7);
        }