public static void FillOperationDetails(PayPalExpressCheckoutOperation operation, Practice practice, AccountContract contractInfo, Billing billing) { operation.DefaultCurrencyCode = CurrencyCode.Brazilian_Real; operation.PaymentRequests = new PayPalList<PayPalPaymentRequest> { new PayPalPaymentRequest { BillingAgreementDescription = "O Cerebello é um software de gerênciamento de consultórios e clínicas médicas.", //BillingType = BillingCode.RecurringPayments, Description = "Cerebello - Plano profissional", InvoiceNum = string.Format("{3}{2}:{0}.{1}", billing.IdentitySetName, billing.IdentitySetNumber, practice.Id, DebugConfig.PayPal.InvoiceIdPrefix), Items = new PayPalList<PayPalPaymentRequestItem> { new PayPalPaymentRequestItem { Amount = contractInfo.BillingAmount, Name = "Cerebello SaaS", Description = "Software de gerenciamento de consultório médico.", Category = ItemCategory.Digital, }, }, }, }; if (contractInfo.BillingDiscountAmount > 0) { operation.PaymentRequests[0].Items.Add(new PayPalPaymentRequestItem { Amount = -contractInfo.BillingDiscountAmount, Name = "Desconto", Category = ItemCategory.Digital, }); } }
/// <summary> /// Create a new Billing object. /// </summary> /// <param name="id">Initial value of the Id property.</param> /// <param name="practiceId">Initial value of the PracticeId property.</param> /// <param name="issuanceDate">Initial value of the IssuanceDate property.</param> /// <param name="dueDate">Initial value of the DueDate property.</param> /// <param name="afterDueTax">Initial value of the AfterDueTax property.</param> /// <param name="afterDueMonthlyTax">Initial value of the AfterDueMonthlyTax property.</param> /// <param name="isPayd">Initial value of the IsPayd property.</param> /// <param name="identitySetName">Initial value of the IdentitySetName property.</param> /// <param name="identitySetNumber">Initial value of the IdentitySetNumber property.</param> /// <param name="mainAmount">Initial value of the MainAmount property.</param> /// <param name="mainDiscount">Initial value of the MainDiscount property.</param> /// <param name="mainAccountContractId">Initial value of the MainAccountContractId property.</param> public static Billing CreateBilling(global::System.Int32 id, global::System.Int32 practiceId, global::System.DateTime issuanceDate, global::System.DateTime dueDate, global::System.Decimal afterDueTax, global::System.Decimal afterDueMonthlyTax, global::System.Boolean isPayd, global::System.String identitySetName, global::System.Int32 identitySetNumber, global::System.Decimal mainAmount, global::System.Decimal mainDiscount, global::System.Int32 mainAccountContractId) { Billing billing = new Billing(); billing.Id = id; billing.PracticeId = practiceId; billing.IssuanceDate = issuanceDate; billing.DueDate = dueDate; billing.AfterDueTax = afterDueTax; billing.AfterDueMonthlyTax = afterDueMonthlyTax; billing.IsPayd = isPayd; billing.IdentitySetName = identitySetName; billing.IdentitySetNumber = identitySetNumber; billing.MainAmount = mainAmount; billing.MainDiscount = mainDiscount; billing.MainAccountContractId = mainAccountContractId; return billing; }
/// <summary> /// Deprecated Method for adding a new object to the Billings EntitySet. Consider using the .Add method of the associated ObjectSet<T> property instead. /// </summary> public void AddToBillings(Billing billing) { base.AddObject("Billings", billing); }
public ActionResult GenerateInvoice(GenerateInvoiceViewModel viewModel) { if (viewModel.Step == null) { if (string.IsNullOrEmpty(viewModel.PracticeIdentifier)) { this.ModelState.Clear(); viewModel.Step = 0; return this.View(); } viewModel.Step = string.IsNullOrEmpty(viewModel.InvoiceName) ? 1 : 2; } var step = viewModel.Step ?? 0; using (var db = this.CreateNewCerebelloEntities()) { // Getting the practice indicated in the view-model. Practice practice = null; if (!string.IsNullOrWhiteSpace(viewModel.PracticeIdentifier)) { practice = db.Practices .SingleOrDefault(p => p.UrlIdentifier == viewModel.PracticeIdentifier); } if (practice == null || practice.ActiveAccountContractId == null || practice.AccountContract.IsTrial || this.ModelState.HasPropertyErrors(() => viewModel.PracticeIdentifier)) { if (practice == null) this.ModelState.AddModelError(() => viewModel.PracticeIdentifier, "Consultório inexistente."); if (practice != null && practice.ActiveAccountContractId == null) this.ModelState.AddModelError(() => viewModel.PracticeIdentifier, "Consultório não possui uma conta ativa."); if (practice != null && practice.AccountContract.IsTrial) this.ModelState.AddModelError(() => viewModel.PracticeIdentifier, "Consultório possui conta trial."); return this.View(viewModel); } var utcNow = this.GetUtcNow(); viewModel.Invoices = GetAccountInvoices(practice, utcNow); if (step == 0) { if (this.Request.HttpMethod == "POST") return this.RedirectToAction("GenerateInvoice", new { viewModel.PracticeIdentifier }); // going to the next step this.ModelState.Clear(); viewModel.Step = 1; return this.View(viewModel); } var localNow = PracticeController.ConvertToLocalDateTime(practice, utcNow); if (step == 1) { this.ModelState.Clear(); viewModel.Step = 1; return this.View(viewModel); } var selectedInvoiceInfo = viewModel.Invoices.SingleOrDefault(bi => bi.NameId == viewModel.InvoiceName); if (selectedInvoiceInfo == null) { this.ModelState.AddModelError(() => viewModel.InvoiceName, "Nome de invoice não encontrado."); viewModel.Step = 1; return this.View(viewModel); } Billing billing = null; var idSet = string.Format( "CEREB.{1}{2}.{0}", localNow.Year, practice.AccountContract.BillingPeriodSize, practice.AccountContract.BillingPeriodType); var invoiceStartUtc = PracticeController.ConvertToUtcDateTime(practice, selectedInvoiceInfo.Start); billing = db.Billings.SingleOrDefault(b => b.PracticeId == practice.Id && b.MainAccountContractId == practice.ActiveAccountContractId && b.ReferenceDate == invoiceStartUtc); if (billing == null) { billing = new Billing { PracticeId = practice.Id, AfterDueMonthlyTax = 1.00m, // 1% AfterDueTax = 2.00m, // 2% IssuanceDate = utcNow, MainAmount = selectedInvoiceInfo.TotalAmount, MainDiscount = selectedInvoiceInfo.TotalDiscount, DueDate = PracticeController.ConvertToUtcDateTime(practice, selectedInvoiceInfo.DueDate), IdentitySetName = idSet, IdentitySetNumber = db.Billings.Count(b => b.PracticeId == practice.Id && b.IdentitySetName == idSet) + 1, ReferenceDate = PracticeController.ConvertToUtcDateTime(practice, selectedInvoiceInfo.Start), ReferenceDateEnd = PracticeController.ConvertToUtcDateTime(practice, selectedInvoiceInfo.End), MainAccountContractId = practice.ActiveAccountContractId.Value, }; db.Billings.AddObject(billing); } if (practice.AccountContract.BillingPaymentMethod == "PayPal Invoice") this.ViewBag.IsPayPalInvoice = true; if (this.ModelState.IsValid) { db.SaveChanges(); // adding PayPal invoice info viewModel.PayPalInvoice = new GenerateInvoiceViewModel.PayPalInvoiceInfo { UserEmail = practice.Owner.Person.Email, IssuanceDate = localNow.ToString("dd-MM-yyyy"), Currency = "BRL - Reais", Number = string.Format("{0}.{1}", billing.IdentitySetName, billing.IdentitySetNumber), DuaDate = selectedInvoiceInfo.DueDate.ToString("dd-MM-yyyy"), Terms = "Vencimento na data especificada", Items = new List<GenerateInvoiceViewModel.PayPalInvoiceItem>(), }; var strStartToEnd = selectedInvoiceInfo.End != null ? string.Format( "{0} até {1}", selectedInvoiceInfo.Start.ToString("yyyy'-'MM'-'dd"), selectedInvoiceInfo.End.Value.ToString("yyyy'-'MM'-'dd")) : string.Format( "{0}", selectedInvoiceInfo.Start.ToString("yyyy'-'MM'-'dd")); viewModel.PayPalInvoice.Items.Add( new GenerateInvoiceViewModel.PayPalInvoiceItem { NameId = "Assinatura Cerebello", Date = "", Quantity = 1, UnitPrice = selectedInvoiceInfo.TotalAmount.ToString("0.00", CultureInfo.InvariantCulture).Replace('.', ','), Description = string.Format("Assinatura do plano profissional do Cerebello ({0})", strStartToEnd), }); var periodType = practice.AccountContract.BillingPeriodType; var periodSize = practice.AccountContract.BillingPeriodSize; var discountReason = periodType == "M" && periodSize == 1 ? "mensal" : periodType == "M" && periodSize == 3 ? "trimestral" : periodType == "M" && periodSize == 6 ? "semestral" : periodType == "M" && periodSize == 12 || periodType == "Y" && periodSize == 1 ? "anual" : ""; if (selectedInvoiceInfo.TotalDiscount > 0) { viewModel.PayPalInvoice.Items.Add( new GenerateInvoiceViewModel.PayPalInvoiceItem { NameId = "Desconto da Assinatura Cerebello", Date = "", Quantity = 1, UnitPrice = selectedInvoiceInfo.TotalDiscount.ToString("'-'0.00", CultureInfo.InvariantCulture).Replace('.', ','), Description = string.Format("Desconto na Assinatura (condições especiais para pagamento {0})", discountReason) }); } viewModel.Step = 2; return this.View(viewModel); } } return this.View(viewModel); }
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(); }