コード例 #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);
                    }
                }
            }
        }
コード例 #2
0
        /// <summary>
        /// Gets the financial scheduled transaction.
        /// </summary>
        /// <param name="rockContext">The rock context.</param>
        /// <returns></returns>
        private FinancialScheduledTransaction GetFinancialScheduledTransaction(RockContext rockContext)
        {
            Guid?scheduledTransactionGuid = hfScheduledTransactionGuid.Value.AsGuidOrNull();

            if (!scheduledTransactionGuid.HasValue)
            {
                return(null);
            }

            var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
            var scheduledTransactionQuery            = financialScheduledTransactionService
                                                       .Queryable().Include(i => i.AuthorizedPersonAlias.Person)
                                                       .Where(t => t.Guid == scheduledTransactionGuid);

            // If the block allows impersonation then just get the scheduled transaction, otherwise use the code below to filter by the current person
            if (!GetAttributeValue(AttributeKey.AllowImpersonation).AsBoolean())
            {
                var personService  = new PersonService(rockContext);
                var validGivingIds = new List <string> {
                    CurrentPerson.GivingId
                };
                validGivingIds.AddRange(personService.GetBusinesses(CurrentPerson.Id).Select(b => b.GivingId));

                scheduledTransactionQuery.Where(t =>
                                                t.AuthorizedPersonAlias != null &&
                                                t.AuthorizedPersonAlias.Person != null &&
                                                validGivingIds.Contains(t.AuthorizedPersonAlias.Person.GivingId));
            }

            var scheduledTransaction = scheduledTransactionQuery.FirstOrDefault();

            return(scheduledTransaction);
        }
コード例 #3
0
        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);
        }
        // helper functional methods (like BindGrid(), etc.)
        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("ScheduledTransactionDetails.Account")
                                .Where(s => givingIds.Contains(s.AuthorizedPersonAlias.Person.GivingId) && s.IsActive == true);

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

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

                if (schedules.Count() == 0)
                {
                    pnlNoScheduledTransactions.Visible   = true;
                    lNoScheduledTransactionsMessage.Text = string.Format("No {0} currently exist.", GetAttributeValue("TransactionLabel").Pluralize().ToLower());
                }
            }
        }
コード例 #5
0
        /// <summary>
        /// Publishes the scheduled transaction event.
        /// </summary>
        /// <param name="scheduledTransactionId">The scheduled transaction identifier.</param>
        /// <param name="eventType">Type of the event.</param>
        /// <param name="gatewaySupportedCardTypesDefinedValueGuid">[Optional] The <see cref="Guid"/> of the <see cref="DefinedValue"/> that indicates the credit card types supported by the <see cref="FinancialGateway"/> for a specified currency.</param>
        /// <param name="gatewayCurrencyUnitMultiple">[Optional] The <see cref="Guid"/> of the <see cref="DefinedValue"/> that indicates the "unit multiple" (e.g., 100 for dollars) of the currency specified by the gatway.</param>
        public static void PublishScheduledTransactionEvent(int scheduledTransactionId, string eventType, Guid?gatewaySupportedCardTypesDefinedValueGuid = null, Guid?gatewayCurrencyUnitMultiple = null)
        {
            using (var rockContext = new RockContext())
            {
                var scheduleService = new FinancialScheduledTransactionService(rockContext);
                var gateway         = scheduleService.Queryable()
                                      .AsNoTracking()
                                      .Include(s => s.FinancialGateway)
                                      .Where(s => s.Id == scheduledTransactionId)
                                      .Select(s => s.FinancialGateway)
                                      .FirstOrDefault();

                var gatewayComponent     = gateway?.GetGatewayComponent();
                var searchKeyTiedGateway = gatewayComponent as ISearchKeyTiedGateway;
                var searchKeyTypeGuid    = searchKeyTiedGateway?.GetPersonSearchKeyTypeGuid(gateway);
                var data = GetScheduledGiftWasModifiedMessageData(rockContext, scheduledTransactionId, searchKeyTypeGuid, gatewaySupportedCardTypesDefinedValueGuid, gatewayCurrencyUnitMultiple);

                if (data != null)
                {
                    var statusGateway = gatewayComponent as IStatusProvidingGateway;

                    var message = new ScheduledGiftWasModifiedMessage
                    {
                        EventType = eventType,
                        Address   = data.Address,
                        FinancialScheduledTransaction = data.FinancialScheduledTransaction,
                        Person = data.Person,
                        Time   = RockDateTime.Now
                    };

                    _ = RockMessageBus.PublishAsync <GivingEventQueue, ScheduledGiftWasModifiedMessage>(message);
                }
            }
        }
