private IReProfilingStrategy GetReProfilingStrategy(ReProfileRequest reProfileRequest,
                                                            FundingStreamPeriodProfilePattern profilePattern)
        {
            string key = profilePattern.GetReProfilingStrategyKeyForFundingAmountChange(reProfileRequest.FundingLineTotalChange);

            return(_reProfilingStrategyLocator.GetStrategy(key));
        }
コード例 #2
0
        public async Task <IActionResult> PreviewProfilingChange(ProfilePreviewRequest request)
        {
            Guard.ArgumentNotNull(request, nameof(request));

            ReProfileRequest reProfileRequest = await _reProfilingRequestBuilder.BuildReProfileRequest(request.SpecificationId,
                                                                                                       request.FundingStreamId,
                                                                                                       request.FundingPeriodId,
                                                                                                       request.ProviderId,
                                                                                                       request.FundingLineCode,
                                                                                                       request.ProfilePatternKey,
                                                                                                       request.ConfigurationType);

            ApiResponse <ReProfileResponse> reProfilingApiResponse = await _profilingResilience.ExecuteAsync(() => _profiling.ReProfile(reProfileRequest));

            ReProfileResponse reProfileResponse = reProfilingApiResponse?.Content;

            if (reProfileResponse == null)
            {
                throw new InvalidOperationException(
                          $"Did not received a valid re-profiling response for profile pattern preview request {request}");
            }

            ExistingProfilePeriod[] existingProfilePeriods = reProfileRequest.ExistingPeriods.ToArray();

            int isPaidTo = GetIsPaidTo(existingProfilePeriods);

            ProfileTotal[] profileTotals = BuildProfileTotals(reProfileResponse, existingProfilePeriods, isPaidTo);

            await AddFundingDatesToProfileTotals(request, profileTotals);

            return(new OkObjectResult(profileTotals));
        }
 private void GivenTheProfilePattern(ReProfileRequest request,
                                     FundingStreamPeriodProfilePattern profilePattern)
 => _profilePatterns.Setup(_ => _.GetProfilePattern(request.FundingStreamId,
                                                    request.FundingPeriodId,
                                                    request.FundingLineCode,
                                                    request.ProfilePatternKey))
 .ReturnsAsync(profilePattern);
        public void GuardsAgainstProfiledAmountsAndCarryOverNotBeingSameAsFundingValueInTheSuppliedRequestAfterReProfiling()
        {
            ReProfileRequest          request         = NewReProfileRequest();
            AllocationProfileResponse profileResponse = NewAllocationProfileResponse();

            string key = NewRandomString();

            FundingStreamPeriodProfilePattern profilePattern = NewFundingStreamPeriodProfilePattern(_ =>
                                                                                                    _.WithReProfilingConfiguration(NewProfilePatternReProfilingConfiguration(cfg =>
                                                                                                                                                                             cfg.WithIsEnabled(true)
                                                                                                                                                                             .WithIncreasedAmountStrategyKey(key))));

            GivenTheProfilePattern(request, profilePattern);
            AndTheReProfilingStrategy(key);
            AndTheProfiling(request, profilePattern, profileResponse);
            AndTheReProfilingStrategyResponse(profileResponse, request, profilePattern, NewReProfileStrategyResult());

            _service
            .Awaiting(_ => _.ReProfile(request))
            .Should()
            .Throw <InvalidOperationException>()
            .Which
            .Message
            .Should()
            .Be($"Profile amounts (0) and carry over amount (0) does not equal funding line total requested ({request.FundingLineTotal}) from strategy.");
        }
 private void AndTheReProfilingStrategyResponse(AllocationProfileResponse profileResponse,
                                                ReProfileRequest request,
                                                FundingStreamPeriodProfilePattern profilePattern,
                                                ReProfileStrategyResult response)
 => _reProfilingStrategy.Setup(_ => _.ReProfile(It.Is <ReProfileContext>(ctx =>
                                                                         ReferenceEquals(ctx.Request, request) &&
                                                                         ReferenceEquals(ctx.ProfilePattern, profilePattern) &&
                                                                         ReferenceEquals(ctx.ProfileResult, profileResponse))))
 .Returns(response);
コード例 #6
0
        private decimal AdjustForUnderOrOverPayment(ReProfileContext context)
        {
            decimal carryOverAmount = 0;

            ReProfileRequest reProfileRequest = context.Request;

            IProfilePeriod[] orderedRefreshProfilePeriods = new YearMonthOrderedProfilePeriods <IProfilePeriod>(context.ProfileResult.DeliveryProfilePeriods)
                                                            .ToArray();
            IExistingProfilePeriod[] orderedExistingProfilePeriods = new YearMonthOrderedProfilePeriods <IExistingProfilePeriod>(reProfileRequest.ExistingPeriods)
                                                                     .ToArray();

            // get the last profile period paid
            IExistingProfilePeriod existingProfilePeriod = orderedExistingProfilePeriods.LastOrDefault(_ => _.IsPaid);

            // the variation pointer is the index of the next period after the last paid profile period
            int variationPointerIndex = existingProfilePeriod == null ? 0 : Array.IndexOf(orderedExistingProfilePeriods, existingProfilePeriod) + 1;

            decimal previousFundingLineValuePaid = orderedExistingProfilePeriods.Take(variationPointerIndex).Sum(_ => _.GetProfileValue());
            decimal latestFundingLineValuePaid   = orderedRefreshProfilePeriods.Take(variationPointerIndex).Sum(_ => _.GetProfileValue());
            decimal latestFundingLineValue       = reProfileRequest.FundingLineTotal;

            decimal fundingChange      = latestFundingLineValuePaid - previousFundingLineValuePaid;
            decimal latestPeriodAmount = (int)(latestFundingLineValue / orderedRefreshProfilePeriods.Length);

            AdjustPeriodsForFundingAlreadyReleased(variationPointerIndex,
                                                   orderedExistingProfilePeriods,
                                                   orderedRefreshProfilePeriods);

            if (fundingChange < 0)
            {
                if (AdjustingPeriodsForOverPaymentLeavesRemainder(variationPointerIndex,
                                                                  latestPeriodAmount,
                                                                  orderedExistingProfilePeriods,
                                                                  orderedRefreshProfilePeriods,
                                                                  out decimal remainingOverPayment))
                {
                    carryOverAmount = remainingOverPayment;
                }
            }
            else if (fundingChange > 0)
            {
                AdjustPeriodsForUnderPayment(variationPointerIndex,
                                             latestPeriodAmount,
                                             orderedExistingProfilePeriods,
                                             orderedRefreshProfilePeriods);
            }
            else
            {
                AdjustPeriodsForNoTotalAllocationChange(variationPointerIndex,
                                                        latestPeriodAmount,
                                                        orderedExistingProfilePeriods,
                                                        orderedRefreshProfilePeriods,
                                                        out carryOverAmount);
            }

            return(carryOverAmount);
        }
