public InternalUpgradeEmailViewModel(User user, ChangeContractViewModel upgradeData)
     : base(user)
 {
     this.Upgrade = upgradeData;
 }
 public InternalUpgradeEmailViewModel(User user, ChangeContractViewModel upgradeData)
     : base(user)
 {
     this.Upgrade = upgradeData;
 }
        public ActionResult SetAccountPaymentInfo(ChangeContractViewModel viewModel)
        {
            var mainContract = dbPractice.AccountContract;

            string planId;
            Bus.ContractToPlan.TryGetValue(dbPractice.AccountContract.SYS_ContractType.UrlIdentifier, out planId);

            // 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.IsPartialBillingInfo)
            {
                if (planId == "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 = planId;
                    viewModel.CurrentDoctorsCount = 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
                        //});

                        // changing the partial contract, to match the new billing settings
                        // Note: the contract will still be partial, and only when the user pays
                        //     the partial flag will be removed and the StartDate will be defined.
                        mainContract.CustomText = viewModel.WholeUserAgreement;
                        mainContract.DoctorsLimit = viewModel.DoctorCount;

                        mainContract.BillingAmount = finalValueWithoutDiscount;
                        mainContract.BillingDiscountAmount = finalValueWithoutDiscount - viewModel.FinalValue;
                        mainContract.BillingDueDay = viewModel.InvoceDueDayOfMonth;
                        mainContract.BillingPeriodCount = null;
                        mainContract.BillingPeriodSize = periodSizesDic[viewModel.PaymentModelName];
                        mainContract.BillingPeriodType = "M";
                        mainContract.BillingPaymentMethod = "PayPal Invoice";

                        this.db.SaveChanges();

                        // Creating the first billing
                        var utcNow = this.GetUtcNow();
                        var localNow = PracticeController.ConvertToLocalDateTime(dbPractice, utcNow);

                        Billing billing = null;
                        var idSet = string.Format(
                            "CEREB.{1}{2}.{0}",
                            localNow.Year,
                            mainContract.BillingPeriodSize,
                            mainContract.BillingPeriodType);

                        billing = db.Billings.SingleOrDefault(b => b.PracticeId == dbPractice.Id
                            && b.MainAccountContractId == dbPractice.ActiveAccountContractId
                            && b.ReferenceDate == null);

                        if (billing == null)
                        {
                            billing = new Billing
                            {
                                PracticeId = dbPractice.Id,
                                AfterDueMonthlyTax = 1.00m, // 1%
                                AfterDueTax = 2.00m, // 2%
                                IssuanceDate = utcNow,
                                MainAmount = (decimal)mainContract.BillingAmount,
                                MainDiscount = (decimal)mainContract.BillingDiscountAmount,
                                DueDate = PracticeController.ConvertToUtcDateTime(dbPractice, localNow.AddDays(10)),
                                IdentitySetName = idSet,
                                IdentitySetNumber = db.Billings.Count(b => b.PracticeId == dbPractice.Id && b.IdentitySetName == idSet) + 1,
                                ReferenceDate = PracticeController.ConvertToUtcDateTime(dbPractice, null),
                                ReferenceDateEnd = PracticeController.ConvertToUtcDateTime(dbPractice, null),
                                MainAccountContractId = dbPractice.ActiveAccountContractId.Value,
                            };

                            db.Billings.AddObject(billing);
                        }

                        this.db.SaveChanges();

                        // Using PayPal API to start an Express Checkout operation, and then redirecting user to PayPal.
                        var operation = new PayPalSetExpressCheckoutOperation();
                        ConfigAccountController.FillOperationDetails(operation, dbPractice, mainContract, billing);
                        var practice = this.dbPractice.UrlIdentifier;
                        operation.PaymentRequests[0].NotifyUrl = UseExternalIpIfDebug(this.Url.ActionAbsolute("PayPalNotification", new { practice }));
                        var opResult = this.SetExpressCheckout(operation, "PayPalConfirm", "PayPalCancel", new { practice });

                        if (opResult.Errors != null && opResult.Errors.Any())
                            return new StatusCodeResult(HttpStatusCode.InternalServerError, opResult.Errors[0].LongMessage);

                        return this.RedirectToCheckout(opResult);
                    }

                    return this.View(viewModel);
                }
            }

            return this.HttpNotFound();
        }
        public ActionResult SetAccountPaymentInfo()
        {
            var contract = dbPractice.AccountContract;
            var periodTypesDic = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase)
                {
                    { "1M", "MONTH" },
                    { "3M", "3-MONTHS" },
                    { "6M", "6-MONTHS" },
                    { "12M", "12-MONTHS" },
                };
            string paymentModelName;
            periodTypesDic.TryGetValue(string.Format("{0}{1}", contract.BillingPeriodSize, contract.BillingPeriodType), out paymentModelName);

            var viewModel = new ChangeContractViewModel
                {
                    ContractUrlId = "ProfessionalPlan",
                    CurrentDoctorsCount = dbPractice.Users.Count(x => x.DoctorId != null),
                    DoctorCount = 1,
                    InvoceDueDayOfMonth = PracticeController.ConvertToLocalDateTime(dbPractice, this.GetUtcNow()).Day,
                    PaymentModelName = paymentModelName,
                };

            return this.View(viewModel);
        }
        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();
        }
        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();
        }