示例#1
0
        public async Task <BasicAwardCalculationResponseDTO> PerformBasicAwardCalculationAsync(
            BasicAwardCalculationRequestModel data,
            IOptions <ConfigLookupRoot> options)
        {
            DateTime date = DateTime.Today;

            var taxRate     = ConfigValueLookupHelper.GetTaxRate(options, date);
            var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, date);

            var     niUpperThreshold = ConfigValueLookupHelper.GetNIUpperThreshold(options, date);
            var     niRate           = ConfigValueLookupHelper.GetNIRate(options, date);
            var     niUpperRate      = ConfigValueLookupHelper.GetNIUpperRate(options, date);
            decimal taxDeducted      = Math.Round(await data.BasicAwardAmount.GetTaxDeducted(taxRate, data.IsTaxable), 2);
            decimal niDeducted       = Math.Round(await data.BasicAwardAmount.GetNIDeducted(niThreshold, niUpperThreshold, niRate, niUpperRate, data.IsTaxable), 2);

            var response = new BasicAwardCalculationResponseDTO()
            {
                GrossEntitlement     = Math.Round(data.BasicAwardAmount, 2),
                IsTaxable            = data.IsTaxable,
                TaxDeducted          = taxDeducted,
                NIDeducted           = niDeducted,
                NetEntitlement       = Math.Round(data.BasicAwardAmount, 2) - taxDeducted - niDeducted,
                PreferentialClaim    = 0m,
                NonPreferentialClaim = Math.Round(data.BasicAwardAmount, 2)
            };

            return(await Task.FromResult(response));
        }
        public void GetBenefitsWaitingDays_ThrowsException_WhenNoConfigForDate()
        {
            var date = new DateTime(2010, 1, 1);

            //Act
            Exception ex = Assert.Throws <MissingConfigurationException>(() => ConfigValueLookupHelper.GetBenefitsWaitingDays(_configOptions, date));
        }
        public void GetStatutaryMax_ThrowsException_WhenNoConfigForDate()
        {
            var date = new DateTime(2010, 1, 1);

            //Act
            Exception ex = Assert.Throws <MissingConfigurationException>(() => ConfigValueLookupHelper.GetStatutoryMax(_configOptions, date));
        }
示例#4
0
        public async Task <ApportionmentCalculationResponseDTO> PerformApportionmentCalculationAsync(
            ApportionmentCalculationRequestModel data, IOptions <ConfigLookupRoot> options)
        {
            decimal preferentialClaim       = 0.0m;
            decimal preferentialLimit       = ConfigValueLookupHelper.GetPreferentialLimit(options, DateTime.Now);
            decimal apportionmentPercentage = 1.0m;

            if (data.TupeStatus)
            {
                preferentialClaim = Math.Min(data.GrossPaidInFourMonth, preferentialLimit);
            }
            if (data.TupeStatus == false && data.TotalClaimedInFourMonth > 0.0m)
            {
                apportionmentPercentage = (data.TotalClaimedInFourMonth <= preferentialLimit) ? 1m :
                                          data.GrossPaidInFourMonth / data.TotalClaimedInFourMonth;

                preferentialClaim = (data.TotalClaimedInFourMonth > preferentialLimit
                    ? preferentialLimit * apportionmentPercentage
                    : data.GrossPaidInFourMonth * apportionmentPercentage);
            }
            apportionmentPercentage = Math.Round(apportionmentPercentage * 100, 4);
            var result = new ApportionmentCalculationResponseDTO()
            {
                ApportionmentPercentage = apportionmentPercentage,
                PrefClaim    = Math.Round(preferentialClaim, 2),
                NonPrefClaim = Math.Round((data.GrossEntitlement - preferentialClaim), 2),
                TupeStatus   = data.TupeStatus
            };

            return(await Task.FromResult(result));
        }
        public void GetTaxRate_ThrowsException_WhenNoConfigForDate()
        {
            var date = new DateTime(1899, 1, 1);

            //Act
            Exception ex = Assert.Throws <MissingConfigurationException>(() => ConfigValueLookupHelper.GetTaxRate(_configOptions, date));
        }
        public void GetNotionalBenefitsWeeklyRate_ThrowsException_WhenNoConfigForDate()
        {
            var date = new DateTime(1899, 1, 1);
            var age  = 26;

            //Act
            Exception ex = Assert.Throws <MissingConfigurationException>(() => ConfigValueLookupHelper.GetNotionalBenefitsWeeklyRate(_configOptions, date, age));
        }
        public void GetStatutaryMax_Returns_CorrectStatMax_ForGiven_InsolvencyDate(DateTime insolvencyDate,
                                                                                   decimal expectedStatutoryMax)
        {
            //Arrange
            //Act
            var result = ConfigValueLookupHelper.GetStatutoryMax(_configOptions, insolvencyDate);

            //Assert
            result.Should().Be(expectedStatutoryMax);
        }
        public void GetPreferentialLimit_Returns_CorrectPreferentialLimit_ForGiven_date(
            DateTime someDate, decimal expectedPreferntialLimit)
        {
            //Arrange
            //Act
            var result = ConfigValueLookupHelper.GetPreferentialLimit(_configOptions, someDate);

            //Assert
            result.Should().Be(expectedPreferntialLimit);
        }
        public void GetBenefitsWaitingDays_Returns_CorrectBenefitsWaitingDays_ForGiven_benefitsStartDate(
            DateTime benefitsStartDate, decimal expectedBenefitsWaitingDays)
        {
            //Arrange
            //Act
            var result = ConfigValueLookupHelper.GetBenefitsWaitingDays(_configOptions, benefitsStartDate);

            //Assert
            result.Should().Be(expectedBenefitsWaitingDays);
        }
        public void GetNIRate_Returns_CorrectNIRate_ForGiven_date(
            DateTime someDate, decimal expectedNIRate)
        {
            //Arrange
            //Act
            var result = ConfigValueLookupHelper.GetNIRate(_configOptions, someDate);

            //Assert
            result.Should().Be(expectedNIRate);
        }
        public void GetNotionalBenefitsWeeklyRate_Returns_CorrectNotionalBenefitsWeeklyRate_ForGivenDateAndAge(
            DateTime someDate, int age, decimal expectedWeeklyRate)
        {
            //Arrange
            //Act
            var result = ConfigValueLookupHelper.GetNotionalBenefitsWeeklyRate(_configOptions, someDate, age);

            //Assert
            result.Should().Be(expectedWeeklyRate);
        }