コード例 #7
0
 private void AndTheTheReProfileRequest(FundingLine fundingLine,
                                        ReProfileRequest reProfileRequest)
 => _reProfileRequestBuilder.Setup(_ => _.BuildReProfileRequest(RefreshState.SpecificationId,
                                                                RefreshState.FundingStreamId,
                                                                RefreshState.FundingPeriodId,
                                                                RefreshState.ProviderId,
                                                                fundingLine.FundingLineCode,
                                                                null,
                                                                ProfileConfigurationType.RuleBased,
                                                                fundingLine.Value))
 .ReturnsAsync(reProfileRequest);
 private void GivenTheReProfileRequest(ProfilePreviewRequest previewRequest,
                                       ReProfileRequest reProfileRequest)
 => _reProfilingRequestBuilder.Setup(_ => _.BuildReProfileRequest(previewRequest.SpecificationId,
                                                                  previewRequest.FundingStreamId,
                                                                  previewRequest.FundingPeriodId,
                                                                  previewRequest.ProviderId,
                                                                  previewRequest.FundingLineCode,
                                                                  previewRequest.ProfilePatternKey,
                                                                  previewRequest.ConfigurationType,
                                                                  null))
 .ReturnsAsync(reProfileRequest);
 private void AndTheProfiling(ReProfileRequest request,
                              FundingStreamPeriodProfilePattern profilePattern,
                              AllocationProfileResponse response)
 => _profiling.Setup(_ => _.ProfileAllocation(It.Is <ProfileRequest>(req =>
                                                                     req.FundingStreamId == request.FundingStreamId &&
                                                                     req.FundingPeriodId == request.FundingPeriodId &&
                                                                     req.FundingLineCode == request.FundingLineCode &&
                                                                     req.FundingValue == request.FundingLineTotal &&
                                                                     req.ProfilePatternKey == request.ProfilePatternKey),
                                              profilePattern,
                                              request.FundingLineTotal))
 .Returns(response);
        public async Task ReturnsBadRequestIfTheMatchingPatternHasNoReProfilingConfiguration()
        {
            ReProfileRequest request = NewReProfileRequest();

            GivenTheProfilePattern(request, NewFundingStreamPeriodProfilePattern());

            ActionResult <ReProfileResponse> result = await WhenTheFundingLineIsReProfiled(request);

            result
            .Result
            .Should()
            .BeOfType <BadRequestObjectResult>()
            .Which
            .Value
            .Should()
            .Be("Re-profiling is not enabled or has not been configured");
        }
