Пример #1
0
        public async Task PerformCalculationAsync_GrossEntitlementDoesNotExceedStatMax()
        {
            var request = new CompensatoryNoticePayCalculationRequestModel
            {
                InsolvencyEmploymentStartDate = new DateTime(2014, 10, 30),
                InsolvencyDate  = new DateTime(2016, 10, 30),
                DismissalDate   = new DateTime(2016, 10, 30),
                DateNoticeGiven = new DateTime(2016, 10, 30),
                WeeklyWage      = 650m,
                ShiftPattern    = new List <string> {
                    "1", "2", "3", "4", "5"
                },
                IsTaxable    = true,
                DateOfBirth  = new DateTime(1976, 10, 30),
                DeceasedDate = null
            };

            var response = await _service.PerformCompensatoryNoticePayCalculationAsync(request, _options);

            response.CompensationEndDate.Should().Be(new DateTime(2016, 11, 13));
            response.DaysInClaim.Should().Be(10);
            response.NoticeStartDate.Should().Be(new DateTime(2016, 10, 31));
            response.NoticeWeeksDue.Should().Be(2);
            response.MaxCNPEntitlement.Should().Be(958m);
            response.StatutoryMax.Should().Be(479m);
            response.ProjectedNoticeDate.Should().Be(new DateTime(2016, 11, 13));
            response.WeeklyResults.Count.Should().Be(2);

            response.WeeklyResults[0].BenefitsDeducted.Should().Be(0);
            response.WeeklyResults[0].EmployerEntitlement.Should().Be(650m);
            response.WeeklyResults[0].GrossEntitlement.Should().Be(479m);
            response.WeeklyResults[0].IsTaxable.Should().BeFalse();
            response.WeeklyResults[0].NIDeducted.Should().Be(0m);
            response.WeeklyResults[0].NetEntitlement.Should().Be(479m);
            response.WeeklyResults[0].NewEmploymentDeducted.Should().Be(0m);
            response.WeeklyResults[0].NotionalBenefitDeducted.Should().Be(0m);
            response.WeeklyResults[0].NotionalTaxDeducted.Should().Be(130m);
            response.WeeklyResults[0].TaxDeducted.Should().Be(0m);
            response.WeeklyResults[0].WageIncreaseDeducted.Should().Be(0m);
            response.WeeklyResults[0].PreferentialClaim.Should().Be(0m);
            response.WeeklyResults[0].NonPreferentialClaim.Should().Be(response.WeeklyResults[0].NetEntitlement);
            response.WeeklyResults[0].WeekNumber.Should().Be(1);

            response.WeeklyResults[1].BenefitsDeducted.Should().Be(0m);
            response.WeeklyResults[1].EmployerEntitlement.Should().Be(650m);
            response.WeeklyResults[1].GrossEntitlement.Should().Be(461.52m);
            response.WeeklyResults[1].NIDeducted.Should().Be(0m);
            response.WeeklyResults[1].NetEntitlement.Should().Be(461.52m);
            response.WeeklyResults[1].NewEmploymentDeducted.Should().Be(0m);
            response.WeeklyResults[1].NotionalBenefitDeducted.Should().Be(73.10m);
            response.WeeklyResults[1].NotionalTaxDeducted.Should().Be(115.38m);
            response.WeeklyResults[1].TaxDeducted.Should().Be(0m);
            response.WeeklyResults[1].WageIncreaseDeducted.Should().Be(0m);
            response.WeeklyResults[1].PreferentialClaim.Should().Be(0m);
            response.WeeklyResults[1].NonPreferentialClaim.Should().Be(response.WeeklyResults[1].NetEntitlement);
            response.WeeklyResults[1].WeekNumber.Should().Be(2);
        }
        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));
        }