示例#12
0
        public static async Task <NoticeWorkedNotPaidResponseDTO> MergePayWeeks(this NoticeWorkedNotPaidResponseDTO nwnpList, IOptions <ConfigLookupRoot> options)
        {
            var weeksToMerge = nwnpList.WeeklyResult.GroupBy(x => x.PayDate).Where(x => x.Count() > 1).ToList();

            foreach (var week in weeksToMerge)
            {
                var payDate = week.Key;

                var mergedWeek = new NoticeWorkedNotPaidWeeklyResult();

                mergedWeek.PayDate = week.Select(x => x.PayDate).FirstOrDefault();

                mergedWeek.MaximumDays         = week.Max(x => x.MaximumDays);
                mergedWeek.EmploymentDays      = week.Sum(x => x.EmploymentDays);
                mergedWeek.MaximumEntitlement  = week.Max(x => x.MaximumEntitlement);
                mergedWeek.EmployerEntitlement = week.Sum(x => x.EmployerEntitlement);
                mergedWeek.GrossEntitlement    = Math.Min(mergedWeek.MaximumEntitlement, mergedWeek.EmployerEntitlement);

                mergedWeek.IsTaxable = week.Select(x => x.IsTaxable).FirstOrDefault();
                var taxRate = ConfigValueLookupHelper.GetTaxRate(options, DateTime.Now);
                mergedWeek.TaxDeducted = Math.Round(await mergedWeek.GrossEntitlement.GetTaxDeducted(taxRate, mergedWeek.IsTaxable), 2);
                var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, DateTime.Now);
                var niRate      = ConfigValueLookupHelper.GetNIRate(options, DateTime.Now);
                mergedWeek.NiDeducted = Math.Round(await mergedWeek.GrossEntitlement.GetNIDeducted(niThreshold, niRate, mergedWeek.IsTaxable), 2);

                mergedWeek.NetEntitlement = await mergedWeek.GrossEntitlement.GetNetLiability(mergedWeek.TaxDeducted, mergedWeek.NiDeducted);

                mergedWeek.MaximumEntitlementIn4MonthPeriod  = week.Max(x => x.MaximumEntitlementIn4MonthPeriod);
                mergedWeek.EmployerEntitlementIn4MonthPeriod = week.Sum(x => x.EmployerEntitlementIn4MonthPeriod);
                mergedWeek.GrossEntitlementIn4Months         = Math.Min(mergedWeek.MaximumEntitlementIn4MonthPeriod, mergedWeek.EmployerEntitlementIn4MonthPeriod);

                mergedWeek.IsSelected = false;
                //remove the weeks with the same payDate from the selected list.
                nwnpList.WeeklyResult.RemoveAll(x => x.PayDate == payDate);
                //add the merged week for that paydate
                nwnpList.WeeklyResult.Add(mergedWeek);
            }

            //update weeknumber on the merged list
            var i = 1;

            nwnpList.WeeklyResult = nwnpList.WeeklyResult.OrderBy(x => x.PayDate).ToList();
            nwnpList.WeeklyResult.ForEach(x => x.WeekNumber = i++);



            return(nwnpList);
        }
        public async Task ReturnsCorrectPrefAndNonPrefClaimValues_WhenTupeAndExceedsLimit()
        {
            // Arrange
            var request = new ApportionmentCalculationRequestModel()
            {
                GrossPaidInFourMonth    = 898.00m,
                GrossEntitlement        = 1002.24m,
                TotalClaimedInFourMonth = 4698.00m,
                TupeStatus = true
            };

            // Act
            var result = await _service.PerformApportionmentCalculationAsync(request, _options);

            // Assert
            Math.Round(result.PrefClaim, 2).Should()
            .Be(ConfigValueLookupHelper.GetPreferentialLimit(_options, DateTime.Now));
            Math.Round(result.NonPrefClaim, 2).Should()
            .Be(Math.Round(request.GrossEntitlement, 2) - Math.Round(result.PrefClaim, 2));
            result.TupeStatus.Should().Be(request.TupeStatus);
            result.ApportionmentPercentage.Should().Be(100.0m);
        }
