Пример #1
0
        /// <summary>
        /// Handles the Click event of the btnRefresh control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void btnRefresh_Click(object sender, EventArgs e)
        {
            int?financialScheduledTransactionId = PageParameter(PageParameterKey.ScheduledTransactionId).AsIntegerOrNull();

            if (financialScheduledTransactionId.HasValue)
            {
                using (var rockContext = new RockContext())
                {
                    var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
                    var financialScheduledTransaction        = financialScheduledTransactionService.Queryable()
                                                               .Include(a => a.AuthorizedPersonAlias.Person)
                                                               .Include(a => a.FinancialGateway)
                                                               .FirstOrDefault(t => t.Id == financialScheduledTransactionId.Value);

                    if (financialScheduledTransaction != null)
                    {
                        string errorMessage = string.Empty;
                        if (financialScheduledTransactionService.GetStatus(financialScheduledTransaction, out errorMessage))
                        {
                            rockContext.SaveChanges();
                        }
                        else
                        {
                            ShowErrorMessage(errorMessage);
                        }

                        ShowView(financialScheduledTransaction);
                    }
                }
            }
        }
        /// <summary>
        /// Binds the scheduled transactions.
        /// </summary>
        private void BindScheduledTransactions()
        {
            var rockContext = new RockContext();
            var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
            var qry = financialScheduledTransactionService
                      .Queryable("ScheduledTransactionDetails,FinancialPaymentDetail.CurrencyTypeValue,FinancialPaymentDetail.CreditCardTypeValue")
                      .AsNoTracking();

            // Valid Accounts
            var accountGuids = GetAttributeValue(AttributeKey.Accounts).SplitDelimitedValues().AsGuidList();

            if (accountGuids.Any())
            {
                qry = qry.Where(t => t.ScheduledTransactionDetails.Any(d => accountGuids.Contains(d.Account.Guid)));
            }



            if (Person.GivingGroupId.HasValue)
            {
                // Person contributes with family
                qry = qry.Where(t => t.AuthorizedPersonAlias.Person.GivingGroupId == Person.GivingGroupId);
            }
            else
            {
                // Person contributes individually
                qry = qry.Where(t => t.AuthorizedPersonAlias.PersonId == Person.Id);
            }

            // only show the button if there some inactive scheduled transactions
            // 12-JAN-22 DMV: This adds a small performance hit here as this hydrates the query.
            btnShowInactiveScheduledTransactions.Visible = qry.Any(a => !a.IsActive);

            var includeInactive = hfShowInactiveScheduledTransactions.Value.AsBoolean();

            if (!includeInactive)
            {
                btnShowInactiveScheduledTransactions.Text = "Show Inactive";
                qry = qry.Where(t => t.IsActive);
            }
            else
            {
                // if including Inactive, show both Active and Inactive
                btnShowInactiveScheduledTransactions.Text = "Hide Inactive";
            }

            qry = qry
                  .OrderBy(t => t.AuthorizedPersonAlias.Person.LastName)
                  .ThenBy(t => t.AuthorizedPersonAlias.Person.NickName)
                  .ThenByDescending(t => t.IsActive)
                  .ThenByDescending(t => t.StartDate);

            var scheduledTransactionList = qry.ToList();

            // Refresh the active transactions
            financialScheduledTransactionService.GetStatus(scheduledTransactionList, true);

            rptScheduledTransaction.DataSource = scheduledTransactionList;
            rptScheduledTransaction.DataBind();
        }
        public virtual void Execute(IJobExecutionContext context)
        {
            List <string> messages = new List <string>();

            using (var rockContext = new RockContext())
            {
                var txnService = new FinancialScheduledTransactionService(rockContext);
                var checkDate  = DateTime.Now.AddDays(-7);
                var txns       = txnService
                                 .Queryable("AuthorizedPersonAlias.Person,FinancialGateway")
                                 .Where(t => t.IsActive)
                                 .Where(t => !t.NextPaymentDate.HasValue || t.NextPaymentDate.Value < checkDate)
                                 .ToList();

                foreach (var txn in txns)
                {
                    string errorMessage = string.Empty;
                    if (txnService.GetStatus(txn, out errorMessage))
                    {
                        rockContext.SaveChanges();
                    }
                    else
                    {
                        messages.Add(errorMessage);
                    }
                }
            }

            context.Result = string.Join("\r\n", messages);
        }
