private CreditAccountStateDto CloseAccount(CreditAccountDto account, CreditAccountStateDto latestCreditAccountState)
        {
            var accountCurrency = account.Currency;

            account.IsClosed = true;
            var updateCreditAccountCommand = new UpdateModelCommand <CreditAccountDto>()
            {
                ModelDto = account
            };

            _creditAccountCommandRepository.Execute(updateCreditAccountCommand);

            var zeroPrice = new PriceDto()
            {
                Currency = accountCurrency,
                Value    = 0m
            };
            var closingCreditAccountState = new CreditAccountStateDto()
            {
                CreditAccount           = account,
                FinesForOverdue         = zeroPrice,
                InterestCounted         = zeroPrice,
                MainDebtRemain          = zeroPrice,
                RemainDebt              = zeroPrice,
                Month                   = latestCreditAccountState.Month + 1,
                TotalInterestSumNotPaid = zeroPrice
            };
            var newCreditAccountStateCommand = new CreateModelCommand <CreditAccountStateDto>()
            {
                ModelDto = closingCreditAccountState
            };

            _creditAccountStateCommandRepository.Execute(newCreditAccountStateCommand);
            return(closingCreditAccountState);
        }
        private CreditAccountStateDto UpdateFinesAndGetAccountState(int creditAccountId, DateTime specifiedDate)
        {
            var query = new ModelQuery()
            {
                Id = creditAccountId
            };
            var account = _creditAccountQueryRepository.Handle(query);
            var latestCreditAccountStateQuery = new ActualCreditAccountStateQuery()
            {
                Id = account.Id
            };
            var latestCreditAccountState   = _creditAccountQueryRepository.Handle(latestCreditAccountStateQuery);
            var creditAccountPaymentsQuery = new CreditPaymentsQuery()
            {
                CreditAccountId = account.Id
            };
            var latestCreditAccountStateDate = account.AgreementDate.AddMonths(latestCreditAccountState.Month);
            var accountPayments         = _creditAccountQueryRepository.Handle(creditAccountPaymentsQuery);
            var paymentsForLatestPeriod = accountPayments.Where(p => latestCreditAccountStateDate < p.Timestamp);
            var accountCurrency         = account.Currency;
            // S
            var sumPaidForLatestPeriod = paymentsForLatestPeriod.Sum(p => p.PaymentSum.Value);

            if (latestCreditAccountState.RemainDebt.Value <= sumPaidForLatestPeriod)
            {
                return(CloseAccount(account, latestCreditAccountState));
            }

            // Z
            var totalDebtRemaining = latestCreditAccountState.RemainDebt.Value;
            // A
            var debtForMonth = GetDebtForMonth(latestCreditAccountState);

            if (latestCreditAccountState.MainDebtRemain.Value > sumPaidForLatestPeriod)
            {
                var finesForOverdue = latestCreditAccountState.FinesForOverdue;
                finesForOverdue.Value += account.CreditType.FineInterest *
                                         latestCreditAccountState.MainDebtRemain.Value;
                var updateFinesCommand = new UpdateModelCommand <PriceDto>()
                {
                    ModelDto = finesForOverdue
                };
                _priceCommandRepository.Execute(updateFinesCommand);

                var totalDebt = latestCreditAccountState.RemainDebt;
                totalDebt.Value += finesForOverdue.Value;
                var updateTotalDebtCommand = new UpdateModelCommand <PriceDto>()
                {
                    ModelDto = totalDebt
                };
                _priceCommandRepository.Execute(updateTotalDebtCommand);
            }
            if (ShouldAccountUpdate(account, specifiedDate))
            {
                var previousFinesForOverdue = latestCreditAccountState.FinesForOverdue;
                // B
                var interestForMonth     = (decimal)account.CreditType.InterestRate / 12 * totalDebtRemaining;
                var totalInterestNotPaid = latestCreditAccountState.TotalInterestSumNotPaid.Value;

                var newTotalDebtRemaining   = totalDebtRemaining;
                var newTotalInterestNotPaid = totalInterestNotPaid;
                var mainDebtRemain          = latestCreditAccountState.MainDebtRemain.Value;
                if (sumPaidForLatestPeriod < debtForMonth + mainDebtRemain)
                {
                    newTotalDebtRemaining   -= sumPaidForLatestPeriod;
                    newTotalDebtRemaining   += debtForMonth;
                    newTotalInterestNotPaid += interestForMonth;
                    mainDebtRemain           = debtForMonth + mainDebtRemain - sumPaidForLatestPeriod;
                }
                else if (sumPaidForLatestPeriod <
                         debtForMonth + mainDebtRemain + totalInterestNotPaid + interestForMonth)
                {
                    newTotalDebtRemaining   -= debtForMonth + mainDebtRemain;
                    newTotalInterestNotPaid += interestForMonth -
                                               (sumPaidForLatestPeriod - debtForMonth - mainDebtRemain);
                    mainDebtRemain = 0m;
                }
                else
                {
                    newTotalInterestNotPaid = 0m;
                    newTotalDebtRemaining  -= sumPaidForLatestPeriod - interestForMonth - totalInterestNotPaid;
                    mainDebtRemain          = 0m;
                }

                var newCreditAccountState = new CreditAccountStateDto()
                {
                    CreditAccount   = account,
                    Month           = latestCreditAccountState.Month + 1,
                    FinesForOverdue = new PriceDto()
                    {
                        Currency = accountCurrency,
                        Value    = previousFinesForOverdue.Value
                    },
                    InterestCounted = new PriceDto()
                    {
                        Currency = accountCurrency,
                        Value    = interestForMonth
                    },
                    RemainDebt = new PriceDto()
                    {
                        Currency = accountCurrency,
                        Value    = Math.Max(newTotalDebtRemaining + previousFinesForOverdue.Value, 0m)
                    },
                    TotalInterestSumNotPaid = new PriceDto()
                    {
                        Currency = accountCurrency,
                        Value    = newTotalInterestNotPaid
                    },
                    MainDebtRemain = new PriceDto()
                    {
                        Currency = accountCurrency,
                        Value    = mainDebtRemain
                    }
                };
                return(newCreditAccountState);
            }
            return(null);
        }
        private static decimal GetDebtForMonth(CreditAccountStateDto accountState)
        {
            var totalDebtRemaining = accountState.RemainDebt.Value;

            return(totalDebtRemaining / (accountState.CreditAccount.TotalMonthDuration - accountState.Month));
        }