private CreditCalculatorResultVM GetResultsForModel(CreditCalcResults res)
        {
            CreditCalculatorResultVM mRes = new CreditCalculatorResultVM();

            mRes.AnnualPercentageRate = res.AnnualPercentageRate;

            mRes.TotalFees         = res.TotalFees;
            mRes.TotalInstallments = res.TotalInstallments;
            mRes.TotalInstallmentsWithTotalFeesAndRates = res.TotalInstallmentsWithTotalFeesAndRates;
            mRes.TotalRates          = res.TotalRates;
            mRes.MonthlyInstallments = new List <MonthlyResultVM>();

            foreach (var m in res.MonthlyInstallments)
            {
                MonthlyResultVM mcalc = new MonthlyResultVM();
                mcalc.Date                 = m.Date;
                mcalc.Fees                 = m.Fees;
                mcalc.Installment          = m.Installment;
                mcalc.PrincipalRemainder   = m.PrincipalRemainder;
                mcalc.PrinicpalInstallment = m.PrinicpalInstallment;
                mcalc.RateInstallment      = m.RateInstallment;
                mcalc.RowNumber            = m.RowNumber;
                mcalc.TotalInstallment     = m.TotalInstallment;
                mRes.MonthlyInstallments.Add(mcalc);
            }

            return(mRes);
        }
        /// <summary>
        /// Смята резултатите на финансовия калкулатор
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public CreditCalcResults Calculate(CreditCalcParams p)
        {
            IsParamsValid(p);
            CreditCalcResults res = null;

            if (p.IsAnnuityInstallments)
            {
                res = CalculateAnnuity(p);
            }
            else
            {
                res = CalculateDescending(p);
            }
            APRCalculator aprCalc = new APRCalculator(p.Amount);

            foreach (var m in res.MonthlyInstallments)
            {
                if (m.RowNumber == 0)
                {
                    aprCalc.AddInstalment(m.Fees, 0);
                }
                else
                {
                    aprCalc.AddInstalment(m.TotalInstallment, 365.25M / 12M * m.RowNumber);
                }
                res.TotalFees         += m.Fees;
                res.TotalRates        += m.RateInstallment;
                res.TotalInstallments += m.Installment;
            }
            res.AnnualPercentageRate = aprCalc.Calculate();
            res.TotalInstallmentsWithTotalFeesAndRates = res.TotalInstallments + res.TotalFees;
            return(res);
        }
        public ActionResult FinanceCalculator(CreditCalculatorParamsVM model)
        {
            if (!model.IsModelValid(ModelState))
            {
                return(PartialView("_InvalidCreditParamsPartial", model));
            }

            var parameters = GetParamsFromModel(model);
            CreditCalcResults        creditResult          = this.calculatorService.CalculateCredit(parameters);
            CreditCalculatorResultVM creditViewModelResult = GetResultsForModel(creditResult);

            return(PartialView("_CreditResultsPartial", creditViewModelResult));
        }
        /// <summary>
        /// Функция, пресмятаща резултатите от погасителния план при намаляващи месечни вноски
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private CreditCalcResults CalculateDescending(CreditCalcParams p)
        {
            CreditCalcResults res = new CreditCalcResults();

            res.MonthlyInstallments = new List <MonthlyResult>();
            res.MonthlyInstallments.Add(GetZeroMonth(p));
            decimal principalInstallment = Math.Round(p.Amount / (p.Period - (p.GratisPeriod ?? 0)), 2);

            for (int i = 1; i <= p.Period; i++)
            {
                MonthlyResult m = new MonthlyResult();
                if (i == 1)
                {
                    m.PrincipalRemainder = p.Amount;
                }
                else
                {
                    m.PrincipalRemainder = res.MonthlyInstallments[i - 1].CurrentPrincipallRemainder();
                }
                m.RowNumber = i;
                m.Date      = DateTime.Now.AddMonths(i);
                bool isInPromotion = (i <= (p.PromotionPeriod ?? 0));
                bool isInGratis    = (i <= (p.GratisPeriod ?? 0));
                m.RateInstallment = Math.Round((decimal)(m.PrincipalRemainder * (isInPromotion ? p.PromotionRate : p.Rate) / 100) / 12, 2);
                if (isInGratis)
                {
                    m.PrinicpalInstallment = 0;
                }
                else
                {
                    m.PrinicpalInstallment = (i == p.Period) ? Math.Round(p.Amount - (i - 1 - (p.GratisPeriod ?? 0)) * principalInstallment, 2) : principalInstallment;
                }
                m.Installment = (isInGratis ? 0 : m.PrinicpalInstallment) + m.RateInstallment;
                if (i % 12 == 1 && i != 1)
                {
                    m.Fees = MonthlyFeesCalc(p, m) + AnnualFeesCalc(p, m);
                }
                else
                {
                    m.Fees = MonthlyFeesCalc(p, m);
                }
                m.TotalInstallment = m.Installment + m.Fees;
                res.MonthlyInstallments.Add(m);
            }
            return(res);
        }
        /// <summary>
        /// Функция, пресмятаща резултатите от погасителния план при анюитетни вноски
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private CreditCalcResults CalculateAnnuity(CreditCalcParams p)
        {
            CreditCalcResults res = new CreditCalcResults();

            res.MonthlyInstallments = new List <MonthlyResult>();
            res.MonthlyInstallments.Add(GetZeroMonth(p));
            bool    hasPromotion           = p.PromotionPeriod.HasValue;
            decimal pmtPromotional         = hasPromotion ? PMT(p.PromotionRate.Value, p.Period - (p.GratisPeriod ?? 0), p.Amount) : 0;
            decimal pmtNormal              = hasPromotion ? 0 : PMT(p.Rate, p.Period - (p.GratisPeriod ?? 0), p.Amount);
            decimal promotionalInstallment = 0;

            for (int i = 1; i <= p.Period; i++)
            {
                MonthlyResult m = new MonthlyResult();
                if (i == 1)
                {
                    m.PrincipalRemainder = p.Amount;
                }
                else
                {
                    m.PrincipalRemainder = res.MonthlyInstallments[i - 1].CurrentPrincipallRemainder();
                }
                m.RowNumber = i;
                m.Date      = DateTime.Now.AddMonths(i);
                if (i <= (p.PromotionPeriod ?? 0))
                {
                    m.RateInstallment = Math.Round((decimal)(m.PrincipalRemainder * p.PromotionRate / 100) / 12, 2);
                    if (i <= (p.GratisPeriod ?? 0))
                    {
                        m.Installment = m.RateInstallment;
                    }
                    else
                    {
                        m.Installment = pmtPromotional;
                    }
                    m.PrinicpalInstallment  = m.Installment - m.RateInstallment;
                    promotionalInstallment += m.PrinicpalInstallment;
                }
                else
                {
                    m.RateInstallment = Math.Round((decimal)(m.PrincipalRemainder * p.Rate / 100) / 12, 2);
                    if (i <= (p.GratisPeriod ?? 0))
                    {
                        m.Installment = m.RateInstallment;
                    }
                    else
                    {
                        if (pmtNormal == 0)
                        {
                            pmtNormal = PMT(p.Rate, p.Period - Math.Max(p.PromotionPeriod.Value, p.GratisPeriod ?? 0), p.Amount - promotionalInstallment);
                        }
                        if (i == p.Period)
                        {
                            pmtNormal = PMT(p.Rate, 1, m.PrincipalRemainder);
                        }
                        m.Installment = pmtNormal;
                    }
                    m.PrinicpalInstallment = m.Installment - m.RateInstallment;
                }
                if (i % 12 == 1 && i != 1)
                {
                    m.Fees = MonthlyFeesCalc(p, m) + AnnualFeesCalc(p, m);
                }
                else
                {
                    m.Fees = MonthlyFeesCalc(p, m);
                }
                m.TotalInstallment = m.Installment + m.Fees;
                res.MonthlyInstallments.Add(m);
            }
            return(res);
        }