Пример #4
0
        /// <summary>
        /// Handles the Click event of the lbCancelSchedule control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void lbCancelSchedule_Click(object sender, EventArgs e)
        {
            int?txnId = PageParameter("ScheduledTransactionId").AsIntegerOrNull();

            if (txnId.HasValue)
            {
                using (var rockContext = new RockContext())
                {
                    var txnService = new FinancialScheduledTransactionService(rockContext);
                    var txn        = txnService
                                     .Queryable("AuthorizedPersonAlias.Person,FinancialGateway")
                                     .FirstOrDefault(t => t.Id == txnId.Value);
                    if (txn != null)
                    {
                        if (txn.FinancialGateway != null)
                        {
                            txn.FinancialGateway.LoadAttributes(rockContext);
                        }

                        string errorMessage = string.Empty;
                        if (txnService.Cancel(txn, out errorMessage))
                        {
                            txnService.GetStatus(txn, out errorMessage);
                            rockContext.SaveChanges();
                        }
                        else
                        {
                            ShowErrorMessage(errorMessage);
                        }

                        ShowView(txn);
                    }
                }
            }
        }
        /// <summary>
        /// Handles the Click event of the bbtnDelete control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void bbtnDelete_Click(object sender, EventArgs e)
        {
            /* 2021-08-27 MDP
             *
             * We really don't want to actually delete a FinancialScheduledTransaction.
             * Just inactivate it, even if there aren't FinancialTransactions associated with it.
             * It is possible the the Gateway has processed a transaction on it that Rock doesn't know about yet.
             * If that happens, Rock won't be able to match a record for that downloaded transaction!
             * We also might want to match inactive or "deleted" schedules on the Gateway to a person in Rock,
             * so we'll need the ScheduledTransaction to do that.
             *
             * So, don't delete ScheduledTransactions.
             *
             */

            BootstrapButton bbtnDelete = ( BootstrapButton )sender;
            RepeaterItem    riItem     = ( RepeaterItem )bbtnDelete.NamingContainer;

            HiddenField hfScheduledTransactionId = ( HiddenField )riItem.FindControl("hfScheduledTransactionId");
            Literal     lLavaContent             = ( Literal )riItem.FindControl("lLavaContent");
            Button      btnEdit = ( Button )riItem.FindControl("btnEdit");

            using (var rockContext = new Rock.Data.RockContext())
            {
                FinancialScheduledTransactionService fstService = new FinancialScheduledTransactionService(rockContext);
                var currentTransaction = fstService.Get(hfScheduledTransactionId.Value.AsInteger());
                if (currentTransaction != null && currentTransaction.FinancialGateway != null)
                {
                    currentTransaction.FinancialGateway.LoadAttributes(rockContext);
                }

                string errorMessage = string.Empty;
                if (fstService.Cancel(currentTransaction, out errorMessage))
                {
                    try
                    {
                        fstService.GetStatus(currentTransaction, out errorMessage);
                    }
                    catch
                    {
                        // Ignore
                    }

                    rockContext.SaveChanges();
                    lLavaContent.Text = string.Format("<div class='alert alert-success'>Your scheduled {0} has been deleted.</div>", GetAttributeValue(AttributeKey.TransactionLabel).ToLower());
                }
                else
                {
                    lLavaContent.Text = string.Format("<div class='alert alert-danger'>An error occurred while deleting your scheduled transaction. Message: {0}</div>", errorMessage);
                }
            }

            bbtnDelete.Visible = false;
            btnEdit.Visible    = false;
        }
        /// <summary>
        /// Event when the user clicks to delete (but really inactivates) the scheduled transaction
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void rptScheduledTransaction_Inactivate(object sender, CommandEventArgs e)
        {
            var scheduledTransactionGuid = e.CommandArgument.ToStringSafe().AsGuid();
            var rockContext = new RockContext();
            var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
            var financialScheduledTransaction        = financialScheduledTransactionService.Get(scheduledTransactionGuid);

            if (financialScheduledTransaction?.FinancialGateway == null)
            {
                return;
            }

            string errorMessage;

            /* 2021-08-27 MDP
             *
             * We really don't want to actually delete a FinancialScheduledTransaction.
             * Just inactivate it, even if there aren't FinancialTransactions associated with it.
             * It is possible the the Gateway has processed a transaction on it that Rock doesn't know about yet.
             * If that happens, Rock won't be able to match a record for that downloaded transaction!
             * We also might want to match inactive or "deleted" schedules on the Gateway to a person in Rock,
             * so we'll need the ScheduledTransaction to do that.
             *
             * So, don't delete ScheduledTransactions.
             *
             * However, if ScheduledTransaction does not currently have any FinancialTransactions associated with it,
             * we can *say* we are deleting it in the messages. Also, when doing a 'Show Inactive Scheduled Transactions'
             * we won't list Scheduled Transactions that are Inactive AND don't currently have financial transactions
             * associated with it. If a transactions come in later, then we'll end up showing it as an inactive scheduled
             * transaction again.
             *
             */

            if (financialScheduledTransactionService.Cancel(financialScheduledTransaction, out errorMessage))
            {
                try
                {
                    financialScheduledTransactionService.GetStatus(financialScheduledTransaction, out errorMessage);
                }
                catch
                {
                    // Ignore
                }

                rockContext.SaveChanges();
            }
            else
            {
                mdWarningAlert.Show(errorMessage, ModalAlertType.Information);
            }

            ShowDetail();
        }
        /// <summary>
        /// Shows the content.
        /// </summary>
        private void ShowContent()
        {
            // get scheduled contributions for current user
            if (CurrentPerson != null)
            {
                var rockContext        = new RockContext();
                var transactionService = new FinancialScheduledTransactionService(rockContext);
                var personService      = new PersonService(rockContext);

                // get business giving id
                var givingIds = personService.GetBusinesses(CurrentPerson.Id).Select(g => g.GivingId).ToList();

                // add the person's regular giving id
                givingIds.Add(CurrentPerson.GivingId);

                var schedules = transactionService.Queryable()
                                .Include(a => a.ScheduledTransactionDetails.Select(s => s.Account))
                                .Where(s => givingIds.Contains(s.AuthorizedPersonAlias.Person.GivingId) && s.IsActive == true);

                // filter the list if necessary
                var gatewayFilterGuid = GetAttributeValue(AttributeKey.GatewayFilter).AsGuidOrNull();
                if (gatewayFilterGuid != null)
                {
                    schedules = schedules.Where(s => s.FinancialGateway.Guid == gatewayFilterGuid);
                }

                foreach (var schedule in schedules)
                {
                    try
                    {
                        // This will ensure we have the most recent status, even if the schedule hasn't been making payments.
                        string errorMessage;
                        transactionService.GetStatus(schedule, out errorMessage);
                    }
                    catch (Exception ex)
                    {
                        // log and ignore
                        LogException(ex);
                    }
                }

                rptScheduledTransactions.DataSource = schedules.ToList();
                rptScheduledTransactions.DataBind();

                if (schedules.Count() == 0)
                {
                    pnlNoScheduledTransactions.Visible   = true;
                    lNoScheduledTransactionsMessage.Text = string.Format("No {0} currently exist.", GetAttributeValue(AttributeKey.TransactionLabel).Pluralize().ToLower());
                }
            }
        }