示例#14
0
        public static async Task <ArrearsOfPayResponseDTO> MergeWeeklyResults(this List <ArrearsOfPayResponseDTO> list, string inputSource, IOptions <ConfigLookupRoot> options)
        {
            var weekList     = list.SelectMany(x => x.WeeklyResult).ToList();
            var weeksToMerge = weekList.GroupBy(x => x.PayDate)
                               .Where(x => x.Count() > 1)
                               .ToList();

            foreach (var week in weeksToMerge)
            {
                var payDate    = week.Key;
                var mergedWeek = new ArrearsOfPayWeeklyResult();

                mergedWeek.PayDate             = week.First().PayDate;
                mergedWeek.ApPayRate           = week.Max(x => x.ApPayRate);
                mergedWeek.MaximumEntitlement  = week.Max(x => x.MaximumEntitlement);
                mergedWeek.EmployerEntitlement = week.Sum(x => x.EmployerEntitlement);
                mergedWeek.MaximumDays         = week.Max(x => x.MaximumDays);
                mergedWeek.EmploymentDays      = week.Sum(x => x.EmploymentDays);
                mergedWeek.MaximumEntitlementIn4MonthPeriod  = week.Max(x => x.MaximumEntitlementIn4MonthPeriod);
                mergedWeek.EmployerEntitlementIn4MonthPeriod = week.Sum(x => x.EmployerEntitlementIn4MonthPeriod);
                mergedWeek.GrossEntitlementIn4Months         = Math.Min(mergedWeek.MaximumEntitlementIn4MonthPeriod, mergedWeek.EmployerEntitlementIn4MonthPeriod);

                mergedWeek.GrossEntitlement = Math.Min(mergedWeek.MaximumEntitlement, mergedWeek.EmployerEntitlement);
                mergedWeek.IsTaxable        = week.Select(x => x.IsTaxable).FirstOrDefault();

                var taxRate = ConfigValueLookupHelper.GetTaxRate(options, DateTime.Now);
                mergedWeek.TaxDeducted = Math.Round(await mergedWeek.GrossEntitlement.GetTaxDeducted(taxRate, mergedWeek.IsTaxable), 2);
                var niThreshold = ConfigValueLookupHelper.GetNIThreshold(options, DateTime.Now);
                var niRate      = ConfigValueLookupHelper.GetNIRate(options, DateTime.Now);
                mergedWeek.NIDeducted     = Math.Round(await mergedWeek.GrossEntitlement.GetNIDeducted(niThreshold, niRate, mergedWeek.IsTaxable), 2);
                mergedWeek.NetEntitlement = await mergedWeek.GrossEntitlement.GetNetLiability(mergedWeek.TaxDeducted, mergedWeek.NIDeducted);

                //remove the weeks with the same payDate from the selected list.
                weekList.RemoveAll(x => x.PayDate == payDate);
                weekList.Add(mergedWeek);
            }

            // update weeknumber on the merged list
            var i = 1;

            weekList = weekList.OrderBy(x => x.PayDate).ToList();
            weekList.ForEach(x => x.WeekNumber = i++);

            if (list.Any())
            {
                return(new ArrearsOfPayResponseDTO()
                {
                    StatutoryMax = list.First().StatutoryMax,
                    InputSource = inputSource,
                    DngApplied = list.Any(x => x.DngApplied),
                    RunNWNP = list.Any(x => x.RunNWNP),
                    WeeklyResult = weekList
                });
            }
            else
            {
                return(new ArrearsOfPayResponseDTO()
                {
                    StatutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, DateTime.Today),
                    InputSource = inputSource,
                    DngApplied = false,
                    RunNWNP = false,
                });
            }
        }
        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 <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));
        }
        public async Task <RedundancyPaymentResponseDto> PerformRedundancyPayCalculationAsync(
            RedundancyPaymentCalculationRequestModel data, IOptions <ConfigLookupRoot> options)
        {
            var calculationResult = new RedundancyPaymentResponseDto();

            var adjStartDate = await data.EmploymentStartDate.GetAdjustedEmploymentStartDate(data.EmploymentBreaks);

            var relevantNoticeDate = await data.DateNoticeGiven.GetRelevantNoticeDate(data.DismissalDate);

            var noticeEntitlementWeeks = await adjStartDate.GetNoticeEntitlementWeeks(relevantNoticeDate);

            var projectedNoticeDate = await relevantNoticeDate.GetProjectedNoticeDate(noticeEntitlementWeeks);

            var relevantDismissalDate = await data.DismissalDate.GetRelevantDismissalDate(projectedNoticeDate);

            var statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, relevantDismissalDate);

            var totalYearsOfService = await adjStartDate.GetServiceYearsAsync(relevantDismissalDate);

            //limit maximum service years to 20
            if (totalYearsOfService > 20)
            {
                totalYearsOfService = 20;
            }

            var appliedRateOfPay = Math.Min(statutoryMax, data.WeeklyWage);

            int yearsOfServiceUpto21 = 0, yearsOfService22To41 = 0;

            var yearsOfServiceOver41 = await data.DateOfBirth.GetYearsOfServiceOver41(adjStartDate, relevantDismissalDate);

            if (yearsOfServiceOver41 < totalYearsOfService)
            {
                yearsOfService22To41 = await data.DateOfBirth.GetYearsOfServiceFrom22To41(adjStartDate, relevantDismissalDate);

                if (yearsOfServiceOver41 + yearsOfService22To41 > totalYearsOfService)
                {
                    yearsOfService22To41 = totalYearsOfService - yearsOfServiceOver41;
                }
            }
            if (yearsOfServiceOver41 + yearsOfService22To41 < totalYearsOfService)
            {
                yearsOfServiceUpto21 = totalYearsOfService - (yearsOfService22To41 + yearsOfServiceOver41);
            }

            var redundancyPayWeeks = decimal.Multiply(yearsOfServiceUpto21, 0.5m) + yearsOfService22To41 + decimal.Multiply(yearsOfServiceOver41, 1.5m);
            var grossEntitlement   = redundancyPayWeeks * appliedRateOfPay;

            var grossEntitlementFinal    = Math.Max(0m, Math.Round(grossEntitlement, 2));
            var employerPartPaymentFinal = Math.Round(data.EmployerPartPayment, 2);
            var netEntitlementFinal      = Math.Max(0m, (grossEntitlementFinal - employerPartPaymentFinal));

            calculationResult.AdjEmploymentStartDate     = adjStartDate;
            calculationResult.NoticeDateForRedundancyPay = relevantDismissalDate;
            calculationResult.NoticeEntitlementWeeks     = noticeEntitlementWeeks;
            calculationResult.RedundancyPayWeeks         = Math.Round(redundancyPayWeeks, 4);
            calculationResult.YearsOfServiceUpto21       = yearsOfServiceUpto21;
            calculationResult.YearsOfServiceFrom22To41   = yearsOfService22To41;
            calculationResult.YearsServiceOver41         = yearsOfServiceOver41;
            //calculationResult.GrossEntitlement = grossEntitlementFinal;
            calculationResult.GrossEntitlement     = netEntitlementFinal; //TEMP CHANGE: Gross entitlement set to Net entitlement as quick fix for CMS message to BW
            calculationResult.EmployerPartPayment  = employerPartPaymentFinal;
            calculationResult.NetEntitlement       = netEntitlementFinal;
            calculationResult.PreferentialClaim    = 0m;
            calculationResult.NonPreferentialClaim = netEntitlementFinal;
            calculationResult.StatutoryMax         = statutoryMax;

            return(calculationResult);
        }
        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));
        }
