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));
        }
예제 #2
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);
        }