Пример #8
0
        public virtual void Execute(IJobExecutionContext context)
        {
            List <string> messages       = new List <string>();
            int           completedCount = 0;

            using (var rockContext = new RockContext())
            {
                var txnService = new FinancialScheduledTransactionService(rockContext);
                var txns       = txnService
                                 .Queryable("FinancialGateway")
                                 .Where(t => t.IsActive)
                                 .ToList();

                foreach (var txn in txns)
                {
                    txn.LoadAttributes(rockContext);

                    var maxCount = txn.GetAttributeValue("com.shepherdchurch.MaxPaymentCount").AsInteger();

                    if (maxCount <= 0 || txn.Transactions.Count < maxCount)
                    {
                        continue;
                    }

                    string errorMessage = string.Empty;
                    if (txnService.Cancel(txn, out errorMessage))
                    {
                        txnService.GetStatus(txn, out errorMessage);
                        rockContext.SaveChanges();
                        completedCount += 1;
                    }
                    else
                    {
                        messages.Add(errorMessage);
                    }
                }
            }

            if (messages.Any())
            {
                throw new Exception(string.Join("\r\n", messages));
            }

            context.Result = string.Format("Marked {0} scheduled transactions as complete.", completedCount);
        }
Пример #9
0
        /// <summary>
        /// Handles the Click event of the btnRefresh control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void btnRefresh_Click(object sender, EventArgs e)
        {
            var financialScheduledTranactionGuid = GetScheduledTransactionGuidFromUrl();

            if (!financialScheduledTranactionGuid.HasValue)
            {
                return;
            }

            using (var rockContext = new RockContext())
            {
                var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
                var financialScheduledTransaction        = financialScheduledTransactionService.Queryable()
                                                           .Include(a => a.AuthorizedPersonAlias.Person)
                                                           .Include(a => a.FinancialGateway)
                                                           .FirstOrDefault(t => t.Guid == financialScheduledTranactionGuid.Value);

                if (financialScheduledTransaction == null)
                {
                    return;
                }

                string errorMessage = string.Empty;
                if (financialScheduledTransactionService.GetStatus(financialScheduledTransaction, out errorMessage))
                {
                    rockContext.SaveChanges();
                }
                else
                {
                    if (financialScheduledTransaction.IsActive == false)
                    {
                        // if GetStatus failed, but the scheduled transaction is inactive, just show Schedule is Inactive
                        // This takes care of dealing with gateways that delete the scheduled payment vs inactivating them on the gateway side
                        ShowErrorMessage("Schedule is inactive");
                    }
                    else
                    {
                        ShowErrorMessage(errorMessage);
                    }
                }

                ShowView(financialScheduledTransaction);
            }
        }
        /// <summary>
        /// Handles the Click event of the bbtnDelete control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void bbtnDelete_Click(object sender, EventArgs e)
        {
            BootstrapButton bbtnDelete = ( BootstrapButton )sender;
            RepeaterItem    riItem     = ( RepeaterItem )bbtnDelete.NamingContainer;

            HiddenField hfScheduledTransactionId = ( HiddenField )riItem.FindControl("hfScheduledTransactionId");
            Literal     content = ( Literal )riItem.FindControl("lLiquidContent");
            Button      btnEdit = ( Button )riItem.FindControl("btnEdit");

            using (var rockContext = new Rock.Data.RockContext())
            {
                FinancialScheduledTransactionService fstService = new FinancialScheduledTransactionService(rockContext);
                var currentTransaction = fstService.Get(hfScheduledTransactionId.Value.AsInteger());
                if (currentTransaction != null && currentTransaction.FinancialGateway != null)
                {
                    currentTransaction.FinancialGateway.LoadAttributes(rockContext);
                }

                string errorMessage = string.Empty;
                if (fstService.Cancel(currentTransaction, out errorMessage))
                {
                    try
                    {
                        fstService.GetStatus(currentTransaction, out errorMessage);
                    }
                    catch
                    {
                        // Ignore
                    }

                    rockContext.SaveChanges();
                    content.Text = string.Format("<div class='alert alert-success'>Your recurring {0} has been deleted.</div>", GetAttributeValue(AttributeKey.TransactionLabel).ToLower());
                }
                else
                {
                    content.Text = string.Format("<div class='alert alert-danger'>An error occurred while deleting your scheduled transaction. Message: {0}</div>", errorMessage);
                }
            }

            bbtnDelete.Visible = false;
            btnEdit.Visible    = false;
        }