示例#19
0
        public async Task <HolidayTakenNotPaidResponseDTO> PerformCalculationAsync(
            List <HolidayTakenNotPaidCalculationRequestModel> data,
            string inputSource,
            decimal maxDaysInCurrentHolidayYear,
            decimal maxDaysInTotal,
            DateTime?holidayYearStart,
            IOptions <ConfigLookupRoot> options, TraceInfo traceInfo = null)
        {
            var statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, data.First().DismissalDate);

            var calculationResult = new HolidayTakenNotPaidResponseDTO();

            calculationResult.StatutoryMax = Math.Round(statutoryMax, 2);
            calculationResult.InputSource  = inputSource;

            var firstRequest = data.FirstOrDefault(r => r.InputSource == inputSource);

            if (firstRequest != null)
            {
                var tweleveMonthsPrior = firstRequest.InsolvencyDate.Date.AddMonths(-12).AddDays(1);
                var htnpEndDate        = firstRequest.DismissalDate.Date < firstRequest.InsolvencyDate.Date ? firstRequest.DismissalDate.Date : firstRequest.InsolvencyDate.Date;

                var weeks = new List <Week>();

                if (holidayYearStart.HasValue)
                {
                    var htnpDaysInCurrentHolidayYear = await data.GetHTNPDays(inputSource, holidayYearStart.Value.Date, htnpEndDate);

                    var htnpDaysIn12MonthsPrior = await data.GetHTNPDays(inputSource, tweleveMonthsPrior, holidayYearStart.Value.Date.AddDays(-1));

                    await SelectDaysAndConvertToWeeks(weeks, htnpDaysInCurrentHolidayYear, firstRequest, maxDaysInCurrentHolidayYear);

                    var numHtnpDaysSelected = weeks.Where(x => x.IsSelected).Sum(x => x.EmploymentDays);
                    await SelectDaysAndConvertToWeeks(weeks, htnpDaysIn12MonthsPrior, firstRequest, maxDaysInTotal - numHtnpDaysSelected);
                }
                else
                {
                    var htnpDays = await data.GetHTNPDays(inputSource, tweleveMonthsPrior, htnpEndDate);
                    await SelectDaysAndConvertToWeeks(weeks, htnpDays, firstRequest, maxDaysInTotal);
                }

                DateTime prefPeriodStartDate = firstRequest.InsolvencyDate.Date.AddMonths(-4);
                DateTime prefPeriodEndDate   = (firstRequest.DismissalDate < firstRequest.InsolvencyDate) ? firstRequest.DismissalDate.Date : firstRequest.InsolvencyDate.Date;

                // generate the output weeks
                int     weekNum    = 1;
                decimal total_days = 0.00m;

                foreach (var week in weeks.OrderBy(x => x.PayDate).ThenBy(x => x.IsSelected))
                {
                    var weekStartDate = week.PayDate.AddDays(-6);
                    var maximumDays   = await weekStartDate.GetNumDaysInIntersectionOfTwoRanges(week.PayDate, DateTime.MinValue.Date, firstRequest.DismissalDate.Date);

                    var maximumDaysInPrefPeriod = await weekStartDate.GetNumDaysInIntersectionOfTwoRanges(week.PayDate, prefPeriodStartDate, prefPeriodEndDate);

                    //calculate Employer Liability for week
                    var employerEntitlement             = firstRequest.WeeklyWage / firstRequest.ShiftPattern.Count * week.EmploymentDays;
                    var employerEntitlementInPrefPeriod = firstRequest.WeeklyWage / firstRequest.ShiftPattern.Count * week.EmploymentDaysInPrefPeriod;
                    var maximumEntitlement             = statutoryMax / 7 * maximumDays;
                    var maximumEntitlementInPrefPeriod = statutoryMax / 7 * maximumDaysInPrefPeriod;

                    var grossEntitlement = Math.Min(maximumEntitlement, employerEntitlement);
                    var taxRate          = ConfigValueLookupHelper.GetTaxRate(options, DateTime.Now);
                    var taxDeducated     = Math.Round(await grossEntitlement.GetTaxDeducted(taxRate, firstRequest.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, firstRequest.IsTaxable), 2);

                    grossEntitlement = Math.Round(grossEntitlement, 2);
                    var netLiability = await grossEntitlement.GetNetLiability(taxDeducated, niDeducted);

                    total_days += week.EmploymentDays;
                    calculationResult.WeeklyResult.Add(new HolidayTakenNotPaidWeeklyResult()
                    {
                        WeekNumber          = weekNum++,
                        PayDate             = week.PayDate,
                        IsSelected          = week.IsSelected,
                        MaximumEntitlement  = Math.Round(maximumEntitlement, 2),
                        EmployerEntitlement = Math.Round(employerEntitlement, 2),
                        GrossEntitlement    = grossEntitlement,
                        IsTaxable           = firstRequest.IsTaxable,
                        TaxDeducted         = taxDeducated,
                        NiDeducted          = niDeducted,
                        NetEntitlement      = netLiability,
                        MaximumDays         = maximumDays,
                        EmploymentDays      = Math.Round(week.EmploymentDays, 4),
                        MaximumEntitlementIn4MonthPeriod  = Math.Round(maximumEntitlementInPrefPeriod, 2),
                        EmployerEntitlementIn4MonthPeriod = Math.Round(employerEntitlementInPrefPeriod, 2),
                        GrossEntitlementIn4Months         = Math.Round(Math.Min(maximumEntitlementInPrefPeriod, employerEntitlementInPrefPeriod), 2),
                    });
                }
                foreach (var req in data.Where(x => x.InputSource == inputSource))
                {
                    traceInfo?.Dates.Add(new TraceInfoDate
                    {
                        StartDate = req.UnpaidPeriodFrom,
                        EndDate   = req.UnpaidPeriodTo,
                    });
                }
                if (traceInfo != null)
                {
                    traceInfo.NumberOfDays = total_days;
                }
            }
            return(await Task.FromResult(calculationResult));
        }
