public async Task PerformCalculationAsync_WithPeriodOutside4Month()
        {
            var request = new NoticeWorkedNotPaidCalculationRequestModel()
            {
                InputSource         = InputSource.Rp14a,
                EmploymentStartDate = new DateTime(2015, 8, 2),
                InsolvencyDate      = new DateTime(2018, 7, 26),
                DateNoticeGiven     = new DateTime(2018, 7, 20),
                DismissalDate       = new DateTime(2018, 8, 8),
                UnpaidPeriodFrom    = new DateTime(2018, 7, 27),
                UnpaidPeriodTo      = new DateTime(2018, 8, 8),
                WeeklyWage          = 320,
                ShiftPattern        = new List <string> {
                    "1", "2", "3", "4", "5"
                },
                PayDay        = 6,
                IsTaxable     = true,
                ApClaimAmount = 100
            };

            var expectedResults = new NoticeWorkedNotPaidResponseDTO(InputSource.Rp14a, 508, weeklyResult: new List <NoticeWorkedNotPaidWeeklyResult>()
            {
                new NoticeWorkedNotPaidWeeklyResult()
                {
                    WeekNumber          = 1,
                    PayDate             = new DateTime(2018, 8, 03),
                    MaximumEntitlement  = 508m,
                    EmployerEntitlement = 55.56M,
                    GrossEntitlement    = 55.56M,
                    IsTaxable           = true,
                    TaxDeducted         = 11.11M,
                    NiDeducted          = 0m,
                    NetEntitlement      = 44.45M,
                    MaximumDays         = 7,
                    EmploymentDays      = 5,
                    MaximumEntitlementIn4MonthPeriod  = 0M,
                    EmployerEntitlementIn4MonthPeriod = 0M,
                    GrossEntitlementIn4Months         = 0m
                },
                new NoticeWorkedNotPaidWeeklyResult()
                {
                    WeekNumber          = 2,
                    PayDate             = new DateTime(2018, 8, 10),
                    MaximumEntitlement  = 362.86m,
                    EmployerEntitlement = 33.33M,
                    GrossEntitlement    = 33.33M,
                    IsTaxable           = true,
                    TaxDeducted         = 6.67M,
                    NiDeducted          = 0m,
                    NetEntitlement      = 26.66M,
                    MaximumDays         = 5,
                    EmploymentDays      = 3,
                    MaximumEntitlementIn4MonthPeriod  = 0M,
                    EmployerEntitlementIn4MonthPeriod = 0m,
                    GrossEntitlementIn4Months         = 0m
                }
            });

            await TestCalculation(request, expectedResults);
        }
        private async Task TestCalculation(
            NoticeWorkedNotPaidCalculationRequestModel request, NoticeWorkedNotPaidResponseDTO expectedResult)
        {
            //Act
            var actualResult = await _noticeWorkedNotPaidCalculationService.PerformNwnpCalculationAsync(request, _options);

            //Assert
            actualResult.InputSource.Should().Be(expectedResult.InputSource);
            actualResult.StatutoryMax.Should().Be(expectedResult.StatutoryMax);

            actualResult.WeeklyResult.Count.Should().Be(expectedResult.WeeklyResult.Count);
            for (var index = 0; index < expectedResult.WeeklyResult.Count; index++)
            {
                actualResult.WeeklyResult[index].WeekNumber.Should().Be(expectedResult.WeeklyResult[index].WeekNumber);
                actualResult.WeeklyResult[index].PayDate.Should().Be(expectedResult.WeeklyResult[index].PayDate);
                actualResult.WeeklyResult[index].MaximumEntitlement.Should().Be(expectedResult.WeeklyResult[index].MaximumEntitlement);
                actualResult.WeeklyResult[index].EmployerEntitlement.Should().Be(expectedResult.WeeklyResult[index].EmployerEntitlement);
                actualResult.WeeklyResult[index].GrossEntitlement.Should().Be(expectedResult.WeeklyResult[index].GrossEntitlement);
                actualResult.WeeklyResult[index].IsTaxable.Should().Be(expectedResult.WeeklyResult[index].IsTaxable);
                actualResult.WeeklyResult[index].TaxDeducted.Should().Be(expectedResult.WeeklyResult[index].TaxDeducted);
                actualResult.WeeklyResult[index].NiDeducted.Should().Be(expectedResult.WeeklyResult[index].NiDeducted);
                actualResult.WeeklyResult[index].NetEntitlement.Should().Be(expectedResult.WeeklyResult[index].NetEntitlement);
                actualResult.WeeklyResult[index].MaximumDays.Should().Be(expectedResult.WeeklyResult[index].MaximumDays);
                actualResult.WeeklyResult[index].EmploymentDays.Should().Be(expectedResult.WeeklyResult[index].EmploymentDays);
                actualResult.WeeklyResult[index].MaximumEntitlementIn4MonthPeriod.Should().Be(expectedResult.WeeklyResult[index].MaximumEntitlementIn4MonthPeriod);
                actualResult.WeeklyResult[index].EmployerEntitlementIn4MonthPeriod.Should().Be(expectedResult.WeeklyResult[index].EmployerEntitlementIn4MonthPeriod);
                actualResult.WeeklyResult[index].GrossEntitlementIn4Months.Should().Be(expectedResult.WeeklyResult[index].GrossEntitlementIn4Months);
            }
        }
        public async Task PerformCalculationAsync_WhenNoticeGivenDuringArrearsOfPay_AndLessThatOneYearsService()
        {
            var request = new NoticeWorkedNotPaidCalculationRequestModel()
            {
                InputSource         = InputSource.Rp14a,
                EmploymentStartDate = new DateTime(2018, 9, 3),
                InsolvencyDate      = new DateTime(2019, 4, 3),
                DateNoticeGiven     = new DateTime(2019, 3, 12),
                DismissalDate       = new DateTime(2019, 3, 28),
                UnpaidPeriodFrom    = new DateTime(2019, 3, 1),
                UnpaidPeriodTo      = new DateTime(2019, 3, 31),
                WeeklyWage          = 302.58m,
                ShiftPattern        = new List <string> {
                    "0", "1", "2", "4", "6"
                },
                PayDay        = 6,
                IsTaxable     = true,
                ApClaimAmount = 1452.38M
            };

            var expectedResults = new NoticeWorkedNotPaidResponseDTO(InputSource.Rp14a, 508, weeklyResult: new List <NoticeWorkedNotPaidWeeklyResult>()
            {
                new NoticeWorkedNotPaidWeeklyResult()
                {
                    WeekNumber          = 1,
                    PayDate             = new DateTime(2019, 3, 19),
                    MaximumEntitlement  = 508m,
                    EmployerEntitlement = 330.09m,
                    GrossEntitlement    = 330.09m,
                    IsTaxable           = true,
                    TaxDeducted         = 66.02m,
                    NiDeducted          = 17.65m,
                    NetEntitlement      = 246.42m,
                    MaximumDays         = 7,
                    EmploymentDays      = 5,
                    MaximumEntitlementIn4MonthPeriod  = 508m,
                    EmployerEntitlementIn4MonthPeriod = 330.09m,
                    GrossEntitlementIn4Months         = 330.09m
                }
            });

            await TestCalculation(request, expectedResults);
        }
Esempio n. 4
0
        public async Task PostAsync_ReturnsBadRequest_When_RequestData_HaveInvalidNWNPData(NoticeWorkedNotPaidCalculationRequestModel request, string expectedErrorMessage)
        {
            //Arrange
            var controller = new NoticeController(_service.Object, _mockLogger.Object, _confOptions);
            var data       = new NoticePayCompositeCalculationRequestModel()
            {
                Cnp  = null,
                Nwnp = new List <NoticeWorkedNotPaidCalculationRequestModel>()
                {
                    request
                }
            };

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