Пример #11
0
        /// <summary>
        /// Handles the Click event of the btnReactivateSchedule control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void btnReactivateSchedule_Click(object sender, EventArgs e)
        {
            var financialScheduledTranactionGuid = GetScheduledTransactionGuidFromUrl();

            if (!financialScheduledTranactionGuid.HasValue)
            {
                return;
            }

            using (var rockContext = new RockContext())
            {
                var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
                var financialScheduledTransaction        = financialScheduledTransactionService.Queryable()
                                                           .Include(a => a.AuthorizedPersonAlias.Person)
                                                           .Include(a => a.FinancialGateway)
                                                           .FirstOrDefault(t => t.Guid == financialScheduledTranactionGuid.Value);

                if (financialScheduledTransaction == null)
                {
                    return;
                }

                if (financialScheduledTransaction.FinancialGateway != null)
                {
                    financialScheduledTransaction.FinancialGateway.LoadAttributes(rockContext);
                }

                string errorMessage = string.Empty;
                if (financialScheduledTransactionService.Reactivate(financialScheduledTransaction, out errorMessage))
                {
                    financialScheduledTransactionService.GetStatus(financialScheduledTransaction, out errorMessage);
                    rockContext.SaveChanges();
                }
                else
                {
                    ShowErrorMessage(errorMessage);
                }

                ShowView(financialScheduledTransaction);
            }
        }
        /// <summary>
        /// Shows the content.
        /// </summary>
        private void ShowContent()
        {
            // get scheduled contributions for current user
            if (CurrentPerson != null)
            {
                var rockContext        = new RockContext();
                var transactionService = new FinancialScheduledTransactionService(rockContext);
                var personService      = new PersonService(rockContext);

                // get business giving id
                var givingIds = personService.GetBusinesses(CurrentPerson.Id).Select(g => g.GivingId).ToList();

                // add the person's regular giving id
                givingIds.Add(CurrentPerson.GivingId);

                var schedules = transactionService.Queryable()
                                .Include(a => a.ScheduledTransactionDetails.Select(s => s.Account))
                                .Where(s => givingIds.Contains(s.AuthorizedPersonAlias.Person.GivingId) && s.IsActive == true);

                // filter the list if necessary
                var gatewayFilterGuid = GetAttributeValue(AttributeKey.GatewayFilter).AsGuidOrNull();
                if (gatewayFilterGuid != null)
                {
                    schedules = schedules.Where(s => s.FinancialGateway.Guid == gatewayFilterGuid);
                }

                // Refresh the active transactions
                transactionService.GetStatus(schedules, true);

                rptScheduledTransactions.DataSource = schedules.ToList();
                rptScheduledTransactions.DataBind();

                if (schedules.Count() == 0)
                {
                    pnlNoScheduledTransactions.Visible   = true;
                    lNoScheduledTransactionsMessage.Text = string.Format("No {0} currently exist.", GetAttributeValue(AttributeKey.TransactionLabel).Pluralize().ToLower());
                }
            }
        }