示例#20
0
        public async Task <ProtectiveAwardResponseDTO> PerformProtectiveAwardCalculationAsync(
            ProtectiveAwardCalculationRequestModel data, IOptions <ConfigLookupRoot> options)
        {
            var statMaxDate = data.DismissalDate;

            if (data.InsolvencyDate.Date > statMaxDate)
            {
                statMaxDate = data.InsolvencyDate.Date;
            }
            if (data.TribunalAwardDate.Date > statMaxDate)
            {
                statMaxDate = data.TribunalAwardDate.Date;
            }


            var statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, statMaxDate);
            var taxRate      = ConfigValueLookupHelper.GetTaxRate(options, data.DismissalDate);
            var niThreshold  = ConfigValueLookupHelper.GetNIThreshold(options, DateTime.Now);
            var niRate       = ConfigValueLookupHelper.GetNIRate(options, DateTime.Now);

            DayOfWeek payDay = (DayOfWeek)data.PayDay;

            bool isTaxable = (await data.DismissalDate.GetTaxYear() == await data.TribunalAwardDate.GetTaxYear());

            //calculate paAwardEndDate e.g. paAwardStartDate + 30 days
            var protectiveAwardEndDate = data.ProtectiveAwardStartDate.Date.AddDays(data.ProtectiveAwardDays.Value - 1);

            var paBenefitsEndDate = protectiveAwardEndDate;

            // get pay weeks in PA Award
            var endDate = protectiveAwardEndDate;

            if (endDate.DayOfWeek != payDay)
            {
                endDate = endDate.AddDays(7);
            }

            // set pref period start date
            DateTime prefPeriodStartDate = data.InsolvencyDate.Date.AddMonths(-4);

            var payDays = await data.ProtectiveAwardStartDate.Date.GetDaysInRange(endDate, payDay);

            //calculate daily benefit rate if benefit amount is provided
            decimal benefitDailyRate = decimal.Zero;

            if (data.paBenefitAmount > decimal.Zero)
            {
                benefitDailyRate = await data.paBenefitAmount.GetDailyAmount(
                    data.paBenefitStartDate.Date,
                    paBenefitsEndDate.Date);
            }


            //step through payWeek
            var counter  = 1;
            var payLines = new List <ProtectiveAwardPayLine>();

            foreach (var payWeekEnd in payDays)
            {
                var employmentDays             = 0;
                var maximumDays                = 0;
                var benefitClaimedAmount       = 0.00m;
                var employmentDaysInPrefPeriod = 0;
                var maximumDaysInPrefPeriod    = 0;


                for (int j = 6; j >= 0; j--)
                {
                    DateTime day = payWeekEnd.AddDays(-j);

                    if (day >= data.ProtectiveAwardStartDate.Date && day <= protectiveAwardEndDate.Date)
                    {
                        //is this day in shift paattern (a working day)?
                        if (await data.ShiftPattern.ContainsDayWeek(day.DayOfWeek))
                        {
                            employmentDays++;

                            if (day >= prefPeriodStartDate && day <= data.InsolvencyDate)
                            {
                                employmentDaysInPrefPeriod++;
                            }
                        }

                        maximumDays++;

                        if (day >= prefPeriodStartDate && day <= data.InsolvencyDate)
                        {
                            maximumDaysInPrefPeriod++;
                        }
                    }

                    if (day >= data.paBenefitStartDate.Date && day <= paBenefitsEndDate.Date)
                    {
                        benefitClaimedAmount += benefitDailyRate;
                    }
                }

                // calculate Employer Liability for week
                decimal empLiability = data.WeeklyWage / data.ShiftPattern.Count * employmentDays;
                decimal employerEntitlementInPrefPeriod = data.WeeklyWage / data.ShiftPattern.Count * employmentDaysInPrefPeriod;

                //get Maximum Liability
                decimal maxLiability = statutoryMax;
                decimal maximumEntitlementInPrefPeriod = statutoryMax / 7 * maximumDaysInPrefPeriod;

                //if first week
                if (payWeekEnd == payDays.First())
                {
                    //if there is only one week OR claimant did start employment in the first week of claim
                    if ((payDays.Count() == 1) || data.EmploymentStartDate.Date >= payDays.First().AddDays(-7))
                    {
                        maxLiability = statutoryMax / 7M * maximumDays;
                    }
                }
                // if it is the last week
                else if (payWeekEnd == payDays.Last())
                {
                    if (payWeekEnd > protectiveAwardEndDate)
                    {
                        maxLiability = statutoryMax / 7 * maximumDays;
                    }
                }

                decimal grossEntitlement = Math.Min(empLiability, maxLiability) - benefitClaimedAmount;
                grossEntitlement = Math.Max(grossEntitlement, 0m);
                decimal taxDeducted = Math.Round(await grossEntitlement.GetTaxDeducted(taxRate, isTaxable), 2);
                decimal niDeducted  = Math.Round(await grossEntitlement.GetNIDeducted(niThreshold, niRate, true), 2);

                grossEntitlement = Math.Round(grossEntitlement, 2);
                decimal netEntitlement = grossEntitlement - taxDeducted - niDeducted;

                payLines.Add(new ProtectiveAwardPayLine()
                {
                    WeekNumber       = counter++,
                    PayDate          = payWeekEnd,
                    BenefitsClaimed  = Math.Round(benefitClaimedAmount, 2),
                    GrossEntitlement = grossEntitlement,
                    TaxDeducted      = taxDeducted,
                    NIDeducted       = niDeducted,
                    NetEntitlement   = netEntitlement,
                    IsSelected       = false,
                    MaximumEntitlementIn4MonthPeriod  = Math.Round(maximumEntitlementInPrefPeriod, 2),
                    EmployerEntitlementIn4MonthPeriod = Math.Round(employerEntitlementInPrefPeriod, 2),
                    GrossEntitlementIn4Months         = Math.Round(Math.Min(maximumEntitlementInPrefPeriod, employerEntitlementInPrefPeriod), 2)
                });
            }
            ;

            var result = new ProtectiveAwardResponseDTO()
            {
                IsTaxable    = isTaxable,
                StatutoryMax = statutoryMax,
                PayLines     = payLines
            };

            return(await Task.FromResult(result));
        }
        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));
        }
