public async Task <ReadOnlyCollection <FundingSourcePaymentEvent> > ProcessReceiverTransferPayment(ProcessUnableToFundTransferFundingSourcePayment message)
        {
            if (!message.AccountId.HasValue)
            {
                throw new InvalidOperationException($"Invalid ProcessUnableToFundTransferFundingSourcePayment event.  No account id populated on message.  Event id: {message.EventId}");
            }

            paymentLogger.LogDebug($"Converting the unable to fund transfer payment to a levy payment.  Event id: {message.EventId}, account id: {message.AccountId}, job id: {message.JobId}");
            var requiredPayment = mapper.Map <CalculatedRequiredLevyAmount>(message);

            paymentLogger.LogVerbose("Mapped ProcessUnableToFundTransferFundingSourcePayment to CalculatedRequiredLevyAmount");
            var payments = new List <FundingSourcePaymentEvent>();
            var monthEndStartedForThisAccount = await monthEndCache.TryGet(CacheKeys.MonthEndStartedForThisAccountCacheKey);

            if (!monthEndStartedForThisAccount.HasValue || !monthEndStartedForThisAccount.Value)
            {
                paymentLogger.LogDebug("Month end has not been started yet so adding the payment to the cache.");
                await AddRequiredPayment(requiredPayment);
            }
            else
            {
                var levyAccountCacheItem = await levyAccountCache.TryGet(CacheKeys.LevyBalanceKey, CancellationToken.None)
                                           .ConfigureAwait(false);

                if (!levyAccountCacheItem.HasValue)
                {
                    throw new InvalidOperationException($"The last levy account balance has not been stored in the reliable for account: {message.AccountId}");
                }

                levyBalanceService.Initialise(levyAccountCacheItem.Value.Balance, levyAccountCacheItem.Value.TransferAllowance);
                paymentLogger.LogDebug("Service has finished month end processing so now generating the payments for the ProcessUnableToFundTransferFundingSourcePayment event.");
                payments.AddRange(fundingSourcePaymentEventBuilder.BuildFundingSourcePaymentsForRequiredPayment(requiredPayment, message.AccountId.Value, message.JobId));
                var remainingBalance = mapper.Map <LevyAccountModel>(levyAccountCacheItem.Value);
                remainingBalance.Balance           = levyBalanceService.RemainingBalance;
                remainingBalance.TransferAllowance = levyBalanceService.RemainingTransferAllowance;
                await levyAccountCache.AddOrReplace(CacheKeys.LevyBalanceKey, remainingBalance);
            }
            paymentLogger.LogInfo($"Finished processing the ProcessUnableToFundTransferFundingSourcePayment. Event id: {message.EventId}, account id: {message.AccountId}, job id: {message.JobId}");
            return(payments.AsReadOnly());
        }
        public async Task <ReadOnlyCollection <FundingSourcePaymentEvent> > HandleMonthEnd(long employerAccountId, long jobId, CollectionPeriod collectionPeriod)
        {
            var levyAccount = await levyFundingSourceRepository.GetLevyAccount(employerAccountId);

            levyBalanceService.Initialise(levyAccount.Balance, levyAccount.TransferAllowance);

            var orderedRequiredLevyPayments = await GetOrderedCalculatedRequiredLevyAmounts(employerAccountId, collectionPeriod).ConfigureAwait(false);

            logger.LogDebug($"Processing {orderedRequiredLevyPayments.Count} required payments, levy balance {levyAccount.Balance}, account {employerAccountId}, job id {jobId}");
            var fundingSourceEvents = new List <FundingSourcePaymentEvent>();

            fundingSourceEvents.AddRange(orderedRequiredLevyPayments.SelectMany(payment =>
                                                                                fundingSourcePaymentEventBuilder.BuildFundingSourcePaymentsForRequiredPayment(payment, employerAccountId, jobId)));

            logger.LogDebug($"Created {fundingSourceEvents.Count} payments - {GetFundsDebugString(fundingSourceEvents)}, account {employerAccountId}, job id {jobId}");

            levyAccount.Balance           = levyBalanceService.RemainingBalance;
            levyAccount.TransferAllowance = levyBalanceService.RemainingTransferAllowance;
            await levyAccountCache.AddOrReplace(CacheKeys.LevyBalanceKey, levyAccount);

            logger.LogInfo($"Finished generating levy and/or co-invested payments for the account: {employerAccountId}, number of payments: {fundingSourceEvents.Count}.");
            return(fundingSourceEvents.AsReadOnly());
        }