Пример #3
0
        public async Task PerformCalculationAsync_ForClaimsPostApril2018()
        {
            var request = new CompensatoryNoticePayCalculationRequestModel
            {
                InsolvencyEmploymentStartDate = new DateTime(2016, 01, 01),
                InsolvencyDate  = new DateTime(2018, 12, 3),
                DismissalDate   = new DateTime(2019, 1, 1),
                DateNoticeGiven = new DateTime(2019, 1, 1),
                WeeklyWage      = 961.54m,
                ShiftPattern    = new List <string> {
                    "1", "2", "3", "4", "5"
                },
                IsTaxable    = true,
                DateOfBirth  = new DateTime(1969, 1, 1),
                DeceasedDate = null
            };

            var response = await _service.PerformCompensatoryNoticePayCalculationAsync(request, _options);

            response.CompensationEndDate.Should().Be(new DateTime(2019, 1, 22));
            response.DaysInClaim.Should().Be(15);
            response.NoticeStartDate.Should().Be(new DateTime(2019, 1, 2));
            response.NoticeWeeksDue.Should().Be(3);
            response.MaxCNPEntitlement.Should().Be(1524m);
            response.StatutoryMax.Should().Be(508m);
            response.ProjectedNoticeDate.Should().Be(new DateTime(2019, 1, 22));
            response.WeeklyResults.Count.Should().Be(3);

            response.WeeklyResults[0].BenefitsDeducted.Should().Be(0m);
            response.WeeklyResults[0].EmployerEntitlement.Should().Be(961.54m);
            response.WeeklyResults[0].GrossEntitlement.Should().Be(508m);
            response.WeeklyResults[0].IsTaxable.Should().BeTrue();
            response.WeeklyResults[0].NIDeducted.Should().Be(39.00m);
            response.WeeklyResults[0].NetEntitlement.Should().Be(367.40m);
            response.WeeklyResults[0].NewEmploymentDeducted.Should().Be(0m);
            response.WeeklyResults[0].NotionalBenefitDeducted.Should().Be(0m);
            response.WeeklyResults[0].NotionalTaxDeducted.Should().Be(0m);
            response.WeeklyResults[0].TaxDeducted.Should().Be(101.60m);
            response.WeeklyResults[0].WageIncreaseDeducted.Should().Be(0m);
            response.WeeklyResults[0].PreferentialClaim.Should().Be(0m);
            response.WeeklyResults[0].NonPreferentialClaim.Should().Be(response.WeeklyResults[0].GrossEntitlement);
            response.WeeklyResults[0].WeekNumber.Should().Be(1);

            response.WeeklyResults[1].BenefitsDeducted.Should().Be(0m);
            response.WeeklyResults[1].EmployerEntitlement.Should().Be(961.54m);
            response.WeeklyResults[1].GrossEntitlement.Should().Be(508m);
            response.WeeklyResults[1].IsTaxable.Should().BeTrue();
            response.WeeklyResults[1].NIDeducted.Should().Be(39.00m);
            response.WeeklyResults[1].NetEntitlement.Should().Be(367.40m);
            response.WeeklyResults[1].NewEmploymentDeducted.Should().Be(0m);
            response.WeeklyResults[1].NotionalBenefitDeducted.Should().Be(73.10m);
            response.WeeklyResults[1].NotionalTaxDeducted.Should().Be(0m);
            response.WeeklyResults[1].TaxDeducted.Should().Be(101.60m);
            response.WeeklyResults[1].WageIncreaseDeducted.Should().Be(0m);
            response.WeeklyResults[1].PreferentialClaim.Should().Be(0m);
            response.WeeklyResults[1].NonPreferentialClaim.Should().Be(response.WeeklyResults[1].GrossEntitlement);
            response.WeeklyResults[1].WeekNumber.Should().Be(2);

            response.WeeklyResults[2].BenefitsDeducted.Should().Be(0m);
            response.WeeklyResults[2].EmployerEntitlement.Should().Be(961.54m);
            response.WeeklyResults[2].GrossEntitlement.Should().Be(508m);
            response.WeeklyResults[2].IsTaxable.Should().BeTrue();
            response.WeeklyResults[2].NIDeducted.Should().Be(39.00m);
            response.WeeklyResults[2].NetEntitlement.Should().Be(367.40m);
            response.WeeklyResults[2].NewEmploymentDeducted.Should().Be(0m);
            response.WeeklyResults[2].NotionalBenefitDeducted.Should().Be(73.10m);
            response.WeeklyResults[2].NotionalTaxDeducted.Should().Be(0m);
            response.WeeklyResults[2].TaxDeducted.Should().Be(101.60m);
            response.WeeklyResults[2].WageIncreaseDeducted.Should().Be(0m);
            response.WeeklyResults[2].PreferentialClaim.Should().Be(0m);
            response.WeeklyResults[2].NonPreferentialClaim.Should().Be(response.WeeklyResults[2].GrossEntitlement);
            response.WeeklyResults[2].WeekNumber.Should().Be(3);
        }