コード例 #6
0
        /// <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();
        }
コード例 #7
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);
                    }
                }
            }
        }
コード例 #8
0
        /// <summary>
        /// Creates a Linq Expression that can be applied to an IQueryable to filter the result set.
        /// </summary>
        /// <param name="entityType">The type of entity in the result set.</param>
        /// <param name="serviceInstance">A service instance that can be queried to obtain the result set.</param>
        /// <param name="parameterExpression">The input parameter that will be injected into the filter expression.</param>
        /// <param name="selection">A formatted string representing the filter settings.</param>
        /// <returns>
        /// A Linq Expression that can be used to filter an IQueryable.
        /// </returns>
        /// <exception cref="System.Exception">Filter issue(s):  + errorMessages.AsDelimited( ;  )</exception>
        public override Expression GetExpression(Type entityType, IService serviceInstance, ParameterExpression parameterExpression, string selection)
        {
            var settings = new FilterSettings(selection);

            var context = (RockContext)serviceInstance.Context;

            // Get the Financial Transaction Data View.
            var dataView = DataComponentSettingsHelper.GetDataViewForFilterComponent(settings.DataViewGuid, context);

            // Evaluate the Data View that defines the Person's Financial Transaction.
            var financialScheduledTransactionService = new FinancialScheduledTransactionService(context);

            var financialScheduledTransactionQuery = financialScheduledTransactionService.Queryable();

            if (dataView != null)
            {
                financialScheduledTransactionQuery = DataComponentSettingsHelper.FilterByDataView(financialScheduledTransactionQuery, dataView, financialScheduledTransactionService);
            }

            var transactionPersonsKey = financialScheduledTransactionQuery.Select(a => a.AuthorizedPersonAliasId);
            // Get all of the Person corresponding to the qualifying Financial Transactions.
            var qry = new PersonService(context).Queryable()
                      .Where(g => g.Aliases.Any(k => transactionPersonsKey.Contains(k.Id)));

            // Retrieve the Filter Expression.
            var extractedFilterExpression = FilterExpressionExtractor.Extract <Model.Person>(qry, parameterExpression, "g");

            return(extractedFilterExpression);
        }
コード例 #9
0
        /// <summary>
        /// Handles the GridReorder event of the grdFinancialGivingProfile control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="GridReorderEventArgs"/> instance containing the event data.</param>
        private void rGridGivingProfile_GridReorder(object sender, GridReorderEventArgs e)
        {
            var profileService = new FinancialScheduledTransactionService();
            var queryable      = profileService.Queryable();

            List <FinancialScheduledTransaction> items = queryable.ToList();

            profileService.Reorder(items, e.OldIndex, e.NewIndex, CurrentPersonId);
            BindGrid();
        }
コード例 #10
0
        /// <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());
                }
            }
        }
コード例 #11
0
        private FinancialScheduledTransaction GetTransaction(RockContext rockContext)
        {
            int?txnId = PageParameter("ScheduledTransactionId").AsIntegerOrNull();

            if (txnId.HasValue)
            {
                var txnService = new FinancialScheduledTransactionService(rockContext);
                return(txnService
                       .Queryable("AuthorizedPersonAlias.Person,FinancialGateway")
                       .FirstOrDefault(t => t.Id == txnId.Value));
            }

            return(null);
        }
コード例 #12
0
        /// <summary>
        /// Gets the scheduled transaction.
        /// </summary>
        /// <returns></returns>
        private FinancialScheduledTransaction GetScheduledTransaction()
        {
            int? txnId = PageParameter( "ScheduledTransactionId" ).AsIntegerOrNull();
            if (txnId.HasValue)
            {
                var rockContext = new RockContext();
                var service = new FinancialScheduledTransactionService( rockContext );
                return service
                    .Queryable( "ScheduledTransactionDetails,FinancialGateway,FinancialPaymentDetail.CurrencyTypeValue,FinancialPaymentDetail.CreditCardTypeValue" )
                    .Where( t => t.Id == txnId.Value )
                    .FirstOrDefault();
            }

            return null;
        }