Пример #13
0
        /// <summary>
        /// Executes this instance.
        /// </summary>
        public void Execute()
        {
            if (!ScheduledTransactionIds.Any())
            {
                return;
            }

            using (var rockContext = new RockContext())
            {
                var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);

                foreach (var scheduledTransactionId in ScheduledTransactionIds)
                {
                    var financialScheduledTransaction = financialScheduledTransactionService.Get(scheduledTransactionId);

                    if (financialScheduledTransaction != null)
                    {
                        financialScheduledTransactionService.GetStatus(financialScheduledTransaction, out _);

                        rockContext.SaveChanges();
                    }
                }
            }
        }
        /// <summary>
        /// Handles the Click event of the btnMigrateScheduledTransactions control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        protected void btnMigrateScheduledTransactions_Click(object sender, EventArgs e)
        {
            var rockContext = new RockContext();

            var binaryFileService = new BinaryFileService(rockContext);
            var binaryFileId      = fuScheduleImportFile.BinaryFileId;

            BinaryFile binaryFile = null;

            if (binaryFileId.HasValue)
            {
                binaryFile = binaryFileService.Get(binaryFileId.Value);
            }

            Dictionary <string, string> subscriptionImportRecordLookup = null;

            var importData = binaryFile.ContentsToString();

            StringReader stringReader = new StringReader(importData);
            CsvReader    csvReader    = new CsvReader(stringReader);

            csvReader.Configuration.HasHeaderRecord = false;

            subscriptionImportRecordLookup = csvReader.GetRecords <SubscriptionCustomerImportRecord>().ToDictionary(k => k.NMISubscriptionId, v => v.PiCustomerId);

            var financialGatewayService = new FinancialGatewayService(rockContext);
            var nmiFinancialGatewayId   = ddlNMIGateway.SelectedValue.AsInteger();
            var nmiFinancialGateway     = financialGatewayService.Get(nmiFinancialGatewayId);
            var nmiGatewayComponent     = nmiFinancialGateway.GetGatewayComponent();
            var piFinancialGatewayId    = ddlPiGateway.SelectedValue.AsInteger();
            var piFinancialGateway      = financialGatewayService.Get(piFinancialGatewayId);
            var piGatewayComponent      = piFinancialGateway.GetGatewayComponent() as IHostedGatewayComponent;

            var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);

            // Get the ScheduledTransaction with NoTracking. If we need to update it, we'll track it with a different rockContext then save it.
            // Limit to active subscriptions that have a NextPaymentDate (onetime or canceled schedules might not have a NextPaymentDate)
            var scheduledTransactions = financialScheduledTransactionService.Queryable().Where(a => a.FinancialGatewayId == nmiFinancialGatewayId & a.IsActive && a.NextPaymentDate.HasValue).AsNoTracking().ToList();

            var earliestPiStartDate = piGatewayComponent.GetEarliestScheduledStartDate(piFinancialGateway);
            var oneTimeFrequencyId  = DefinedValueCache.GetId(Rock.SystemGuid.DefinedValue.TRANSACTION_FREQUENCY_ONE_TIME.AsGuid());

            string errorMessage;

            var scheduledTransactionResultsBuilder = new StringBuilder();

            var scheduledTransactionCount    = scheduledTransactions.Count();
            var scheduledTransactionProgress = 0;

            // Migrating Scheduled Transactions might take a while. Each migrated Scheduled Payment may take a half second or so to create on the Pi Gateway.
            var importTask = new Task(() =>
            {
                // wait a little so the browser can render and start listening to events
                Task.Delay(1000).Wait();
                _hubContext.Clients.All.setButtonVisibilty(this.SignalRNotificationKey, false);

                foreach (var scheduledTransaction in scheduledTransactions)
                {
                    System.Threading.Thread.Sleep(1000);

                    UpdateProgressMessage(string.Format("Migrating Scheduled Transactions: {0} of {1}", scheduledTransactionProgress, scheduledTransactionCount), " ");

                    scheduledTransactionProgress++;
                    var nmiSubscriptionId = scheduledTransaction.GatewayScheduleId;
                    var nmiCustomerId     = scheduledTransaction.ForeignKey;
                    var piCustomerId      = subscriptionImportRecordLookup.GetValueOrNull(nmiSubscriptionId);
                    if (piCustomerId == null)
                    {
                        scheduledTransactionResultsBuilder.AppendFormat(
                            "WARNING: No Pi CustomerId found for Financial Scheduled Transaction with Id: {0} which is associated NMI SubscriptionId: '{1}'" + Environment.NewLine,
                            scheduledTransaction.Id,
                            nmiSubscriptionId
                            );
                        continue;
                    }

                    // Pi requires that NextPaymentDate is in the Future (using UTC). That math is done in the gateway implementation...
                    // if the NextPayment null or earlier than whatever Pi considers the earliest start date, see if we can fix that up by calling GetStatus
                    if (scheduledTransaction.NextPaymentDate == null || scheduledTransaction.NextPaymentDate < earliestPiStartDate)
                    {
                        financialScheduledTransactionService.GetStatus(scheduledTransaction, out errorMessage);
                    }

                    if (scheduledTransaction.NextPaymentDate == null)
                    {
                        // Shouldn't happen, but just in case
                        scheduledTransactionResultsBuilder.AppendFormat(
                            "WARNING: Unknown NextPaymentDate for FinancialScheduledTransaction.Id: {0} NMI SubscriptionId: '{1}'" + Environment.NewLine,
                            scheduledTransaction.Id,
                            nmiSubscriptionId
                            );
                        continue;
                    }


                    if (scheduledTransaction.NextPaymentDate < earliestPiStartDate)
                    {
                        if ((scheduledTransaction.NextPaymentDate > RockDateTime.Today) && earliestPiStartDate.Subtract(scheduledTransaction.NextPaymentDate.Value).TotalDays <= 2)
                        {
                            // if the NextPaymentDate is after Today but before the Earliest Pi Start Date, it'll be off by less than 24 hrs, so just reschedule it for the Earliest Pi Start Date
                            scheduledTransaction.NextPaymentDate = earliestPiStartDate;
                        }
                        else
                        {
                            // if the NextPaymentDate is still too early AFTER getting the most recent status, then we can't safely figure it out, so report it
                            scheduledTransactionResultsBuilder.AppendFormat(
                                "WARNING: NextPaymentDate of {0} for FinancialScheduledTransaction.Id: {1} and NMI SubscriptionId: '{2}' must have a NextPaymentDate of at least {3}." + Environment.NewLine,
                                scheduledTransaction.NextPaymentDate,
                                scheduledTransaction.Id,
                                nmiSubscriptionId,
                                earliestPiStartDate
                                );
                        }
                    }

                    // create a subscription in the Pi System, then cancel the one on the NMI system
                    PaymentSchedule paymentSchedule = new PaymentSchedule
                    {
                        TransactionFrequencyValue = DefinedValueCache.Get(scheduledTransaction.TransactionFrequencyValueId),
                        StartDate = scheduledTransaction.NextPaymentDate.Value,
                        PersonId  = scheduledTransaction.AuthorizedPersonAlias.PersonId
                    };

                    ReferencePaymentInfo referencePaymentInfo = new ReferencePaymentInfo
                    {
                        GatewayPersonIdentifier = piCustomerId,
                        Description             = string.Format("Migrated from NMI SubscriptionID:{0}", nmiSubscriptionId)
                    };

                    var piGateway = (piGatewayComponent as PiGateway);
                    string alreadyMigratedPiSubscriptionId = null;

                    if (piGateway != null)
                    {
                        var customerPiSubscriptions     = piGateway.SearchCustomerSubscriptions(piFinancialGateway, piCustomerId);
                        alreadyMigratedPiSubscriptionId = customerPiSubscriptions.Data.Where(a => a.Description.Contains(referencePaymentInfo.Description)).Select(a => a.Customer.Id).FirstOrDefault();
                    }

                    if (string.IsNullOrEmpty(alreadyMigratedPiSubscriptionId))
                    {
                        // hasn't already been migrated, so go ahead and migrate it
                        var tempFinancialScheduledTransaction = piGatewayComponent.AddScheduledPayment(piFinancialGateway, paymentSchedule, referencePaymentInfo, out errorMessage);
                        if (tempFinancialScheduledTransaction != null)
                        {
                            ////////////#### DISABLE this when debugger #####
                            nmiGatewayComponent.CancelScheduledPayment(scheduledTransaction, out errorMessage);

                            // update the scheduled transaction to point to the Pi scheduled transaction
                            using (var updateRockContext = new RockContext())
                            {
                                // Attach the person to the updateRockContext so that it'll be tracked/saved using updateRockContext
                                updateRockContext.FinancialScheduledTransactions.Attach(scheduledTransaction);
                                scheduledTransaction.TransactionCode    = tempFinancialScheduledTransaction.TransactionCode;
                                scheduledTransaction.GatewayScheduleId  = tempFinancialScheduledTransaction.GatewayScheduleId;
                                scheduledTransaction.FinancialGatewayId = tempFinancialScheduledTransaction.FinancialGatewayId;
                                updateRockContext.SaveChanges();
                            }

                            scheduledTransactionResultsBuilder.AppendFormat(
                                "SUCCESS: Scheduled Transaction migration succeeded. (FinancialScheduledTransaction.Id: {0}, NMI SubscriptionId: '{1}', Pi CustomerId: {2}, Pi SubscriptionId: {3})" + Environment.NewLine,
                                scheduledTransaction.Id,
                                nmiSubscriptionId,
                                piCustomerId,
                                scheduledTransaction.GatewayScheduleId
                                );
                        }
                        else
                        {
                            scheduledTransactionResultsBuilder.AppendFormat(
                                "ERROR: Scheduled Transaction migration failed. ErrorMessage: {0}, FinancialScheduledTransaction.Id: {1}, NMI SubscriptionId: '{2}', Pi CustomerId: {3}" + Environment.NewLine,
                                errorMessage,
                                scheduledTransaction.Id,
                                nmiSubscriptionId,
                                piCustomerId
                                );
                        }
                    }
                    else
                    {
                        scheduledTransactionResultsBuilder.AppendFormat(
                            "INFO: Scheduled Transaction already migrated to PI. FinancialScheduledTransaction.Id: {0}, NMI SubscriptionId: '{1}', Pi SubscriptionId: '{2}', Pi CustomerId: {3}" + Environment.NewLine,
                            scheduledTransaction.Id,
                            nmiSubscriptionId,
                            alreadyMigratedPiSubscriptionId,
                            piCustomerId
                            );
                    }
                }
            });

            string importResult = string.Empty;

            importTask.ContinueWith((c) =>
            {
                if (c.Exception != null)
                {
                    ExceptionLogService.LogException(c.Exception);
                    scheduledTransactionResultsBuilder.AppendLine(string.Format("EXCEPTION: {0}", c.Exception.Flatten().Message));
                    importResult = "EXCEPTION";
                    UpdateProgressMessage(importResult, scheduledTransactionResultsBuilder.ToString());
                }
                else
                {
                    importResult = "Migrate Scheduled Transactions Completed Successfully";
                    UpdateProgressMessage(importResult, scheduledTransactionResultsBuilder.ToString());
                }

                this.SetBlockUserPreference("MigrateScheduledTransactionsResultSummary", importResult);
                this.SetBlockUserPreference("MigrateScheduledTransactionsResultDetails", scheduledTransactionResultsBuilder.ToString());
            });

            importTask.Start();

            nbMigrateScheduledTransactions.Visible = false;

            // wait for 5 seconds to see if this happens fast enough to do without Signal R. Otherwise, let the importTask continue and send progress to Signal R.
            var waitResult = importTask.Wait(5000);

            if (waitResult)
            {
                // wait just a little bit to make sure the importResult gets set
                System.Threading.Thread.Sleep(1000);

                nbMigrateScheduledTransactions.Visible             = true;
                nbMigrateScheduledTransactions.Title               = "Success";
                nbMigrateScheduledTransactions.NotificationBoxType = Rock.Web.UI.Controls.NotificationBoxType.Success;

                var resultDetails = scheduledTransactionResultsBuilder.ToString();
                if (resultDetails.Contains("ERROR") || resultDetails.Contains("WARNING"))
                {
                    nbMigrateScheduledTransactions.Title = "Completed with Warnings";
                    nbMigrateScheduledTransactions.NotificationBoxType = Rock.Web.UI.Controls.NotificationBoxType.Info;
                }

                nbMigrateScheduledTransactions.Text    = importResult;
                nbMigrateScheduledTransactions.Details = resultDetails.ConvertCrLfToHtmlBr();
            }
        }
