public async Task GetHolidayYearStart_Returns_AdjustedHolidayYearStart()
        {
            // Arrange
            var model = new HolidayPayAccruedCalculationRequestModel()
            {
                EmpStartDate     = new DateTime(2017, 08, 22),
                HolidayYearStart = new DateTime(2018, 01, 01)
            };

            var expectedAdjEmpStartDate = new DateTime(2018, 01, 01);

            // Act
            var result = await model.GetHolidayYearStart();

            // Assert
            result.Should().Be(expectedAdjEmpStartDate);
        }
        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));
        }