コード例 #13
0
        /// <summary>
        /// Gets the transaction.
        /// </summary>
        /// <param name="rockContext">The rock context.</param>
        /// <returns></returns>
        private FinancialScheduledTransaction GetTransaction(RockContext rockContext)
        {
            var scheduledTransactionGuid = GetScheduledTransactionGuidFromUrl();

            if (scheduledTransactionGuid.HasValue)
            {
                var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
                return(financialScheduledTransactionService
                       .Queryable()
                       .Include(a => a.AuthorizedPersonAlias.Person)
                       .Include(a => a.FinancialGateway)
                       .FirstOrDefault(t => t.Guid == scheduledTransactionGuid.Value));
            }

            return(null);
        }
コード例 #14
0
        /// <summary>
        /// Gets the transaction.
        /// </summary>
        /// <param name="rockContext">The rock context.</param>
        /// <returns></returns>
        private FinancialScheduledTransaction GetTransaction(RockContext rockContext)
        {
            int?scheduledTransactionId = PageParameter(PageParameterKey.ScheduledTransactionId).AsIntegerOrNull();

            if (scheduledTransactionId.HasValue)
            {
                var financialScheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
                return(financialScheduledTransactionService
                       .Queryable()
                       .Include(a => a.AuthorizedPersonAlias.Person)
                       .Include(a => a.FinancialGateway)
                       .FirstOrDefault(t => t.Id == scheduledTransactionId.Value));
            }

            return(null);
        }
コード例 #15
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);
        }
コード例 #16
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);
            }
        }
コード例 #17
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);
            }
        }
コード例 #18
0
        // helper functional methods (like BindGrid(), etc.)
        private void ShowContent()
        {
            // get scheduled contributions 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.PersonId == CurrentPerson.Id && s.IsActive == true);

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

                if (schedules.Count() == 0)
                {
                    pnlNoScheduledTransactions.Visible   = true;
                    lNoScheduledTransactionsMessage.Text = string.Format("No {0} currently exist.", GetAttributeValue("TransactionLabel").Pluralize().ToLower());
                }
            }
        }
コード例 #19
0
        /// <summary>
        /// Gets the scheduled transaction.
        /// </summary>
        /// <returns></returns>
        private FinancialScheduledTransaction GetScheduledTransaction()
        {
            var financialScheduledTransactionGuid = GetScheduledTransactionGuidFromUrl();

            if (financialScheduledTransactionGuid.HasValue)
            {
                var rockContext = new RockContext();
                var service     = new FinancialScheduledTransactionService(rockContext);

                return(service
                       .Queryable()
                       .Include(s => s.ScheduledTransactionDetails)
                       .Include(s => s.FinancialGateway)
                       .Include(s => s.FinancialPaymentDetail.CurrencyTypeValue)
                       .Include(s => s.FinancialPaymentDetail.CreditCardTypeValue)
                       .Where(t => t.Guid == financialScheduledTransactionGuid.Value)
                       .FirstOrDefault());
            }

            return(null);
        }
コード例 #20
0
        /// <summary>
        /// Gets the scheduled transaction.
        /// </summary>
        /// <returns></returns>
        private FinancialScheduledTransaction GetScheduledTransaction()
        {
            int?financialScheduledTransactionId = PageParameter(PageParameterKey.ScheduledTransactionId).AsIntegerOrNull();

            if (financialScheduledTransactionId.HasValue)
            {
                var rockContext = new RockContext();
                var service     = new FinancialScheduledTransactionService(rockContext);

                return(service
                       .Queryable()
                       .Include(s => s.ScheduledTransactionDetails)
                       .Include(s => s.FinancialGateway)
                       .Include(s => s.FinancialPaymentDetail.CurrencyTypeValue)
                       .Include(s => s.FinancialPaymentDetail.CreditCardTypeValue)
                       .Where(t => t.Id == financialScheduledTransactionId.Value)
                       .FirstOrDefault());
            }

            return(null);
        }
コード例 #21
0
        /// <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();
        }
