public async Task <ProjectionSource> GetOriginalProjectionSource(long employerAccountId, ProjectionSource currentProjectionSource)
        {
            _telemetry.AddEmployerAccountId(employerAccountId);
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            var messageProjectionSource = currentProjectionSource;

            if (messageProjectionSource == ProjectionSource.Commitment)
            {
                var previousProjection = await _accountProjectionRepository.Get(employerAccountId);

                var projectionGenerationType = previousProjection?.FirstOrDefault()?.ProjectionGenerationType;
                if (projectionGenerationType != null)
                {
                    messageProjectionSource = projectionGenerationType == ProjectionGenerationType.LevyDeclaration
                        ? ProjectionSource.LevyDeclaration : ProjectionSource.PaymentPeriodEnd;
                }
            }
            stopwatch.Stop();
            _telemetry.TrackDuration("GetOriginalProjectionSource", stopwatch.Elapsed);

            return(messageProjectionSource);
        }
        public async Task Handle(PaymentCreatedMessage message, string allowProjectionsEndpoint)
        {
            if (message.EarningDetails == null)
            {
                throw new InvalidOperationException($"Invalid payment created message. Earning details is null so cannot create commitment data. Employer account: {message.EmployerAccountId}, payment id: {message.Id}");
            }

            _telemetry.AddEmployerAccountId(message.EmployerAccountId);
            _telemetry.AddProperty("Payment Id", message.Id);
            _telemetry.AddProperty("Apprenticeship Id", message.ApprenticeshipId.ToString());
            var stopwatch = new Stopwatch();

            stopwatch.Start();

            var commitment = await _repository.Get(message.EmployerAccountId, message.ApprenticeshipId);

            var commitmentModel = _paymentMapper.MapToCommitment(message);

            if (!commitment.RegisterCommitment(commitmentModel))
            {
                _logger.Info($"Not storing the employer commitment. Employer: {message.EmployerAccountId}, ApprenticeshipId: {message.ApprenticeshipId}, payment id: {message.Id}");
                return;
            }

            _logger.Info($"Now storing the employer commitment. Employer: {message.EmployerAccountId}, ApprenticeshipId: {message.ApprenticeshipId}, payment id: {message.Id}");
            await _repository.Store(commitment);

            _logger.Info($"Finished adding the employer commitment. Employer: {message.EmployerAccountId}, ApprenticeshipId: {message.ApprenticeshipId}, payment id: {message.Id}");
            _queueService.SendMessageWithVisibilityDelay(message, allowProjectionsEndpoint);
            stopwatch.Stop();

            _telemetry.TrackDuration("Stored Commitment", stopwatch.Elapsed);
        }
        public async Task Handle(GenerateAccountProjectionCommand message)
        {
            _logger.Debug($"Getting balances for account: {message.EmployerAccountId}");
            _telemetry.AddEmployerAccountId(message.EmployerAccountId);
            var currentBalance = await _currentBalanceRepository.Get(message.EmployerAccountId);


            var refreshBalance = await _accountProjectionService.GetOriginalProjectionSource(message.EmployerAccountId,
                                                                                             message.ProjectionSource) == ProjectionSource.PaymentPeriodEnd ? currentBalance.RefreshBalance(true, true) : currentBalance.RefreshBalance(true);

            if (!await refreshBalance)
            {
                _telemetry.TrackEvent("Account Balance Already Refreshed");
                _logger.Warn($"Failed to refresh the account balance for account {message.EmployerAccountId}.  It's possible the account has been refreshed recently.");
                return;
            }
            await _currentBalanceRepository.Store(currentBalance);

            _telemetry.TrackEvent("Refreshed Account Balance");
            _logger.Info($"Finished updating recorded balance for account: {message.EmployerAccountId}");
        }
        public async Task Handle(PaymentCreatedMessage paymentCreatedMessage, string allowProjectionsEndpoint)
        {
            _telemetry.AddEmployerAccountId(paymentCreatedMessage.EmployerAccountId);
            _telemetry.AddProperty("Payment Id", paymentCreatedMessage.Id);
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            var employerPayment = _mapper.MapToPayment(paymentCreatedMessage);

            _logger.Debug($"Now storing the employer payment. Employer: {employerPayment.EmployerAccountId}, Payment Id: {employerPayment.ExternalPaymentId}, Collection period: {employerPayment.CollectionPeriod.Year} - {employerPayment.CollectionPeriod.Month}, Delivery period: {employerPayment.DeliveryPeriod.Year} - {employerPayment.DeliveryPeriod.Month}");
            var payment = await _repository.Get(paymentCreatedMessage.EmployerAccountId, paymentCreatedMessage.Id);

            payment.RegisterPayment(employerPayment);
            await _repository.StorePayment(payment);

            _logger.Info($"Finished adding the employer payment. Employer: {employerPayment.EmployerAccountId}, Payment Id: {employerPayment.ExternalPaymentId}, Collection period: {employerPayment.CollectionPeriod.Year} - {employerPayment.CollectionPeriod.Month}, Delivery period: {employerPayment.DeliveryPeriod.Year} - {employerPayment.DeliveryPeriod.Month}");

            stopwatch.Stop();
            _telemetry.TrackDuration("Store Payment", stopwatch.Elapsed);
            _telemetry.TrackEvent("Stored Payment");
        }
        public async Task Handle(GenerateAccountProjectionCommand message)
        {
            _telemetry.AddEmployerAccountId(message.EmployerAccountId);
            var stopwatch = new Stopwatch();

            stopwatch.Start();
            var projections = await _accountProjectionRepository.InitialiseProjection(message.EmployerAccountId);

            var startDate = new DateTime(
                GetValue(message.StartPeriod?.Year, DateTime.Today.Year),
                GetValue(message.StartPeriod?.Month, DateTime.Today.Month),
                GetValue(message.StartPeriod?.Day, DateTime.Today.Day));

            var messageProjectionSource = await _accountProjectionService.GetOriginalProjectionSource(message.EmployerAccountId, message.ProjectionSource);

            if (messageProjectionSource == ProjectionSource.LevyDeclaration)
            {
                projections.BuildLevyTriggeredProjections(startDate, _config.NumberOfMonthsToProject);
            }
            else
            {
                projections.BuildPayrollPeriodEndTriggeredProjections(startDate, _config.NumberOfMonthsToProject);
            }

            var expiringFunds = await _expiredFundsService.GetExpiringFunds(projections.Projections, message.EmployerAccountId, messageProjectionSource, startDate);

            if (expiringFunds.Any())
            {
                projections.UpdateProjectionsWithExpiredFunds(expiringFunds);
            }

            await _accountProjectionRepository.Store(projections);

            stopwatch.Stop();
            _telemetry.TrackDuration("BuildAccountProjection", stopwatch.Elapsed);
        }