コード例 #11
0
        public async Task ReProfilesFundingLinesInTheRefreshStateWhereTheyShowAsAffectedFundingLineCodes()
        {
            FundingLine fundingLineOne   = NewFundingLine();
            FundingLine fundingLineTwo   = NewFundingLine();
            FundingLine fundingLineThree = NewFundingLine();

            ReProfileRequest reProfileRequestOne   = NewReProfileRequest();
            ReProfileRequest reProfileRequestThree = NewReProfileRequest();

            ReProfileResponse reProfileResponseOne   = NewReProfileResponse();
            ReProfileResponse reProfileResponseThree = NewReProfileResponse();

            DistributionPeriod[] distributionPeriodsOne   = NewDistributionPeriods();
            DistributionPeriod[] distributionPeriodsTwo   = NewDistributionPeriods();
            DistributionPeriod[] distributionPeriodsThree = NewDistributionPeriods();

            fundingLineOne.DistributionPeriods   = NewDistributionPeriods(_ => _.WithDistributionPeriodId(distributionPeriodsOne.Single().DistributionPeriodId));
            fundingLineTwo.DistributionPeriods   = distributionPeriodsTwo;
            fundingLineThree.DistributionPeriods = NewDistributionPeriods(_ => _.WithDistributionPeriodId(distributionPeriodsThree.Single().DistributionPeriodId));

            GivenTheFundingLines(fundingLineOne, fundingLineTwo, fundingLineThree);
            AndTheAffectedFundingLineCodes(fundingLineOne.FundingLineCode, fundingLineThree.FundingLineCode);
            AndTheTheReProfileRequest(fundingLineOne, reProfileRequestOne);
            AndTheTheReProfileRequest(fundingLineThree, reProfileRequestThree);
            AndTheReProfileResponse(reProfileRequestOne, reProfileResponseOne);
            AndTheReProfileResponse(reProfileRequestThree, reProfileResponseThree);
            AndTheReProfileResponseMapping(reProfileResponseOne, distributionPeriodsOne);
            AndTheReProfileResponseMapping(reProfileResponseThree, distributionPeriodsThree);

            await WhenTheChangeIsApplied();

            fundingLineOne.DistributionPeriods
            .Should()
            .BeEquivalentTo <DistributionPeriod>(distributionPeriodsOne);

            fundingLineTwo.DistributionPeriods
            .Should()
            .BeSameAs(distributionPeriodsTwo);

            fundingLineThree.DistributionPeriods
            .Should()
            .BeEquivalentTo <DistributionPeriod>(distributionPeriodsThree);
        }
 private void AndTheReProfileRequest(string specificationId,
                                     string fundingStreamId,
                                     string fundingPeriodId,
                                     string providerId,
                                     string fundingLineCode,
                                     string profilePatternKey,
                                     ProfileConfigurationType configurationType,
                                     decimal?fundingLineTotal,
                                     ReProfileRequest profileRequest)
 {
     _reProfilingRequestBuilder.Setup(_ => _.BuildReProfileRequest(specificationId,
                                                                   fundingStreamId,
                                                                   fundingPeriodId,
                                                                   providerId,
                                                                   fundingLineCode,
                                                                   profilePatternKey,
                                                                   configurationType,
                                                                   fundingLineTotal))
     .ReturnsAsync(profileRequest);
 }
        public async Task ReturnsBadRequestIfTheReProfilingConfigurationSuppliesAnUnsupportedStrategy()
        {
            ReProfileRequest request = NewReProfileRequest();

            GivenTheProfilePattern(request,
                                   NewFundingStreamPeriodProfilePattern(_ =>
                                                                        _.WithReProfilingConfiguration(NewProfilePatternReProfilingConfiguration(cfg =>
                                                                                                                                                 cfg.WithIsEnabled(true)))));

            ActionResult <ReProfileResponse> result = await WhenTheFundingLineIsReProfiled(request);

            result
            .Result
            .Should()
            .BeOfType <BadRequestObjectResult>()
            .Which
            .Value
            .Should()
            .Be("Re-profiling is not enabled for this scenario or the strategy was not found");
        }
        public async Task ProfilesFundingLinesNormallyThenReProfilesUsingTheseResultsWithSameAmountStrategyIfFundingTheSame()
        {
            decimal newFundingTotal = NewRandomTotal();

            ReProfileRequest request = NewReProfileRequest(_ => _.WithFundingValue(newFundingTotal)
                                                           .WithExistingFundingValue(newFundingTotal));
            AllocationProfileResponse profileResponse = NewAllocationProfileResponse();

            string key = NewRandomString();

            FundingStreamPeriodProfilePattern profilePattern = NewFundingStreamPeriodProfilePattern(_ =>
                                                                                                    _.WithReProfilingConfiguration(NewProfilePatternReProfilingConfiguration(cfg =>
                                                                                                                                                                             cfg.WithIsEnabled(true)
                                                                                                                                                                             .WithSameAmountStrategyKey(key))));

            DistributionPeriods   distributionPeriods1   = NewDistributionPeriods();
            DeliveryProfilePeriod deliveryProfilePeriod1 = NewDeliveryProfilePeriod(_ => _.WithProfiledValue(10));
            DeliveryProfilePeriod deliveryProfilePeriod2 = NewDeliveryProfilePeriod(_ => _.WithProfiledValue(newFundingTotal - 20));

            GivenTheProfilePattern(request, profilePattern);
            AndTheReProfilingStrategy(key);
            AndTheProfiling(request, profilePattern, profileResponse);
            AndTheReProfilingStrategyResponse(profileResponse, request, profilePattern, NewReProfileStrategyResult(_ =>
                                                                                                                   _.WithDistributionPeriods(distributionPeriods1)
                                                                                                                   .WithDeliveryProfilePeriods(deliveryProfilePeriod1, deliveryProfilePeriod2)
                                                                                                                   .WithCarryOverAmount(10)));

            ActionResult <ReProfileResponse> reProfileResponse = await WhenTheFundingLineIsReProfiled(request);

            reProfileResponse?
            .Value
            .Should()
            .BeEquivalentTo(new ReProfileResponse
            {
                DistributionPeriods       = new [] { distributionPeriods1 },
                CarryOverAmount           = 10,
                DeliveryProfilePeriods    = new [] { deliveryProfilePeriod1, deliveryProfilePeriod2 },
                ProfilePatternKey         = profilePattern.ProfilePatternKey,
                ProfilePatternDisplayName = profilePattern.ProfilePatternDisplayName
            });
        }
        private ReProfileContext CreateReProfilingContext(ReProfileRequest reProfileRequest,
                                                          FundingStreamPeriodProfilePattern profilePattern)
        {
            AllocationProfileResponse profileResult = _calculateProfileService.ProfileAllocation(new ProfileRequest
            {
                FundingLineCode   = reProfileRequest.FundingLineCode,
                FundingPeriodId   = reProfileRequest.FundingPeriodId,
                FundingStreamId   = reProfileRequest.FundingStreamId,
                FundingValue      = reProfileRequest.FundingLineTotal,
                ProfilePatternKey = reProfileRequest.ProfilePatternKey
            },
                                                                                                 profilePattern,
                                                                                                 reProfileRequest.FundingLineTotal);

            return(new ReProfileContext
            {
                Request = reProfileRequest,
                ProfilePattern = profilePattern,
                ProfileResult = profileResult
            });
        }
        private async Task ReProfileFundingLine(string fundingLineCode,
                                                PublishedProviderVersion refreshState,
                                                IApplyProviderVariations variationApplications)
        {
            FundingLine fundingLine = refreshState.FundingLines.SingleOrDefault(_ => _.FundingLineCode == fundingLineCode);

            string providerId = refreshState.ProviderId;

            if (fundingLine == null)
            {
                throw new NonRetriableException($"Could not locate funding line {fundingLineCode} for published provider version {providerId}");
            }

            ReProfileRequest reProfileRequest = await variationApplications.ReProfilingRequestBuilder.BuildReProfileRequest(refreshState.SpecificationId,
                                                                                                                            refreshState.FundingStreamId,
                                                                                                                            refreshState.FundingPeriodId,
                                                                                                                            providerId,
                                                                                                                            fundingLineCode,
                                                                                                                            null,
                                                                                                                            ProfileConfigurationType.RuleBased,
                                                                                                                            fundingLine.Value);

            ReProfileResponse reProfileResponse = (await variationApplications.ResiliencePolicies.ProfilingApiClient.ExecuteAsync(()
                                                                                                                                  => variationApplications.ProfilingApiClient.ReProfile(reProfileRequest)))?.Content;

            if (reProfileResponse == null)
            {
                throw new NonRetriableException($"Could not re profile funding line {fundingLineCode} for provider {providerId}");
            }

            IEnumerable <DistributionPeriod> distributionPeriods = variationApplications.ReProfilingResponseMapper.MapReProfileResponseIntoDistributionPeriods(reProfileResponse);

            foreach (DistributionPeriod distributionPeriod in distributionPeriods)
            {
                refreshState.UpdateDistributionPeriodForFundingLine(fundingLineCode,
                                                                    distributionPeriod.DistributionPeriodId,
                                                                    distributionPeriod.ProfilePeriods);
            }
        }
        public async Task <ActionResult <ReProfileResponse> > ReProfile(ReProfileRequest reProfileRequest)
        {
            FundingStreamPeriodProfilePattern profilePattern = await _profilePatternService.GetProfilePattern(reProfileRequest.FundingStreamId,
                                                                                                              reProfileRequest.FundingPeriodId,
                                                                                                              reProfileRequest.FundingLineCode,
                                                                                                              reProfileRequest.ProfilePatternKey);

            if (profilePattern == null)
            {
                return(new NotFoundObjectResult("Profile pattern not found"));
            }

            if (profilePattern.ReProfilingConfiguration == null || !profilePattern.ReProfilingConfiguration.ReProfilingEnabled)
            {
                return(new BadRequestObjectResult("Re-profiling is not enabled or has not been configured"));
            }

            IReProfilingStrategy strategy = GetReProfilingStrategy(reProfileRequest, profilePattern);

            if (strategy == null)
            {
                return(new BadRequestObjectResult("Re-profiling is not enabled for this scenario or the strategy was not found"));
            }

            ReProfileContext context = CreateReProfilingContext(reProfileRequest, profilePattern);

            ReProfileStrategyResult strategyResult = strategy.ReProfile(context);

            VerifyProfileAmountsReturnedMatchRequestedFundingLineValue(reProfileRequest, strategyResult);

            return(new ReProfileResponse
            {
                DeliveryProfilePeriods = strategyResult.DeliveryProfilePeriods,
                DistributionPeriods = strategyResult.DistributionPeriods,
                ProfilePatternDisplayName = profilePattern.ProfilePatternDisplayName,
                ProfilePatternKey = profilePattern.ProfilePatternKey,
                CarryOverAmount = strategyResult.CarryOverAmount
            });
        }