示例#22
0
        public async Task <NoticePayCompositeCalculationResponseDTO> PerformNoticePayCompositeCalculationAsync(NoticePayCompositeCalculationRequestModel data, IOptions <ConfigLookupRoot> options)
        {
            var result        = new NoticePayCompositeCalculationResponseDTO();
            var cnpOutput     = new CompensatoryNoticePayCalculationResponseDTO();
            var nwnpOutput    = new NoticeWorkedNotPaidCompositeOutput();
            var rp1TraceInfo  = new TraceInfo();
            var rp14TraceInfo = new TraceInfo();

            //CNP calculation
            if (data.Cnp != null)
            {
                cnpOutput = await _cnpService.PerformCompensatoryNoticePayCalculationAsync(data.Cnp, options);

                cnpOutput.WeeklyResults.ForEach(x => x.IsSelected = true);
            }

            //NWNP calculation
            if (data.Nwnp != null && data.Nwnp.Any())
            {
                var statutoryMax = ConfigValueLookupHelper.GetStatutoryMax(options, data.Nwnp.FirstOrDefault().InsolvencyDate);

                foreach (var nwnp in data.Nwnp)
                {
                    var traceDate = new TraceInfoDate();
                    var res       = await _nwnpService.PerformNwnpCalculationAsync(nwnp, options, traceDate);

                    if (res.InputSource == InputSource.Rp1)
                    {
                        //nwnpOutput.nwnpResults.rP1ResultsList.Add(res);
                        nwnpOutput.rp1Results.WeeklyResult.AddRange(res.WeeklyResult);
                        rp1TraceInfo.Dates.Add(traceDate);
                        rp1TraceInfo.NumberOfDays = 0.00m;
                        foreach (var week in res.WeeklyResult)
                        {
                            rp1TraceInfo.NumberOfDays += week.EmploymentDays;
                        }
                    }
                    else if (res.InputSource == InputSource.Rp14a)
                    {
                        //nwnpOutput.nwnpResults.rP14aResultsList.Add(res);
                        nwnpOutput.rp14aResults.WeeklyResult.AddRange(res.WeeklyResult);
                        rp14TraceInfo.Dates.Add(traceDate);
                        rp14TraceInfo.NumberOfDays = 0.00m;
                        foreach (var week in res.WeeklyResult)
                        {
                            rp14TraceInfo.NumberOfDays += week.EmploymentDays;
                        }
                    }
                }

                var rp1Total   = 0m;
                var rp14aTotal = 0m;

                //merge pay weeks
                nwnpOutput.rp1Results = await nwnpOutput.rp1Results.MergePayWeeks(options);

                nwnpOutput.rp1Results.InputSource  = InputSource.Rp1;
                nwnpOutput.rp1Results.StatutoryMax = statutoryMax;
                rp1Total = nwnpOutput.rp1Results.WeeklyResult.Sum(x => x.NetEntitlement);

                //merge pay weeks
                nwnpOutput.rp14aResults = await nwnpOutput.rp14aResults.MergePayWeeks(options);

                nwnpOutput.rp14aResults.InputSource  = InputSource.Rp14a;
                nwnpOutput.rp14aResults.StatutoryMax = statutoryMax;
                rp14aTotal = nwnpOutput.rp14aResults.WeeklyResult.Sum(x => x.NetEntitlement);

                //select the input source
                // Choose RP1 list or RP14a list with the lowest total NetEntitlement
                if ((rp1Total < rp14aTotal && rp14aTotal != 0 && rp1Total != 0) || (rp1Total > 0 && rp14aTotal == 0))
                {
                    nwnpOutput.SelectedInputSource = InputSource.Rp1;
                    nwnpOutput.rp1Results.WeeklyResult.ForEach(x => x.IsSelected   = true);
                    nwnpOutput.rp14aResults.WeeklyResult.ForEach(x => x.IsSelected = false);
                    nwnpOutput.TraceInfo = await rp1TraceInfo.ConvertToJson();
                }
                else
                {
                    nwnpOutput.SelectedInputSource = InputSource.Rp14a;
                    nwnpOutput.rp1Results.WeeklyResult.ForEach(x => x.IsSelected   = false);
                    nwnpOutput.rp14aResults.WeeklyResult.ForEach(x => x.IsSelected = true);
                    nwnpOutput.TraceInfo = await rp14TraceInfo.ConvertToJson();
                }
            }

            result.Cnp  = cnpOutput;
            result.Nwnp = nwnpOutput;
            return(result);
        }