public ActionResult Upgrade(string id)
        {
            var mainContract = this.DbPractice.AccountContract;

            // because each plan has a different set of features,
            // this is going to be used by the view, to define the partial page that will be shown
            this.ViewBag.CurrentContractName = mainContract.SYS_ContractType.UrlIdentifier;

            if (mainContract.SYS_ContractType.IsTrial)
            {
                var viewModel = new ChangeContractViewModel
                {
                    ContractUrlId       = id,
                    CurrentDoctorsCount = this.DbPractice.Users.Count(x => x.DoctorId != null),
                };

                return(View(viewModel));
            }

            return(this.HttpNotFound());
        }
        public ActionResult Upgrade(string id, ChangeContractViewModel viewModel)
        {
            var mainContract = this.DbPractice.AccountContract;

            // because each plan has a different set of features,
            // this is going to be used by the view, to define the partial page that will be shown
            this.ViewBag.CurrentContractName = mainContract.SYS_ContractType.UrlIdentifier;

            if (!viewModel.AcceptedByUser)
            {
                this.ModelState.AddModelError(
                    () => viewModel.AcceptedByUser, "A caixa de checagem de aceitação do contrato precisa ser marcada para concluir o processo.");
            }

            if (mainContract.SYS_ContractType.IsTrial)
            {
                if (id == "ProfessionalPlan")
                {
                    // calculating values to see if the submited values are correct
                    var unitPrice = Bus.Pro.DOCTOR_PRICE;
                    Func <double, double, double> integ       = (x, n) => Math.Pow(n, x) / Math.Log(n);
                    Func <double, double, double> integ0to    = (x, n) => (integ(x, n) - integ(0, n));
                    Func <double, double>         priceFactor = x => x * (1.0 - 0.1 * integ0to((x - 1) / 3.0, 0.75));
                    Func <double, double>         price       = (extraDoctors) => Math.Round(priceFactor(extraDoctors) * unitPrice * 100) / 100;

                    var dicValues = new Dictionary <string, decimal>(StringComparer.InvariantCultureIgnoreCase)
                    {
                        { "MONTH", Bus.Pro.PRICE_MONTH },
                        { "3-MONTHS", Bus.Pro.PRICE_QUARTER },
                        { "6-MONTHS", Bus.Pro.PRICE_SEMESTER },
                        { "12-MONTHS", Bus.Pro.PRICE_YEAR },
                    };

                    var dicDiscount = new Dictionary <string, decimal>(StringComparer.InvariantCultureIgnoreCase)
                    {
                        { "MONTH", 0 },
                        { "3-MONTHS", Bus.Pro.DISCOUNT_QUARTER },
                        { "6-MONTHS", Bus.Pro.DISCOUNT_SEMESTER },
                        { "12-MONTHS", Bus.Pro.DISCOUNT_YEAR },
                    };

                    var periodSizesDic = new Dictionary <string, int>(StringComparer.InvariantCultureIgnoreCase)
                    {
                        { "MONTH", 1 },
                        { "3-MONTHS", 3 },
                        { "6-MONTHS", 6 },
                        { "12-MONTHS", 12 },
                    };

                    var dicount      = 1m - dicDiscount[viewModel.PaymentModelName] / 100m;
                    var accountValue = dicValues[viewModel.PaymentModelName];
                    var doctorsValueWithoutDiscount = (decimal)Math.Round(price(viewModel.DoctorCount - 1))
                                                      * periodSizesDic[viewModel.PaymentModelName];

                    var finalValue = accountValue + doctorsValueWithoutDiscount * dicount;

                    var finalValueWithoutDiscount =
                        Bus.Pro.PRICE_MONTH * periodSizesDic[viewModel.PaymentModelName]
                        + doctorsValueWithoutDiscount;

                    // tolerance of R$ 0.10 in the final value... maybe the browser could not make the calculations correctly,
                    // but we must use that value, since it is the value that the user saw
                    if (Math.Abs(finalValue - viewModel.FinalValue) >= 0.10m)
                    {
                        this.ModelState.AddModelError(
                            () => viewModel.FinalValue,
                            "Seu browser apresentou um defeito no cálculo do valor final. Não foi possível processar sua requisição de upgrade.");
                    }

                    viewModel.ContractUrlId       = id;
                    viewModel.CurrentDoctorsCount = this.DbPractice.Users.Count(x => x.DoctorId != null);

                    if (this.ModelState.IsValid)
                    {
                        // sending e-mail to [email protected]
                        // to remember us to send the payment request
                        var emailViewModel = new InternalUpgradeEmailViewModel(this.DbUser, viewModel);
                        var toAddress      = new MailAddress("*****@*****.**", this.DbUser.Person.FullName);
                        var mailMessage    = this.CreateEmailMessagePartial("InternalUpgradeEmail", toAddress, emailViewModel);
                        this.SendEmailAsync(mailMessage).ContinueWith(t =>
                        {
                            // observing exception so that it is not raised
                            var ex = t.Exception;

                            // todo: should do something when e-mail is not sent
                            // 1) use a schedule table to save a serialized e-mail, and then send it later
                            // 2) log a warning message somewhere stating that this e-mail was not sent
                            // send e-mail again is not an option, SendEmailAsync already tries a lot of times
                        });

                        var utcNow = this.GetUtcNow();

                        // terminating the previous contract
                        var currentContract = this.DbPractice.AccountContract;
                        currentContract.EndDate = utcNow;

                        // setting up the professional contract
                        this.DbPractice.AccountContract = new AccountContract
                        {
                            PracticeId   = this.DbPractice.Id,
                            IssuanceDate = this.GetUtcNow(),

                            ContractTypeId = (int)ContractTypes.ProfessionalContract,
                            IsTrial        = false,
                            StartDate      = utcNow, // contract starts NOW... without delays
                            EndDate        = null,   // this is an unlimited contract
                            CustomText     = viewModel.WholeUserAgreement,

                            DoctorsLimit  = viewModel.DoctorCount,
                            PatientsLimit = null,     // there is no patients limit anymore

                            // billing informations
                            BillingAmount         = viewModel.FinalValue,
                            BillingDiscountAmount = finalValueWithoutDiscount - viewModel.FinalValue,
                            BillingDueDay         = viewModel.InvoceDueDayOfMonth,
                            BillingPeriodCount    = null, // no limit... this contract is valid forever
                            BillingPeriodSize     = periodSizesDic[viewModel.PaymentModelName],
                            BillingPeriodType     = "M",  // same as date-time formatter 'd' for days, 'M' for months, 'y' for years
                            BillingPaymentMethod  = "PayPal Invoice",
                        };

                        this.db.SaveChanges();

                        return(this.RedirectToAction("UpgradeDone"));
                    }

                    return(this.View(viewModel));
                }
            }

            return(this.HttpNotFound());
        }