コード例 #22
0
        /// <summary>
        /// Safely load entities that have not yet been assigned a non-null value based on the arguments.
        /// </summary>
        private void LoadEntities()
        {
            if (_automatedPaymentArgs.ScheduledTransactionId.HasValue && _financialScheduledTransaction == null)
            {
                _financialScheduledTransaction = _financialScheduledTransactionService.Queryable()
                                                 .AsNoTracking()
                                                 .Include(s => s.TransactionFrequencyValue)
                                                 .FirstOrDefault(s => s.Id == _automatedPaymentArgs.ScheduledTransactionId.Value);
            }

            if (_authorizedPerson == null)
            {
                _authorizedPerson = _personAliasService.GetPersonNoTracking(_automatedPaymentArgs.AuthorizedPersonAliasId);
            }

            if (_financialGateway == null)
            {
                _financialGateway = _financialGatewayService.GetNoTracking(_automatedPaymentArgs.AutomatedGatewayId);
            }

            if (_financialGateway != null && _automatedGatewayComponent == null)
            {
                _automatedGatewayComponent = _financialGateway.GetGatewayComponent();
            }

            if (_financialAccounts == null)
            {
                var accountIds = _automatedPaymentArgs.AutomatedPaymentDetails.Select(d => d.AccountId).ToList();
                _financialAccounts = _financialAccountService.GetByIds(accountIds).AsNoTracking().ToDictionary(fa => fa.Id, fa => fa);
            }

            if (_authorizedPerson != null && _financialPersonSavedAccount == null && _financialGateway != null)
            {
                // Pick the correct saved account based on args or default for the user
                var financialGatewayId = _financialGateway.Id;

                var savedAccounts = _financialPersonSavedAccountService
                                    .GetByPersonId(_authorizedPerson.Id)
                                    .AsNoTracking()
                                    .Where(sa => sa.FinancialGatewayId == financialGatewayId)
                                    .Include(sa => sa.FinancialPaymentDetail)
                                    .OrderByDescending(sa => sa.CreatedDateTime ?? DateTime.MinValue)
                                    .ToList();

                if (_automatedPaymentArgs.FinancialPersonSavedAccountId.HasValue)
                {
                    // If there is an indicated saved account to use, don't assume any other saved account even with a schedule
                    var savedAccountId = _automatedPaymentArgs.FinancialPersonSavedAccountId.Value;
                    _financialPersonSavedAccount = savedAccounts.FirstOrDefault(sa => sa.Id == savedAccountId);
                }
                else
                {
                    // If there is a schedule and no indicated saved account to use, try to use payment info associated with the schedule
                    if (_financialScheduledTransaction != null)
                    {
                        _financialPersonSavedAccount =
                            // sa.ReferenceNumber == fst.TransactionCode
                            savedAccounts.FirstOrDefault(sa => !string.IsNullOrEmpty(sa.ReferenceNumber) && sa.ReferenceNumber == _financialScheduledTransaction.TransactionCode) ??
                            // sa.GatewayPersonIdentifier == fst.TransactionCode
                            savedAccounts.FirstOrDefault(sa => !string.IsNullOrEmpty(sa.GatewayPersonIdentifier) && sa.GatewayPersonIdentifier == _financialScheduledTransaction.TransactionCode) ??
                            // sa.FinancialPaymentDetailId == fst.FinancialPaymentDetailId
                            savedAccounts.FirstOrDefault(sa => sa.FinancialPaymentDetailId.HasValue && sa.FinancialPaymentDetailId == _financialScheduledTransaction.FinancialPaymentDetailId) ??
                            // sa.TransactionCode == fst.TransactionCode
                            savedAccounts.FirstOrDefault(sa => !string.IsNullOrEmpty(sa.TransactionCode) && sa.TransactionCode == _financialScheduledTransaction.TransactionCode);
                    }

                    if (_financialPersonSavedAccount == null)
                    {
                        // Use the default or first if no default
                        _financialPersonSavedAccount =
                            savedAccounts.FirstOrDefault(sa => sa.IsDefault) ??
                            savedAccounts.FirstOrDefault();
                    }
                }
            }

            if (_financialPersonSavedAccount != null && _referencePaymentInfo == null)
            {
                _referencePaymentInfo = _financialPersonSavedAccount.GetReferencePayment();
            }

            if (_transactionType == null)
            {
                _transactionType = DefinedValueCache.Get(_automatedPaymentArgs.TransactionTypeGuid ?? SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid());
            }

            if (_financialSource == null)
            {
                _financialSource = DefinedValueCache.Get(_automatedPaymentArgs.FinancialSourceGuid ?? SystemGuid.DefinedValue.FINANCIAL_SOURCE_TYPE_WEBSITE.AsGuid());
            }
        }