Пример #15
0
        // helper functional methods (like BindGrid(), etc.)
        private void ShowContent()
        {
            List <Dictionary <string, object> > scheduleSummaries = new List <Dictionary <string, object> >();

            // get scheduled transactions for current user
            if (CurrentPerson != null)
            {
                var rockContext = new RockContext();
                FinancialScheduledTransactionService transactionService = new FinancialScheduledTransactionService(rockContext);

                var schedules = transactionService.Queryable("ScheduledTransactionDetails.Account")
                                .Where(s => s.AuthorizedPersonAlias.Person.GivingId == CurrentPerson.GivingId && s.IsActive == true);

                foreach (FinancialScheduledTransaction schedule in schedules)
                {
                    string errorMsgs = string.Empty;
                    transactionService.GetStatus(schedule, out errorMsgs);

                    decimal totalAmount = 0;

                    Dictionary <string, object> scheduleSummary = new Dictionary <string, object>();
                    scheduleSummary.Add("Id", schedule.Id);
                    scheduleSummary.Add("Guid", schedule.Guid);
                    scheduleSummary.Add("StartDate", schedule.StartDate);
                    scheduleSummary.Add("EndDate", schedule.EndDate);
                    scheduleSummary.Add("NextPaymentDate", schedule.NextPaymentDate);

                    if (schedule.NextPaymentDate.HasValue)
                    {
                        scheduleSummary.Add("DaysTillNextPayment", (schedule.NextPaymentDate.Value - DateTime.Now).Days);
                    }
                    else
                    {
                        scheduleSummary.Add("DaysTillNextPayment", null);
                    }

                    DateTime?lastPaymentDate = schedule.Transactions.Max(t => t.TransactionDateTime);
                    scheduleSummary.Add("LastPaymentDate", lastPaymentDate);

                    if (lastPaymentDate.HasValue)
                    {
                        scheduleSummary.Add("DaysSinceLastPayment", (DateTime.Now - lastPaymentDate.Value).Days);
                    }
                    else
                    {
                        scheduleSummary.Add("DaysSinceLastPayment", null);
                    }

                    scheduleSummary.Add("CurrencyType", (schedule.FinancialPaymentDetail != null && schedule.FinancialPaymentDetail.CurrencyTypeValue != null) ? schedule.FinancialPaymentDetail.CurrencyTypeValue.Value : "");
                    scheduleSummary.Add("CreditCardType", (schedule.FinancialPaymentDetail != null && schedule.FinancialPaymentDetail.CreditCardTypeValue != null) ? schedule.FinancialPaymentDetail.CreditCardTypeValue.Value : "");
                    scheduleSummary.Add("UrlEncryptedKey", schedule.UrlEncodedKey);
                    scheduleSummary.Add("Frequency", schedule.TransactionFrequencyValue.Value);
                    scheduleSummary.Add("FrequencyDescription", schedule.TransactionFrequencyValue.Description);

                    List <Dictionary <string, object> > summaryDetails = new List <Dictionary <string, object> >();

                    foreach (FinancialScheduledTransactionDetail detail in schedule.ScheduledTransactionDetails)
                    {
                        Dictionary <string, object> detailSummary = new Dictionary <string, object>();
                        detailSummary.Add("AccountId", detail.Id);
                        detailSummary.Add("AccountName", detail.Account.Name);
                        detailSummary.Add("Amount", detail.Amount);
                        detailSummary.Add("Summary", detail.Summary);

                        summaryDetails.Add(detailSummary);

                        totalAmount += detail.Amount;
                    }

                    scheduleSummary.Add("ScheduledAmount", totalAmount);
                    scheduleSummary.Add("TransactionDetails", summaryDetails);

                    scheduleSummaries.Add(scheduleSummary);
                }

                rockContext.SaveChanges();
            }

            // added linked pages to mergefields
            Dictionary <string, object> linkedPages = new Dictionary <string, object>();

            linkedPages.Add("ManageScheduledTransactionsPage", LinkedPageRoute("ManageScheduledTransactionsPage"));
            linkedPages.Add("TransactionHistoryPage", LinkedPageRoute("TransactionHistoryPage"));
            linkedPages.Add("TransactionEntryPage", LinkedPageRoute("TransactionEntryPage"));



            var scheduleValues = new Dictionary <string, object>();

            scheduleValues.Add("ScheduledTransactions", scheduleSummaries.ToList());
            scheduleValues.Add("LinkedPages", linkedPages);
            // TODO: When support for "Person" is not supported anymore (should use "CurrentPerson" instead), remove this line
            scheduleValues.Add("Person", CurrentPerson);
            scheduleValues.Add("CurrentPerson", CurrentPerson);

            string content = GetAttributeValue("Template").ResolveMergeFields(scheduleValues);

            lContent.Text = content;
        }
        /// <summary>
        /// Shows the details.
        /// </summary>
        private void ShowDetails()
        {
            var rockContext          = new RockContext();
            var scheduledTransaction = this.GetFinancialScheduledTransaction(rockContext);

            if (scheduledTransaction == null)
            {
                // Note: Also verified in OnInit
                ShowConfigurationMessage(NotificationBoxType.Warning, "Warning", "Scheduled Transaction not found.");
                return;
            }

            var    financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
            string errorMessages;

            if (!financialScheduledTransactionService.GetStatus(scheduledTransaction, out errorMessages))
            {
                ShowConfigurationMessage(NotificationBoxType.Danger, "Error", errorMessages);
                return;
            }

            hfScheduledTransactionGuid.Value = scheduledTransaction.Guid.ToString();

            List <int> selectableAccountIds = new FinancialAccountService(rockContext).GetByGuids(this.GetAttributeValues(AttributeKey.AccountsToDisplay).AsGuidList()).Select(a => a.Id).ToList();

            CampusAccountAmountPicker.AccountIdAmount[] accountAmounts = scheduledTransaction.ScheduledTransactionDetails.Select(a => new CampusAccountAmountPicker.AccountIdAmount(a.AccountId, a.Amount)).ToArray();

            // if the scheduledTransaction already has Multiple Account, enabled multi account mode. Otherwise, only enabled multi account based on the block setting.
            var hasMultipleAccounts = accountAmounts.Length > 1;

            bool enableMultiAccount = hasMultipleAccounts || this.GetAttributeValue(AttributeKey.EnableMultiAccount).AsBoolean();

            if (enableMultiAccount)
            {
                caapPromptForAccountAmounts.AmountEntryMode = CampusAccountAmountPicker.AccountAmountEntryMode.MultipleAccounts;
            }
            else
            {
                caapPromptForAccountAmounts.AmountEntryMode = CampusAccountAmountPicker.AccountAmountEntryMode.SingleAccount;
            }

            caapPromptForAccountAmounts.AskForCampusIfKnown = this.GetAttributeValue(AttributeKey.AskForCampusIfKnown).AsBoolean();

            caapPromptForAccountAmounts.SelectableAccountIds = selectableAccountIds.ToArray();

            if (!caapPromptForAccountAmounts.SelectableAccountIds.Any())
            {
                ShowConfigurationMessage(NotificationBoxType.Warning, "Configuration", "At least one Financial Account must be selected in the configuration for this block.");
                pnlPromptForChanges.Visible = false;
                return;
            }

            if (this.FinancialGateway == null)
            {
                ShowConfigurationMessage(NotificationBoxType.Warning, "Configuration", "Unable to determine the financial gateway for this scheduled transaction.");
                pnlPromptForChanges.Visible = false;
                return;
            }

            if (this.FinancialGatewayComponent == null || !(this.FinancialGatewayComponent is IHostedGatewayComponent))
            {
                ShowConfigurationMessage(NotificationBoxType.Danger, "Configuration", "This page is not configured to allow edits for the payment gateway associated with the selected transaction.");
                pnlPromptForChanges.Visible = false;
                return;
            }

            caapPromptForAccountAmounts.AccountAmounts = accountAmounts;

            var targetPerson = scheduledTransaction.AuthorizedPersonAlias.Person;

            SetAccountPickerCampus(targetPerson);

            int oneTimeFrequencyId = DefinedValueCache.GetId(Rock.SystemGuid.DefinedValue.TRANSACTION_FREQUENCY_ONE_TIME.AsGuid()) ?? 0;

            ddlFrequency.Items.Clear();
            var supportedFrequencies = this.FinancialGatewayComponent.SupportedPaymentSchedules;

            foreach (var supportedFrequency in supportedFrequencies)
            {
                // If this isn't a one-time scheduled transaction, don't allow changing scheduled transaction to a one-time,
                if (scheduledTransaction.TransactionFrequencyValueId == oneTimeFrequencyId || supportedFrequency.Id != oneTimeFrequencyId)
                {
                    ddlFrequency.Items.Add(new ListItem(supportedFrequency.Value, supportedFrequency.Id.ToString()));
                }
            }

            ddlFrequency.SetValue(scheduledTransaction.TransactionFrequencyValueId);

            /* 2020-02-26 MDP: Payment prompt behavior..
             *  - No Saved Accounts
             *      - Show text with existing payment method with a 'Change' link.
             *      - If 'Change' is clicked, existing payment info prompt will disappear and hosted payment will be displayed
             *  - Has Saved Accounts
             *      - Show RadioButtons with first item with the existing payment as the option, followed by saved accounts
             *      - Then under the RadioButtons show a 'Add Method'.
             *      - If 'Add Method' is clicked, RadioButtons will disappear and hosted payment will be displayed
             */

            string paymentName;

            if (scheduledTransaction.FinancialPaymentDetail.FinancialPersonSavedAccountId.HasValue)
            {
                paymentName = scheduledTransaction.FinancialPaymentDetail.FinancialPersonSavedAccount.Name;
            }
            else
            {
                paymentName = scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValue?.Value;
            }

            string existingPaymentInfoDisplayText;

            if (scheduledTransaction.FinancialPaymentDetail.ExpirationDate.IsNotNullOrWhiteSpace())
            {
                existingPaymentInfoDisplayText = $"Existing Payment Method - {paymentName} ({scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked} Expires: {scheduledTransaction.FinancialPaymentDetail.ExpirationDate})";
            }
            else
            {
                existingPaymentInfoDisplayText = $"Existing Payment Method - {paymentName} ({scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked})";
            }

            lUseExistingPaymentMethodNoSavedAccounts.Text = existingPaymentInfoDisplayText;

            var personSavedAccountList = GetSavedAccounts();

            pnlHostedPaymentControl.Visible = false;

            if (personSavedAccountList.Any())
            {
                pnlUseExistingPaymentWithSavedAccounts.Visible = true;
                pnlUseExistingPaymentNoSavedAccounts.Visible   = false;
                BindPersonSavedAccounts(personSavedAccountList);
                rblExistingPaymentOrPersonSavedAccount.Items.Insert(0, new ListItem(existingPaymentInfoDisplayText, "0"));

                // default to using existing payment method
                rblExistingPaymentOrPersonSavedAccount.SetValue(0);
            }
            else
            {
                // no saved account, so just prompt for payment info (or using existing payment info)
                pnlUseExistingPaymentNoSavedAccounts.Visible   = true;
                pnlUseExistingPaymentWithSavedAccounts.Visible = false;
            }

            dtpStartDate.SelectedDate = scheduledTransaction.NextPaymentDate;

            // NOTE: Depending on the Gateway, the earliest date could be more than 1-2+ days in the future
            var earliestScheduledStartDate = FinancialGatewayComponent.GetEarliestScheduledStartDate(FinancialGateway);

            if (dtpStartDate.SelectedDate.HasValue && dtpStartDate.SelectedDate.Value < earliestScheduledStartDate)
            {
                dtpStartDate.SelectedDate = earliestScheduledStartDate;
            }

            var person = scheduledTransaction.AuthorizedPersonAlias.Person;

            Location billingLocation = null;

            // default to the billing location of the scheduled transaction, or the mailing location if unable to get a billing location from the scheduled transaction.
            if (scheduledTransaction.FinancialPaymentDetail != null)
            {
                billingLocation = scheduledTransaction.FinancialPaymentDetail.BillingLocation;
            }

            if (billingLocation == null)
            {
                billingLocation = person.GetMailingLocation();
            }

            acBillingAddress.SetValues(billingLocation);
        }
        /// <summary>
        /// Binds the scheduled transactions.
        /// </summary>
        private void BindScheduledTransactions()
        {
            var rockContext = new RockContext();
            var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
            var qry = financialScheduledTransactionService
                      .Queryable("ScheduledTransactionDetails,FinancialPaymentDetail.CurrencyTypeValue,FinancialPaymentDetail.CreditCardTypeValue")
                      .AsNoTracking();

            // Valid Accounts
            var accountGuids = GetAttributeValue(AttributeKey.Accounts).SplitDelimitedValues().AsGuidList();

            if (accountGuids.Any())
            {
                qry = qry.Where(t => t.ScheduledTransactionDetails.Any(d => accountGuids.Contains(d.Account.Guid)));
            }



            if (Person.GivingGroupId.HasValue)
            {
                // Person contributes with family
                qry = qry.Where(t => t.AuthorizedPersonAlias.Person.GivingGroupId == Person.GivingGroupId);
            }
            else
            {
                // Person contributes individually
                qry = qry.Where(t => t.AuthorizedPersonAlias.PersonId == Person.Id);
            }

            // only show the button if there some in active scheduled transactions
            btnShowInactiveScheduledTransactions.Visible = qry.Any(a => !a.IsActive);

            var includeInactive = hfShowInactiveScheduledTransactions.Value.AsBoolean();

            if (!includeInactive)
            {
                btnShowInactiveScheduledTransactions.Text = "Show Inactive";
                qry = qry.Where(t => t.IsActive);
            }
            else
            {
                // if including Inactive, show both Active and Inactive
                btnShowInactiveScheduledTransactions.Text = "Hide Inactive";
            }

            qry = qry
                  .OrderBy(t => t.AuthorizedPersonAlias.Person.LastName)
                  .ThenBy(t => t.AuthorizedPersonAlias.Person.NickName)
                  .ThenByDescending(t => t.IsActive)
                  .ThenByDescending(t => t.StartDate);

            var scheduledTransactionList = qry.ToList();

            foreach (var schedule in scheduledTransactionList)
            {
                try
                {
                    // This will ensure we have the most recent status, even if the schedule hasn't been making payments.
                    string errorMessage;
                    financialScheduledTransactionService.GetStatus(schedule, out errorMessage);
                }
                catch (Exception ex)
                {
                    // log and ignore
                    LogException(ex);
                }
            }

            rptScheduledTransaction.DataSource = scheduledTransactionList;
            rptScheduledTransaction.DataBind();
        }