Пример #4
0
        public async Task PerformCalculationAsync_ForWageIncreaseWithNoEndDate()
        {
            var request = new CompensatoryNoticePayCalculationRequestModel
            {
                InsolvencyEmploymentStartDate = new DateTime(2016, 02, 01),
                InsolvencyDate  = new DateTime(2018, 6, 1),
                DismissalDate   = new DateTime(2018, 06, 05),
                DateNoticeGiven = new DateTime(2018, 06, 01),
                WeeklyWage      = 330.25m,
                ShiftPattern    = new List <string> {
                    "1", "2", "3", "4", "5"
                },
                IsTaxable     = true,
                DateOfBirth   = new DateTime(1990, 1, 1),
                DeceasedDate  = null,
                WageIncreases = new List <CompensatoryNoticePayWageIncrease>()
                {
                    new CompensatoryNoticePayWageIncrease()
                    {
                        WageIncreaseStartDate = new DateTime(2018, 06, 05),
                        WageIncreaseAmount    = 220
                    }
                }
            };

            var response = await _service.PerformCompensatoryNoticePayCalculationAsync(request, _options);

            response.CompensationEndDate.Should().Be(new DateTime(2018, 6, 15));
            response.DaysInClaim.Should().Be(8);
            response.NoticeStartDate.Should().Be(new DateTime(2018, 6, 2));
            response.NoticeWeeksDue.Should().Be(2);
            response.MaxCNPEntitlement.Should().Be(660.5m);
            response.StatutoryMax.Should().Be(508m);
            response.ProjectedNoticeDate.Should().Be(new DateTime(2018, 6, 15));
            response.WeeklyResults.Count.Should().Be(2);

            response.WeeklyResults[0].BenefitsDeducted.Should().Be(0m);
            response.WeeklyResults[0].EmployerEntitlement.Should().Be(198.15m);
            response.WeeklyResults[0].GrossEntitlement.Should().Be(138.15m);
            response.WeeklyResults[0].IsTaxable.Should().BeTrue();
            response.WeeklyResults[0].NIDeducted.Should().Be(0m);
            response.WeeklyResults[0].NetEntitlement.Should().Be(110.52m);
            response.WeeklyResults[0].NewEmploymentDeducted.Should().Be(0m);
            response.WeeklyResults[0].NotionalBenefitDeducted.Should().Be(0m);
            response.WeeklyResults[0].NotionalTaxDeducted.Should().Be(0m);
            response.WeeklyResults[0].TaxDeducted.Should().Be(27.63m);
            response.WeeklyResults[0].WageIncreaseDeducted.Should().Be(60m);
            response.WeeklyResults[0].PreferentialClaim.Should().Be(0m);
            response.WeeklyResults[0].NonPreferentialClaim.Should().Be(response.WeeklyResults[0].GrossEntitlement);
            response.WeeklyResults[0].WeekNumber.Should().Be(1);

            response.WeeklyResults[1].BenefitsDeducted.Should().Be(0m);
            response.WeeklyResults[1].EmployerEntitlement.Should().Be(330.25m);
            response.WeeklyResults[1].GrossEntitlement.Should().Be(190.25m);
            response.WeeklyResults[1].NIDeducted.Should().Be(0.87m);
            response.WeeklyResults[1].NetEntitlement.Should().Be(151.33m);
            response.WeeklyResults[1].NewEmploymentDeducted.Should().Be(0m);
            response.WeeklyResults[1].NotionalBenefitDeducted.Should().Be(0m);
            response.WeeklyResults[1].NotionalTaxDeducted.Should().Be(0m);
            response.WeeklyResults[1].TaxDeducted.Should().Be(38.05m);
            response.WeeklyResults[1].WageIncreaseDeducted.Should().Be(140m);
            response.WeeklyResults[1].PreferentialClaim.Should().Be(0m);
            response.WeeklyResults[1].NonPreferentialClaim.Should().Be(response.WeeklyResults[1].GrossEntitlement);
            response.WeeklyResults[1].WeekNumber.Should().Be(2);
        }
Пример #5
0
        public async Task PostAsync_ReturnsBadRequest_When_RequestData_HaveInvalidCNPData(CompensatoryNoticePayCalculationRequestModel request, string expectedErrorMessage)
        {
            //Arrange
            var controller = new NoticeController(_service.Object, _mockLogger.Object, _confOptions);
            var data       = new NoticePayCompositeCalculationRequestModel()
            {
                Cnp  = request,
                Nwnp = null
            };

            //Act

            var result = await controller.PostAsync(data);

            //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> >()
                                   ));
        }