コード例 #23
0
        /// <summary>
        /// Gets the message data for a <see cref="ScheduledGiftWasModifiedMessage"/>.
        /// </summary>
        /// <param name="rockContext">The rock context.</param>
        /// <param name="scheduledTransactionId">The scheduled transaction identifier.</param>
        /// <param name="personSearchKeyTypeGuid">The person search key type unique identifier.</param>
        /// <param name="gatewaySupportedCardTypesDefinedValueGuid">[Optional] The <see cref="Guid"/> of the <see cref="DefinedValue"/> that indicates the credit card types supported by the <see cref="FinancialGateway"/> for a specified currency.</param>
        /// <param name="gatewayCurrencyUnitMultiple">[Optional] The <see cref="Guid"/> of the <see cref="DefinedValue"/> that indicates the "unit multiple" (e.g., 100 for dollars) of the currency specified by the gatway.</param>
        /// <returns></returns>
        private static ScheduledGiftWasModifiedMessageData GetScheduledGiftWasModifiedMessageData(RockContext rockContext, int scheduledTransactionId, Guid?personSearchKeyTypeGuid, Guid?gatewaySupportedCardTypesDefinedValueGuid, Guid?gatewayCurrencyUnitMultiple)
        {
            var status        = "success";
            var statusMessage = string.Empty;

            var scheduledTransactionService = new FinancialScheduledTransactionService(rockContext);
            var scheduledTransaction        = scheduledTransactionService.Queryable()
                                              .AsNoTracking()
                                              .Where(s => s.Id == scheduledTransactionId)
                                              .FirstOrDefault();

            var data = new ScheduledGiftWasModifiedMessageData
            {
                Person = new TransactionPersonView
                {
                    PrimaryAliasId = scheduledTransaction.AuthorizedPersonAliasId,
                    Id             = scheduledTransaction.AuthorizedPersonAlias.Person.Id,
                    Guid           = scheduledTransaction.Guid,
                    FirstName      = scheduledTransaction.AuthorizedPersonAlias.Person.FirstName,
                    NickName       = scheduledTransaction.AuthorizedPersonAlias.Person.NickName,
                    LastName       = scheduledTransaction.AuthorizedPersonAlias.Person.LastName,
                    Email          = scheduledTransaction.AuthorizedPersonAlias.Person.Email,
                    ForeignId      = scheduledTransaction.AuthorizedPersonAlias.Person.ForeignId,
                },
                FinancialScheduledTransaction = new ScheduledTransactionView
                {
                    Id   = scheduledTransaction.Id,
                    Guid = scheduledTransaction.Guid,
                    CreditCardTypeValueId = scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId,
                    CurrencyTypeValueId   = scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId,
                    ForeignCurrencyCode   = new TransactionCurrencyCodeView
                    {
                        ValueId = scheduledTransaction.ForeignCurrencyCodeValueId,
                    },
                    ScheduledTransactionId    = scheduledTransaction.Id,
                    TransactionFrequencyValue = new TransactionFrequencyView {
                        ValueId = scheduledTransaction.TransactionFrequencyValueId,
                    },
                    SourceTypeValueId      = scheduledTransaction.SourceTypeValueId,
                    Status                 = status,
                    StatusMessage          = statusMessage,
                    TransactionCode        = scheduledTransaction.TransactionCode,
                    TransactionTypeValueId = scheduledTransaction.TransactionTypeValueId,
                    ForeignKey             = scheduledTransaction.ForeignKey,
                    Details                = scheduledTransaction.ScheduledTransactionDetails.Select(d => new ScheduledTransactionDetailView
                    {
                        Id                = d.Id,
                        Guid              = d.Guid,
                        AccountId         = d.AccountId,
                        AccountName       = d.Account.Name,
                        PublicAccountName = d.Account.PublicName,

                        /* Shaun Cummings - September 10, 2021
                         *
                         * For scheduled transactions, if the transaction has a ForeignCurrencyCode, the Amount property
                         * reflects the amount in that foreign currency.
                         * */
                        Amount = d.Amount
                    }).ToList()
                }
            };

            if (data != null)
            {
                data.Person.HydratePersonData(rockContext, data.Address, personSearchKeyTypeGuid);
                data.FinancialScheduledTransaction.HydrateDefinedValues(gatewaySupportedCardTypesDefinedValueGuid, gatewayCurrencyUnitMultiple);
            }

            return(data);
        }
コード例 #24
0
        /// <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();
            }
        }
コード例 #25
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;
        }