コード例 #18
0
        private async Task ReProfileFundingLine(PublishedProviderVersion newPublishedProviderVersion,
                                                ProfilePatternKey profilePatternKey,
                                                string fundingLineCode,
                                                FundingLine fundingLine)
        {
            ReProfileRequest reProfileRequest = await _profilingRequestBuilder.BuildReProfileRequest(newPublishedProviderVersion.SpecificationId,
                                                                                                     newPublishedProviderVersion.FundingStreamId,
                                                                                                     newPublishedProviderVersion.FundingPeriodId,
                                                                                                     newPublishedProviderVersion.ProviderId,
                                                                                                     fundingLineCode,
                                                                                                     profilePatternKey.Key,
                                                                                                     ProfileConfigurationType.Custom,
                                                                                                     fundingLine.Value);

            ReProfileResponse reProfileResponse = (await _profilingPolicy.ExecuteAsync(() => _profiling.ReProfile(reProfileRequest)))?.Content;

            if (reProfileResponse == null)
            {
                string error = $"Unable to re-profile funding line {fundingLineCode} on specification {newPublishedProviderVersion.SpecificationId} with profile pattern {profilePatternKey.Key}";

                _logger.Error(error);

                throw new InvalidOperationException(error);
            }

            fundingLine.DistributionPeriods = _reProfilingResponseMapper.MapReProfileResponseIntoDistributionPeriods(reProfileResponse);

            newPublishedProviderVersion.RemoveCarryOver(fundingLineCode);

            if (reProfileResponse.CarryOverAmount > 0)
            {
                newPublishedProviderVersion.AddCarryOver(fundingLineCode,
                                                         ProfilingCarryOverType.CustomProfile,
                                                         reProfileResponse.CarryOverAmount);
            }
        }
コード例 #19
0
        public ReProfileContextBuilder WithReProfileRequest(ReProfileRequest request)
        {
            _request = request;

            return(this);
        }
 private async Task <ActionResult <ReProfileResponse> > WhenTheFundingLineIsReProfiled(ReProfileRequest request)
 => await _service.ReProfile(request);
        public async Task BuildsReProfileRequestsOutOfExistingFundingInformationUsingPublishedProvidersAndVariationPointers()
        {
            string  providerId       = NewRandomString();
            string  fundingStreamId  = NewRandomString();
            string  fundingPeriodId  = NewRandomString();
            string  fundingLineCode  = NewRandomString();
            string  specificationId  = NewRandomString();
            string  profilePattern   = NewRandomString();
            decimal fundingLineTotal = NewRandomAmount();
            ProfileConfigurationType profileConfigurationType = NewRandomProfileConfigurationType();

            GivenThePublishedProvider(fundingStreamId,
                                      fundingPeriodId,
                                      providerId,
                                      NewPublishedProvider(_ => _.WithCurrent(NewPublisherProviderVersion(pvp =>
                                                                                                          pvp.WithFundingLines(NewFundingLine(),
                                                                                                                               NewFundingLine(fl => fl.WithFundingLineCode(fundingLineCode)
                                                                                                                                              .WithDistributionPeriods(NewDistributionPeriod(dp =>
                                                                                                                                                                                             dp.WithProfilePeriods(NewProfilePeriod(pp => pp.WithDistributionPeriodId("dp1")
                                                                                                                                                                                                                                    .WithAmount(23)
                                                                                                                                                                                                                                    .WithOccurence(0)
                                                                                                                                                                                                                                    .WithYear(2021)
                                                                                                                                                                                                                                    .WithType(ProfilePeriodType.CalendarMonth)
                                                                                                                                                                                                                                    .WithTypeValue("January")),
                                                                                                                                                                                                                   NewProfilePeriod(pp => pp.WithDistributionPeriodId("dp1")
                                                                                                                                                                                                                                    .WithAmount(24)
                                                                                                                                                                                                                                    .WithOccurence(1)
                                                                                                                                                                                                                                    .WithYear(2021)
                                                                                                                                                                                                                                    .WithType(ProfilePeriodType.CalendarMonth)
                                                                                                                                                                                                                                    .WithTypeValue("January")),
                                                                                                                                                                                                                   NewProfilePeriod(pp => pp.WithDistributionPeriodId("dp1")
                                                                                                                                                                                                                                    .WithAmount(25)
                                                                                                                                                                                                                                    .WithOccurence(0)
                                                                                                                                                                                                                                    .WithYear(2021)
                                                                                                                                                                                                                                    .WithType(ProfilePeriodType.CalendarMonth)
                                                                                                                                                                                                                                    .WithTypeValue("March")),
                                                                                                                                                                                                                   NewProfilePeriod(pp => pp.WithDistributionPeriodId("dp1")
                                                                                                                                                                                                                                    .WithAmount(26)
                                                                                                                                                                                                                                    .WithOccurence(0)
                                                                                                                                                                                                                                    .WithYear(2021)
                                                                                                                                                                                                                                    .WithType(ProfilePeriodType.CalendarMonth)
                                                                                                                                                                                                                                    .WithTypeValue("April"))
                                                                                                                                                                                                                   )))))))));

            ReProfileRequest reProfileRequest = await WhenTheReProfileRequestIsBuilt(specificationId,
                                                                                     fundingStreamId,
                                                                                     fundingPeriodId,
                                                                                     providerId,
                                                                                     fundingLineCode,
                                                                                     profilePattern,
                                                                                     profileConfigurationType,
                                                                                     fundingLineTotal);

            reProfileRequest
            .Should()
            .BeEquivalentTo(new ReProfileRequest
            {
                ConfigurationType        = profileConfigurationType,
                FundingLineCode          = fundingLineCode,
                ExistingFundingLineTotal = 23 + 24 + 25 + 26,
                FundingLineTotal         = fundingLineTotal,
                FundingPeriodId          = fundingPeriodId,
                FundingStreamId          = fundingStreamId,
                ProfilePatternKey        = profilePattern,
                ExistingPeriods          = new []
                {
                    NewExististingProfilePeriod(_ => _.WithOccurrence(0)
                                                .WithDistributionPeriod("dp1")
                                                .WithValue(null)
                                                .WithPeriodType(PeriodType.CalendarMonth)
                                                .WithTypeValue("January")
                                                .WithYear(2021)),
                    NewExististingProfilePeriod(_ => _.WithOccurrence(1)
                                                .WithDistributionPeriod("dp1")
                                                .WithValue(null)
                                                .WithPeriodType(PeriodType.CalendarMonth)
                                                .WithTypeValue("January")
                                                .WithYear(2021)),
                    NewExististingProfilePeriod(_ => _.WithOccurrence(0)
                                                .WithDistributionPeriod("dp1")
                                                .WithValue(null)
                                                .WithPeriodType(PeriodType.CalendarMonth)
                                                .WithTypeValue("March")
                                                .WithYear(2021)),
                    NewExististingProfilePeriod(_ => _.WithOccurrence(0)
                                                .WithDistributionPeriod("dp1")
                                                .WithValue(null)
                                                .WithPeriodType(PeriodType.CalendarMonth)
                                                .WithTypeValue("April")
                                                .WithYear(2021))
                }
            });
        }
 public async Task <ActionResult <ReProfileResponse> > Post([FromBody] ReProfileRequest request)
 => await _reProfilingService.ReProfile(request);
        private static void VerifyProfileAmountsReturnedMatchRequestedFundingLineValue(ReProfileRequest reProfileRequest,
                                                                                       ReProfileStrategyResult strategyResult)
        {
            decimal profileTotals = (strategyResult.DeliveryProfilePeriods?.Sum(_ => _.ProfileValue)).GetValueOrDefault();
            decimal totalAmount   = profileTotals + strategyResult.CarryOverAmount;

            if (totalAmount != reProfileRequest.FundingLineTotal)
            {
                throw new InvalidOperationException(
                          $"Profile amounts ({profileTotals}) and carry over amount ({strategyResult.CarryOverAmount}) does not equal funding line total requested ({reProfileRequest.FundingLineTotal}) from strategy.");
            }
        }
        public async Task ReProfilesFundingLineAndMapsResultsIntoProfileTotals()
        {
            ProfilePreviewRequest previewRequest = NewProfilePreviewRequest();

            string distributionPeriod = NewRandomString();

            ReProfileRequest expectedReProfileRequest = NewReProfileRequest(_ => _.WithFundingLineTotal(999)
                                                                            .WithExistingFundingLineTotal(1000)
                                                                            .WithExistingProfilePeriods(NewExistingProfilePeriod(exp =>
                                                                                                                                 exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                 .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                 .WithTypeValue("January")
                                                                                                                                 .WithValue(23)
                                                                                                                                 .WithYear(2021)
                                                                                                                                 .WithOccurrence(0)),
                                                                                                        NewExistingProfilePeriod(exp =>
                                                                                                                                 exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                 .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                 .WithTypeValue("January")
                                                                                                                                 .WithValue(24)
                                                                                                                                 .WithYear(2021)
                                                                                                                                 .WithOccurrence(1)),
                                                                                                        NewExistingProfilePeriod(exp =>
                                                                                                                                 exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                 .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                 .WithTypeValue("February")
                                                                                                                                 .WithValue(null)
                                                                                                                                 .WithYear(2021)
                                                                                                                                 .WithOccurrence(0)),
                                                                                                        NewExistingProfilePeriod(exp =>
                                                                                                                                 exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                 .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                 .WithTypeValue("March")
                                                                                                                                 .WithValue(null)
                                                                                                                                 .WithYear(2021)
                                                                                                                                 .WithOccurrence(0)),
                                                                                                        NewExistingProfilePeriod(exp =>
                                                                                                                                 exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                 .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                 .WithTypeValue("April")
                                                                                                                                 .WithValue(null)
                                                                                                                                 .WithYear(2021)
                                                                                                                                 .WithOccurrence(0)),
                                                                                                        NewExistingProfilePeriod(exp =>
                                                                                                                                 exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                 .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                 .WithTypeValue("May")
                                                                                                                                 .WithValue(null)
                                                                                                                                 .WithYear(2021)
                                                                                                                                 .WithOccurrence(0))
                                                                                                        ));
            ReProfileResponse expectedReProfileResponse = NewReProfileResponse(_ => _.WithCarryOverAmount(992)
                                                                               .WithDeliveryProfilePeriods(NewDeliveryProfilePeriod(exp =>
                                                                                                                                    exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                    .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                    .WithTypeValue("January")
                                                                                                                                    .WithValue(33)
                                                                                                                                    .WithYear(2021)
                                                                                                                                    .WithOccurrence(0)),
                                                                                                           NewDeliveryProfilePeriod(exp =>
                                                                                                                                    exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                    .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                    .WithTypeValue("January")
                                                                                                                                    .WithValue(34)
                                                                                                                                    .WithYear(2021)
                                                                                                                                    .WithOccurrence(1)),
                                                                                                           NewDeliveryProfilePeriod(exp =>
                                                                                                                                    exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                    .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                    .WithTypeValue("February")
                                                                                                                                    .WithValue(35)
                                                                                                                                    .WithYear(2021)
                                                                                                                                    .WithOccurrence(0)),
                                                                                                           NewDeliveryProfilePeriod(exp =>
                                                                                                                                    exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                    .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                    .WithTypeValue("March")
                                                                                                                                    .WithValue(36)
                                                                                                                                    .WithYear(2021)
                                                                                                                                    .WithOccurrence(0)),
                                                                                                           NewDeliveryProfilePeriod(exp =>
                                                                                                                                    exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                    .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                    .WithTypeValue("April")
                                                                                                                                    .WithValue(37)
                                                                                                                                    .WithYear(2021)
                                                                                                                                    .WithOccurrence(0)),
                                                                                                           NewDeliveryProfilePeriod(exp =>
                                                                                                                                    exp.WithDistributionPeriod(distributionPeriod)
                                                                                                                                    .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                                    .WithTypeValue("May")
                                                                                                                                    .WithValue(38)
                                                                                                                                    .WithYear(2021)
                                                                                                                                    .WithOccurrence(0))));

            DateTimeOffset expectedActualDateOne   = NewRandomDate();
            DateTimeOffset expectedActualDateTwo   = NewRandomDate();
            DateTimeOffset expectedActualDateThree = NewRandomDate();
            DateTimeOffset expectedActualDateFour  = NewRandomDate();

            FundingDate expectedFundingDate = NewFundingDate(_ => _.WithPatterns(NewFundingDatePattern(fdp =>
                                                                                                       fdp.WithOccurrence(0)
                                                                                                       .WithPeriod("January")
                                                                                                       .WithPeriodYear(2021)
                                                                                                       .WithPaymentDate(expectedActualDateOne)),
                                                                                 NewFundingDatePattern(fdp =>
                                                                                                       fdp.WithOccurrence(0)
                                                                                                       .WithPeriod("February")
                                                                                                       .WithPeriodYear(2021)
                                                                                                       .WithPaymentDate(expectedActualDateTwo)),
                                                                                 NewFundingDatePattern(fdp =>
                                                                                                       fdp.WithOccurrence(0)
                                                                                                       .WithPeriod("April")
                                                                                                       .WithPeriodYear(2021)
                                                                                                       .WithPaymentDate(expectedActualDateThree)),
                                                                                 NewFundingDatePattern(fdp =>
                                                                                                       fdp.WithOccurrence(0)
                                                                                                       .WithPeriod("May")
                                                                                                       .WithPeriodYear(2021)
                                                                                                       .WithPaymentDate(expectedActualDateFour)),
                                                                                 NewFundingDatePattern()));

            GivenTheReProfileRequest(previewRequest, expectedReProfileRequest);
            AndTheReProfileResponse(expectedReProfileRequest, expectedReProfileResponse);
            AndTheFundingDate(previewRequest, expectedFundingDate);

            OkObjectResult response = await WhenTheProfilePatternChangeIsPreviewed(previewRequest) as OkObjectResult;

            response?
            .Value
            .Should()
            .BeEquivalentTo(new [] {
                NewProfileTotal(_ => _.WithOccurrence(0)
                                .WithDistributionPeriod(distributionPeriod)
                                .WithValue(23)
                                .WithYear(2021)
                                .WithPeriodType("CalenderMonth")
                                .WithTypeValue("January")
                                .WithIsPaid(true)
                                .WithInstallmentNumber(1)
                                .WithActualDate(expectedActualDateOne)),
                NewProfileTotal(_ => _.WithOccurrence(1)
                                .WithDistributionPeriod(distributionPeriod)
                                .WithValue(24)
                                .WithYear(2021)
                                .WithPeriodType("CalenderMonth")
                                .WithTypeValue("January")
                                .WithIsPaid(true)
                                .WithInstallmentNumber(2)
                                .WithActualDate(null)),
                NewProfileTotal(_ => _.WithOccurrence(0)
                                .WithDistributionPeriod(distributionPeriod)
                                .WithValue(35)
                                .WithYear(2021)
                                .WithPeriodType("CalenderMonth")
                                .WithTypeValue("February")
                                .WithIsPaid(false)
                                .WithInstallmentNumber(3)
                                .WithActualDate(expectedActualDateTwo)),
                NewProfileTotal(_ => _.WithOccurrence(0)
                                .WithDistributionPeriod(distributionPeriod)
                                .WithValue(36)
                                .WithYear(2021)
                                .WithPeriodType("CalenderMonth")
                                .WithTypeValue("March")
                                .WithIsPaid(false)
                                .WithInstallmentNumber(4)
                                .WithActualDate(null)),
                NewProfileTotal(_ => _.WithOccurrence(0)
                                .WithDistributionPeriod(distributionPeriod)
                                .WithValue(37)
                                .WithYear(2021)
                                .WithPeriodType("CalenderMonth")
                                .WithTypeValue("April")
                                .WithIsPaid(false)
                                .WithInstallmentNumber(5)
                                .WithActualDate(expectedActualDateThree)),
                NewProfileTotal(_ => _.WithOccurrence(0)
                                .WithDistributionPeriod(distributionPeriod)
                                .WithValue(38)
                                .WithYear(2021)
                                .WithPeriodType("CalenderMonth")
                                .WithTypeValue("May")
                                .WithIsPaid(false)
                                .WithInstallmentNumber(6)
                                .WithActualDate(expectedActualDateFour))
            });
        }
 private void AndTheReProfileResponse(ReProfileRequest request,
                                      ReProfileResponse expectedResponse)
 => _profiling.Setup(_ => _.ReProfile(request))
 .ReturnsAsync(new ApiResponse <ReProfileResponse>(HttpStatusCode.OK, expectedResponse));
        public async Task <ApiResponse <ReProfileResponse> > ReProfile(ReProfileRequest request)
        {
            Guard.ArgumentNotNull(request, nameof(request));

            return(await PostAsync <ReProfileResponse, ReProfileRequest>("reprofile", request));
        }
        public async Task AssignProfilePatternKeyForFundingLineWithAlreadyPaidProfilePeriodsUsesReProfileResponseResultsFromApi()
        {
            string fundingLineCode            = NewRandomString();
            string existingProfilePatternKey  = NewRandomString();
            string newProfilePatterFundingKey = NewRandomString();
            string specificationId            = NewRandomString();

            int occurence = NewRandomNumberBetween(1, 100);
            ProfilePeriodType profilePeriodType = NewRandomEnum <ProfilePeriodType>();
            string            typeValue         = NewRandomString();
            int     year = NewRandomNumberBetween(2019, 2021);
            string  distributionPeriodId = NewRandomString();
            decimal carryOverAmount      = NewRandomNumberBetween(1, int.MaxValue);

            GivenTheFundingConfiguration(true);
            ProfilePatternKey profilePatternKey = NewProfilePatternKey(_ => _.WithFundingLineCode(fundingLineCode).WithKey(newProfilePatterFundingKey));
            ProfilePeriod     paidProfilePeriod = NewProfilePeriod(pp => pp
                                                                   .WithDistributionPeriodId(distributionPeriodId)
                                                                   .WithType(profilePeriodType)
                                                                   .WithTypeValue(typeValue)
                                                                   .WithYear(year)
                                                                   .WithOccurence(occurence));

            FundingLine fundingLine = NewFundingLine(_ => _
                                                     .WithFundingLineCode(fundingLineCode)
                                                     .WithDistributionPeriods(
                                                         NewDistributionPeriod(dp => dp
                                                                               .WithDistributionPeriodId(distributionPeriodId)
                                                                               .WithProfilePeriods(paidProfilePeriod))
                                                         ));
            PublishedProviderVersion existingPublishedProviderVersion =
                NewPublishedProviderVersion(ppv => ppv
                                            .WithFundingStreamId(_fundingStreamId)
                                            .WithFundingPeriodId(_fundingPeriodId)
                                            .WithSpecificationId(specificationId)
                                            .WithProfilePatternKeys(
                                                NewProfilePatternKey(_ => _.WithFundingLineCode(fundingLineCode)
                                                                     .WithKey(existingProfilePatternKey)))
                                            .WithFundingLines(fundingLine)
                                            );

            PublishedProvider publishedProvider = NewPublishedProvider(_ => _.WithCurrent(existingPublishedProviderVersion));
            PublishedProviderCreateVersionRequest publishedProviderCreateVersionRequest = NewPublishedProviderCreateVersionRequest(_ =>
                                                                                                                                   _.WithPublishedProvider(publishedProvider)
                                                                                                                                   .WithNewVersion(existingPublishedProviderVersion));
            IEnumerable <ProfileVariationPointer> profileVariationPointers =
                NewProfileVariationPointers(_ => _
                                            .WithFundingLineId(fundingLineCode)
                                            .WithFundingStreamId(_fundingStreamId)
                                            .WithOccurence(occurence)
                                            .WithPeriodType(profilePeriodType.ToString())
                                            .WithTypeValue(typeValue)
                                            .WithYear(year));

            ReProfileRequest  reProfileRequest  = NewReProfileRequest();
            ReProfileResponse reProfileResponse = NewReProfileResponse(_ => _.WithCarryOverAmount(carryOverAmount)
                                                                       .WithDeliveryProfilePeriods(NewDeliveryProfilePeriod(dpp => dpp.WithOccurrence(1)
                                                                                                                            .WithValue(10)
                                                                                                                            .WithYear(2021)
                                                                                                                            .WithTypeValue("JANUARY")
                                                                                                                            .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                            .WithDistributionPeriod("dp1")),
                                                                                                   NewDeliveryProfilePeriod(dpp => dpp.WithOccurrence(1)
                                                                                                                            .WithValue(20)
                                                                                                                            .WithYear(2022)
                                                                                                                            .WithTypeValue("JANUARY")
                                                                                                                            .WithPeriodType(PeriodType.CalendarMonth)
                                                                                                                            .WithDistributionPeriod("dp2"))));

            GivenThePublishedProvider(publishedProvider);
            AndThePublishedProviderCreateVersionRequest(publishedProvider, publishedProviderCreateVersionRequest);
            AndTheNewCreatedPublishedProvider(publishedProvider, publishedProviderCreateVersionRequest);
            AndTheProfileVariationPointers(profileVariationPointers, specificationId);
            AndTheReProfileRequest(existingPublishedProviderVersion.SpecificationId,
                                   existingPublishedProviderVersion.FundingStreamId,
                                   existingPublishedProviderVersion.FundingPeriodId,
                                   existingPublishedProviderVersion.ProviderId,
                                   profilePatternKey.FundingLineCode,
                                   profilePatternKey.Key,
                                   ProfileConfigurationType.Custom,
                                   fundingLine.Value,
                                   reProfileRequest);
            AndTheReProfileResponse(reProfileRequest, reProfileResponse);
            AndTheProfileFundingLines(profilePatternKey);
            AndTheSavePublishedProviderVersionResponse(HttpStatusCode.OK, existingPublishedProviderVersion);
            AndTheUpsertPublishedProviderResponse(HttpStatusCode.OK, publishedProvider);

            IActionResult result = await WhenProfilePatternKeyIsAssigned(_fundingStreamId, _fundingPeriodId, _providerId, profilePatternKey);

            ThenResultShouldBe(result, HttpStatusCode.OK);
            AndProfilePatternKeyWasUpdated(existingPublishedProviderVersion, profilePatternKey);

            fundingLine.DistributionPeriods
            .Count()
            .Should()
            .Be(2);

            DistributionPeriod firstDistributionPeriod = fundingLine.DistributionPeriods.SingleOrDefault(_ => _.DistributionPeriodId == "dp1");

            firstDistributionPeriod.ProfilePeriods
            .Should()
            .BeEquivalentTo(NewProfilePeriod(_ => _.WithAmount(10)
                                             .WithTypeValue("JANUARY")
                                             .WithDistributionPeriodId("dp1")
                                             .WithType(ProfilePeriodType.CalendarMonth)
                                             .WithOccurence(1)
                                             .WithYear(2021)));

            DistributionPeriod secondDistributionPeriod = fundingLine.DistributionPeriods.SingleOrDefault(_ => _.DistributionPeriodId == "dp2");

            secondDistributionPeriod.ProfilePeriods
            .Should()
            .BeEquivalentTo(NewProfilePeriod(_ => _.WithAmount(20)
                                             .WithTypeValue("JANUARY")
                                             .WithDistributionPeriodId("dp2")
                                             .WithType(ProfilePeriodType.CalendarMonth)
                                             .WithOccurence(1)
                                             .WithYear(2022)));

            existingPublishedProviderVersion.CarryOvers
            .Should()
            .BeEquivalentTo(NewCarryOver(_ => _.WithAmount(carryOverAmount)
                                         .WithFundingLineCode(fundingLineCode)
                                         .WithType(ProfilingCarryOverType.CustomProfile)));

            AndThePublishedProviderWasProcessed(publishedProvider);
            AndTheProfilingAuditWasUpdatedForTheFundingLine(publishedProvider, profilePatternKey.FundingLineCode, _author);
        }