Service/Data access class for Rock.Model.FinancialTransaction entity objects.
Exemple #1
0
        /// <summary>
        /// Sets the status.
        /// </summary>
        /// <param name="scheduledTransaction">The scheduled transaction.</param>
        /// <param name="errorMessages">The error messages.</param>
        /// <returns></returns>
        public bool GetStatus(FinancialScheduledTransaction scheduledTransaction, out string errorMessages)
        {
            if (scheduledTransaction != null &&
                scheduledTransaction.FinancialGateway != null &&
                scheduledTransaction.FinancialGateway.IsActive)
            {
                var rockContext = this.Context as RockContext;
                if (scheduledTransaction.FinancialGateway.Attributes == null)
                {
                    scheduledTransaction.FinancialGateway.LoadAttributes(rockContext);
                }

                var gateway = scheduledTransaction.FinancialGateway.GetGatewayComponent();
                if (gateway != null)
                {
                    var result = gateway.GetScheduledPaymentStatus(scheduledTransaction, out errorMessages);

                    var scheduledTransactionId = scheduledTransaction.Id;
                    var lastTransactionDate    = new FinancialTransactionService(rockContext).Queryable().Where(a => a.ScheduledTransactionId.HasValue && a.ScheduledTransactionId == scheduledTransactionId && a.TransactionDateTime.HasValue).Max(t => ( DateTime? )t.TransactionDateTime.Value);
                    scheduledTransaction.NextPaymentDate = gateway.GetNextPaymentDate(scheduledTransaction, lastTransactionDate);
                    if (scheduledTransaction.TransactionFrequencyValueId == DefinedValueCache.GetId(Rock.SystemGuid.DefinedValue.TRANSACTION_FREQUENCY_ONE_TIME.AsGuid()))
                    {
                        if (!scheduledTransaction.NextPaymentDate.HasValue || scheduledTransaction.NextPaymentDate < RockDateTime.Now)
                        {
                            scheduledTransaction.IsActive = false;
                        }
                    }

                    return(result);
                }
            }

            errorMessages = "Gateway is invalid or not active";
            return(false);
        }
Exemple #2
0
        /// <summary>
        /// Sets the status.
        /// </summary>
        /// <param name="scheduledTransaction">The scheduled transaction.</param>
        /// <param name="errorMessages">The error messages.</param>
        /// <returns></returns>
        public bool GetStatus(FinancialScheduledTransaction scheduledTransaction, out string errorMessages)
        {
            if (scheduledTransaction != null &&
                scheduledTransaction.FinancialGateway != null &&
                scheduledTransaction.FinancialGateway.IsActive)
            {
                var rockContext = this.Context as RockContext;
                if (scheduledTransaction.FinancialGateway.Attributes == null)
                {
                    scheduledTransaction.FinancialGateway.LoadAttributes(rockContext);
                }

                var gateway = scheduledTransaction.FinancialGateway.GetGatewayComponent();
                if (gateway != null)
                {
                    var result = gateway.GetScheduledPaymentStatus(scheduledTransaction, out errorMessages);

                    var lastTransactionDate = new FinancialTransactionService(rockContext).Queryable().Where(a => a.ScheduledTransactionId.HasValue && a.ScheduledTransactionId == scheduledTransaction.Id).Max(t => t.TransactionDateTime);
                    scheduledTransaction.NextPaymentDate = gateway.GetNextPaymentDate(scheduledTransaction, lastTransactionDate);

                    return(result);
                }
            }

            errorMessages = "Gateway is invalid or not active";
            return(false);
        }
Exemple #3
0
        /// <summary>
        /// Handles the GridReorder event of the rGridTransactions 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 rGridTransactions_GridReorder(object sender, GridReorderEventArgs e)
        {
            var batchService = new Rock.Model.FinancialTransactionService();
            var queryable    = batchService.Queryable();

            List <Rock.Model.FinancialTransaction> items = queryable.ToList();

            batchService.Reorder(items, e.OldIndex, e.NewIndex, CurrentPersonId);
            BindGrid();
        }
Exemple #4
0
        /// <summary>
        /// Handles the Delete event of the grdFinancialTransactions control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="RowEventArgs"/> instance containing the event data.</param>
        protected void rGridTransactions_Delete(object sender, RowEventArgs e)
        {
            var  financialTransactionService = new Rock.Model.FinancialTransactionService();
            Grid grdTransactions             = sender as Grid;

            Rock.Model.FinancialTransaction financialTransaction = financialTransactionService.Get((int)grdTransactions.DataKeys[e.RowIndex]["id"]);
            if (financialTransaction != null)
            {
                financialTransactionService.Delete(financialTransaction, CurrentPersonId);
                financialTransactionService.Save(financialTransaction, CurrentPersonId);
            }
        }
        /// <summary>
        /// Shows the detail.
        /// </summary>
        /// <param name="itemKey">The item key.</param>
        /// <param name="itemKeyValue">The item key value.</param>
        public void ShowDetail( string itemKey, int itemKeyValue )
        {
            if ( !itemKey.Equals( "transactionId" ) && !!itemKey.Equals( "batchfk" ) )
            {
                return;
            }

            FinancialTransaction transaction = null;
            if ( !itemKeyValue.Equals( 0 ) )
            {
                transaction = new FinancialTransactionService( new RockContext() ).Get( itemKeyValue );
            }
            else
            {
                transaction = new FinancialTransaction { Id = 0 };
            }

            hfIdTransValue.Value = transaction.Id.ToString();
            hfBatchId.Value = PageParameter( "financialBatchId" );

            if ( !readOnly )
            {
                lbEdit.Visible = true;
                if ( transaction.Id > 0 )
                {
                    ShowSummary( transaction );
                }
                else
                {
                    BindDropdowns();
                    ShowEdit( transaction );
                }

                gTransactionDetails.Actions.ShowAdd = true;
                gTransactionDetails.IsDeleteEnabled = true;
                pnlImageUpload.Visible = true;
            }
            else
            {
                lbEdit.Visible = false;
                ShowSummary( transaction );
                gTransactionDetails.Actions.ShowAdd = false;
                gTransactionDetails.IsDeleteEnabled = false;
                pnlImageUpload.Visible = false;
            }

            lbSave.Visible = !readOnly;
            LoadAccountDropDown();
            BindTransactionDetailGrid( transaction );
            BindDefinedTypeDropdown( ddlTransactionImageType, new Guid( Rock.SystemGuid.DefinedType.FINANCIAL_TRANSACTION_IMAGE_TYPE ), "Transaction Image Type" );
            LoadRelatedImages( transaction.Id );
        }
        /// <summary>
        /// Handles the Delete event of the rGridTransactions control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Rock.Web.UI.Controls.RowEventArgs"/> instance containing the event data.</param>
        protected void rGridTransactions_Delete(object sender, Rock.Web.UI.Controls.RowEventArgs e)
        {
            var financialTransactionService = new Rock.Model.FinancialTransactionService();

            FinancialTransaction financialTransaction = financialTransactionService.Get((int)e.RowKeyValue);

            if (financialTransaction != null)
            {
                financialTransactionService.Delete(financialTransaction, CurrentPersonId);
                financialTransactionService.Save(financialTransaction, CurrentPersonId);
            }

            BindGrid();
        }
Exemple #7
0
        /// <summary>
        /// Process a refund for a transaction.
        /// </summary>
        /// <param name="transaction">The refund transaction.</param>
        /// <param name="amount">The amount.</param>
        /// <param name="reasonValueId">The reason value identifier.</param>
        /// <param name="summary">The summary.</param>
        /// <param name="process">if set to <c>true</c> [process].</param>
        /// <param name="batchNameSuffix">The batch name suffix.</param>
        /// <param name="errorMessage">The error message.</param>
        /// <returns></returns>
        public static FinancialTransaction ProcessRefund(this FinancialTransaction transaction, decimal?amount, int?reasonValueId, string summary, bool process, string batchNameSuffix, out string errorMessage)
        {
            using (var rockContext = new RockContext())
            {
                var service           = new FinancialTransactionService(rockContext);
                var refundTransaction = service.ProcessRefund(transaction, null, null, string.Empty, true, string.Empty, out errorMessage);

                if (refundTransaction != null)
                {
                    rockContext.SaveChanges();
                }

                return(refundTransaction);
            }
        }
Exemple #8
0
        /// <summary>
        /// Method that will be called on an entity immediately after the item is saved
        /// </summary>
        /// <param name="dbContext">The database context.</param>
        public override void PostSaveChanges(Data.DbContext dbContext)
        {
            if (HistoryChangeList.Any())
            {
                HistoryService.SaveChanges(( RockContext )dbContext, typeof(FinancialTransaction), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), this.TransactionId, HistoryChangeList, true, this.ModifiedByPersonAliasId);

                var txn = new FinancialTransactionService(( RockContext )dbContext).GetSelect(this.TransactionId, s => new { s.Id, s.BatchId });
                if (txn != null && txn.BatchId != null)
                {
                    var batchHistory = new History.HistoryChangeList();
                    batchHistory.AddChange(History.HistoryVerb.Modify, History.HistoryChangeType.Record, $"Transaction ID:{txn.Id}");
                    HistoryService.SaveChanges(( RockContext )dbContext, typeof(FinancialBatch), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), txn.BatchId.Value, batchHistory, string.Empty, typeof(FinancialTransaction), this.TransactionId, true, this.ModifiedByPersonAliasId, dbContext.SourceOfChange);
                }
            }

            base.PostSaveChanges(dbContext);
        }
        /// <summary>
        /// Method that will be called on an entity immediately after the item is saved
        /// </summary>
        /// <param name="dbContext">The database context.</param>
        public override void PostSaveChanges(DbContext dbContext)
        {
            if (HistoryChanges.Any())
            {
                HistoryService.SaveChanges((RockContext)dbContext, typeof(FinancialTransaction), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), this.TransactionId, HistoryChanges, true, this.ModifiedByPersonAliasId);

                var txn = new FinancialTransactionService((RockContext)dbContext).Get(this.TransactionId);
                if (txn != null && txn.BatchId != null)
                {
                    var batchHistory = new List <string> {
                        string.Format("Updated <span class='field-name'>Transaction</span> ID: <span class='field-value'>{0}</span>.", txn.Id)
                    };
                    HistoryService.SaveChanges((RockContext)dbContext, typeof(FinancialBatch), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), txn.BatchId.Value, batchHistory, string.Empty, typeof(FinancialTransaction), this.TransactionId, true, this.ModifiedByPersonAliasId);
                }
            }

            base.PostSaveChanges(dbContext);
        }
        /// <summary>
        /// When implemented by a class, enables a server control to process an event raised when a form is posted to the server.
        /// </summary>
        /// <param name="eventArgument">A <see cref="T:System.String" /> that represents an optional event argument to be passed to the event handler.</param>
        public void RaisePostBackEvent( string eventArgument )
        {
            if ( _canEdit && _batch != null )
            {
                if ( eventArgument == "MoveTransactions" &&
                    _ddlMove != null &&
                    _ddlMove.SelectedValue != null &&
                    !String.IsNullOrWhiteSpace( _ddlMove.SelectedValue ) )
                {
                    var txnsSelected = new List<int>();
                    gTransactions.SelectedKeys.ToList().ForEach( b => txnsSelected.Add( b.ToString().AsInteger() ) );

                    if ( txnsSelected.Any() )
                    {
                        var rockContext = new RockContext();
                        var batchService = new FinancialBatchService( rockContext );

                        var newBatch = batchService.Get( _ddlMove.SelectedValue.AsInteger() );
                        var oldBatch = batchService.Get( _batch.Id );

                        if ( oldBatch != null && newBatch != null && newBatch.Status == BatchStatus.Open )
                        {
                            var txnService = new FinancialTransactionService( rockContext );
                            var txnsToUpdate = txnService.Queryable( "AuthorizedPersonAlias.Person" )
                                .Where( t => txnsSelected.Contains( t.Id ) )
                                .ToList();

                            decimal oldBatchControlAmount = oldBatch.ControlAmount;
                            decimal newBatchControlAmount = newBatch.ControlAmount;

                            foreach ( var txn in txnsToUpdate )
                            {
                                string caption = ( txn.AuthorizedPersonAlias != null && txn.AuthorizedPersonAlias.Person != null ) ?
                                    txn.AuthorizedPersonAlias.Person.FullName :
                                    string.Format( "Transaction: {0}", txn.Id );

                                var changes = new List<string>();
                                History.EvaluateChange( changes, "Batch",
                                    string.Format( "{0} (Id:{1})", oldBatch.Name, oldBatch.Id ),
                                    string.Format( "{0} (Id:{1})", newBatch.Name, newBatch.Id ) );

                                HistoryService.SaveChanges(
                                    rockContext,
                                    typeof( FinancialBatch ),
                                    Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(),
                                    oldBatch.Id,
                                    changes,
                                    caption,
                                    typeof( FinancialTransaction ),
                                    txn.Id,
                                    false
                                );

                                HistoryService.SaveChanges(
                                    rockContext,
                                    typeof( FinancialBatch ),
                                    Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(),
                                    newBatch.Id,
                                    changes,
                                    caption,
                                    typeof( FinancialTransaction ),
                                    txn.Id, false
                                );

                                txn.BatchId = newBatch.Id;
                                oldBatchControlAmount -= txn.TotalAmount;
                                newBatchControlAmount += txn.TotalAmount;
                            }

                            var oldBatchChanges = new List<string>();
                            History.EvaluateChange( oldBatchChanges, "Control Amount", oldBatch.ControlAmount.FormatAsCurrency(), oldBatchControlAmount.FormatAsCurrency() );
                            oldBatch.ControlAmount = oldBatchControlAmount;

                            HistoryService.SaveChanges(
                                rockContext,
                                typeof( FinancialBatch ),
                                Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(),
                                oldBatch.Id,
                                oldBatchChanges,
                                false
                            );

                            var newBatchChanges = new List<string>();
                            History.EvaluateChange( newBatchChanges, "Control Amount", newBatch.ControlAmount.FormatAsCurrency(), newBatchControlAmount.FormatAsCurrency() );
                            newBatch.ControlAmount = newBatchControlAmount;

                            HistoryService.SaveChanges(
                                rockContext,
                                typeof( FinancialBatch ),
                                Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(),
                                newBatch.Id,
                                newBatchChanges,
                                false
                            );

                            rockContext.SaveChanges();

                            var pageRef = new Rock.Web.PageReference( RockPage.PageId );
                            pageRef.Parameters = new Dictionary<string, string>();
                            pageRef.Parameters.Add( "batchid", newBatch.Id.ToString() );
                            string newBatchLink = string.Format( "<a href='{0}'>{1}</a>",
                                pageRef.BuildUrl(), newBatch.Name );

                            RockPage.UpdateBlocks( "~/Blocks/Finance/BatchDetail.ascx" );

                            nbResult.Text = string.Format( "{0} transactions were moved to the '{1}' batch.",
                                txnsToUpdate.Count().ToString( "N0" ), newBatchLink );
                            nbResult.NotificationBoxType = NotificationBoxType.Success;
                            nbResult.Visible = true;
                        }
                        else
                        {
                            nbResult.Text = string.Format( "The selected batch does not exist, or is no longer open." );
                            nbResult.NotificationBoxType = NotificationBoxType.Danger;
                            nbResult.Visible = true;
                        }
                    }
                    else
                    {
                        nbResult.Text = string.Format( "There were not any transactions selected." );
                        nbResult.NotificationBoxType = NotificationBoxType.Warning;
                        nbResult.Visible = true;
                    }
                }

                _ddlMove.SelectedIndex = 0;
            }

            BindGrid();
        }
        /// <summary>
        /// Handles the SaveClick event of the dlgReassign 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 dlgReassign_SaveClick( object sender, EventArgs e )
        {
            if ( _canEdit && _person != null )
            {
                int? personAliasId = ppReassign.PersonAliasId;
                var txnsSelected = new List<int>();
                gTransactions.SelectedKeys.ToList().ForEach( b => txnsSelected.Add( b.ToString().AsInteger() ) );

                if ( txnsSelected.Any() && personAliasId.HasValue )
                {
                    var rockContext = new RockContext();
                    var txnService = new FinancialTransactionService( rockContext );
                    var txnsToUpdate = txnService.Queryable( "AuthorizedPersonAlias.Person" )
                        .Where( t => txnsSelected.Contains( t.Id ) )
                        .ToList();

                    foreach ( var txn in txnsToUpdate )
                    {
                        txn.AuthorizedPersonAliasId = personAliasId.Value;
                    }

                    rockContext.SaveChanges();
                }
            }

            HideDialog();
            BindGrid();
        }
        private bool ProcessStep3( string resultQueryString, out string errorMessage )
        {
            var rockContext = new RockContext();

            var transactionGuid = hfTransactionGuid.Value.AsGuid();

            bool isACHTxn = hfPaymentTab.Value == "ACH";
            var financialGateway = isACHTxn ? _achGateway : _ccGateway;
            var gateway = ( isACHTxn ? _achGatewayComponent : _ccGatewayComponent ) as ThreeStepGatewayComponent;

            if ( gateway == null )
            {
                errorMessage = "There was a problem creating the payment gateway information";
                return false;
            }

            // only create/update the person if they are giving as a person. If they are giving as a Business, the person record already exists
            Person person = GetPerson( !phGiveAsOption.Visible || tglGiveAsOption.Checked );

            if ( person == null )
            {
                errorMessage = "There was a problem creating the person information";
                return false;
            }

            if ( !person.PrimaryAliasId.HasValue )
            {
                errorMessage = "There was a problem creating the person's primary alias";
                return false;
            }

            Person BusinessOrPerson = GetPersonOrBusiness( person );

            PaymentInfo paymentInfo = GetPaymentInfo();
            if ( paymentInfo == null )
            {
                errorMessage = "There was a problem creating the payment information";
                return false;
            }
            else
            {
                paymentInfo.FirstName = person.FirstName;
                paymentInfo.LastName = person.LastName;
            }

            if ( paymentInfo.CreditCardTypeValue != null )
            {
                CreditCardTypeValueId = paymentInfo.CreditCardTypeValue.Id;
            }

            if ( GetAttributeValue( "EnableCommentEntry" ).AsBoolean() )
            {
                paymentInfo.Comment1 = !string.IsNullOrWhiteSpace( GetAttributeValue( "PaymentComment" ) ) ? string.Format( "{0}: {1}", GetAttributeValue( "PaymentComment" ), txtCommentEntry.Text ) : txtCommentEntry.Text;
            }
            else
            {
                paymentInfo.Comment1 = GetAttributeValue( "PaymentComment" );
            }

            var transactionAlreadyExists = new FinancialTransactionService( rockContext ).Queryable().FirstOrDefault( a => a.Guid == transactionGuid );
            if ( transactionAlreadyExists != null )
            {
                // hopefully shouldn't happen, but just in case the transaction already went thru, show the success screen
                ShowSuccess( gateway, person, paymentInfo, null, transactionAlreadyExists.FinancialPaymentDetail, rockContext );
                errorMessage = string.Empty;
                return true;
            }

            PaymentSchedule schedule = GetSchedule();
            FinancialPaymentDetail paymentDetail = null;
            if ( schedule != null )
            {
                var scheduledTransaction = gateway.AddScheduledPaymentStep3( financialGateway, resultQueryString, out errorMessage );
                if ( scheduledTransaction == null )
                {
                    return false;
                }

                paymentDetail = scheduledTransaction.FinancialPaymentDetail.Clone( false );
                SaveScheduledTransaction( financialGateway, gateway, BusinessOrPerson, paymentInfo, schedule, scheduledTransaction, rockContext );
            }
            else
            {
                var transaction = gateway.ChargeStep3( financialGateway, resultQueryString, out errorMessage );
                if ( transaction == null || !string.IsNullOrWhiteSpace( errorMessage ) )
                {
                    return false;
                }

                // manually assign the Guid that we generated at the beginning of the transaction UI entry to help make duplicate transactions impossible
                transaction.Guid = transactionGuid;

                paymentDetail = transaction.FinancialPaymentDetail.Clone( false );
                SaveTransaction( financialGateway, gateway, BusinessOrPerson, paymentInfo, transaction, rockContext );
            }

            ShowSuccess( gateway, person, paymentInfo, schedule, paymentDetail, rockContext );

            errorMessage = string.Empty;
            return true;
        }
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid()
        {
            RockContext rockContext = new RockContext();
            FinancialTransactionService transService = new FinancialTransactionService( rockContext );

            // get list of selected accounts
            List<int> selectedAccountIds = cblAccounts.Items.Cast<ListItem>()
                                            .Where( i => i.Selected == true )
                                            .Select( i => int.Parse( i.Value ) ).ToList();

            var qry = transService.Queryable("TransactionDetails.Account")
                        .Where( t => t.TransactionDetails.Any( d => selectedAccountIds.Contains( d.AccountId ) )
                                && t.AuthorizedPerson.GivingGroupId == CurrentPerson.GivingGroupId );

            if (drpFilterDates.LowerValue.HasValue) {
                qry = qry.Where(t => t.TransactionDateTime.Value >= drpFilterDates.LowerValue.Value);
            }

            if ( drpFilterDates.UpperValue.HasValue )
            {
                var lastDate = drpFilterDates.UpperValue.Value.AddDays( 1 ); // add one day to ensure we get all transactions till midnight
                qry = qry.Where( t => t.TransactionDateTime.Value < lastDate );
            }

            gTransactions.DataSource = qry.ToList();
            gTransactions.DataBind();

            // get account totals
            Dictionary<string, decimal> accountTotals = new Dictionary<string, decimal>();

            foreach ( var transaction in qry.ToList() )
            {
                foreach ( var transactionDetail in transaction.TransactionDetails )
                {
                    if ( accountTotals.Keys.Contains( transactionDetail.Account.Name ) )
                    {
                        accountTotals[transactionDetail.Account.Name] += transactionDetail.Amount;
                    }
                    else
                    {
                        accountTotals.Add( transactionDetail.Account.Name, transactionDetail.Amount );
                    }
                }
            }

            if ( accountTotals.Count > 0 )
            {
                pnlSummary.Visible = true;
                foreach ( var key in accountTotals.Keys )
                {
                    lAccountSummary.Text += String.Format( "<li>{0}: ${1}</li>", key, accountTotals[key] );
                }
            }
            else
            {
                pnlSummary.Visible = false;
            }
        }
        /// <summary>
        /// Processes the payments.
        /// </summary>
        /// <param name="gateway">The gateway.</param>
        /// <param name="batchNamePrefix">The batch name prefix.</param>
        /// <param name="payments">The payments.</param>
        /// <param name="batchUrlFormat">The batch URL format.</param>
        /// <param name="receiptEmail">The receipt email.</param>
        /// <returns></returns>
        public static string ProcessPayments( FinancialGateway gateway, string batchNamePrefix, List<Payment> payments, string batchUrlFormat = "", Guid? receiptEmail = null )
        {
            int totalPayments = 0;
            int totalAlreadyDownloaded = 0;
            int totalNoScheduledTransaction = 0;
            int totalAdded = 0;
            int totalReversals = 0;
            int totalStatusChanges = 0;

            var batches = new List<FinancialBatch>();
            var batchSummary = new Dictionary<Guid, List<Decimal>>();
            var initialControlAmounts = new Dictionary<Guid, decimal>();

            var txnPersonNames = new Dictionary<Guid, string>();

            var gatewayComponent = gateway.GetGatewayComponent();

            var newTransactions = new List<FinancialTransaction>();

            using ( var rockContext = new RockContext() )
            {
                var accountService = new FinancialAccountService( rockContext );
                var txnService = new FinancialTransactionService( rockContext );
                var batchService = new FinancialBatchService( rockContext );
                var scheduledTxnService = new FinancialScheduledTransactionService( rockContext );

                var contributionTxnType = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid() );

                var defaultAccount = accountService.Queryable()
                    .Where( a =>
                        a.IsActive &&
                        !a.ParentAccountId.HasValue &&
                        ( !a.StartDate.HasValue || a.StartDate.Value <= RockDateTime.Now ) &&
                        ( !a.EndDate.HasValue || a.EndDate.Value >= RockDateTime.Now )
                        )
                    .OrderBy( a => a.Order )
                    .FirstOrDefault();

                var batchTxnChanges = new Dictionary<Guid, List<string>>();
                var batchBatchChanges = new Dictionary<Guid, List<string>>();
                var scheduledTransactionIds = new List<int>();

                foreach ( var payment in payments.Where( p => p.Amount > 0.0M ) )
                {
                    totalPayments++;

                    var scheduledTransaction = scheduledTxnService.GetByScheduleId( payment.GatewayScheduleId, gateway.Id );
                    if ( scheduledTransaction != null )
                    {
                        // Find existing payments with same transaction code
                        var txns = txnService
                            .Queryable( "TransactionDetails" )
                            .Where( t => t.TransactionCode == payment.TransactionCode )
                            .ToList();

                        // Calculate whether a transaction needs to be added
                        var txnAmount = CalculateTransactionAmount( payment, txns );
                        if ( txnAmount != 0.0M )
                        {
                            scheduledTransactionIds.Add( scheduledTransaction.Id );
                            if ( payment.ScheduleActive.HasValue )
                            {
                                scheduledTransaction.IsActive = payment.ScheduleActive.Value;
                            }

                            var transaction = new FinancialTransaction();
                            transaction.FinancialPaymentDetail = new FinancialPaymentDetail();

                            transaction.Guid = Guid.NewGuid();
                            transaction.TransactionCode = payment.TransactionCode;
                            transaction.TransactionDateTime = payment.TransactionDateTime;
                            transaction.Status = payment.Status;
                            transaction.StatusMessage = payment.StatusMessage;
                            transaction.ScheduledTransactionId = scheduledTransaction.Id;
                            transaction.AuthorizedPersonAliasId = scheduledTransaction.AuthorizedPersonAliasId;
                            transaction.SourceTypeValueId = scheduledTransaction.SourceTypeValueId;
                            txnPersonNames.Add( transaction.Guid, scheduledTransaction.AuthorizedPersonAlias.Person.FullName );
                            transaction.FinancialGatewayId = gateway.Id;
                            transaction.TransactionTypeValueId = contributionTxnType.Id;

                            if ( txnAmount < 0.0M )
                            {
                                transaction.Summary = "Reversal for previous transaction that failed during processing." + Environment.NewLine;
                            }

                            var currencyTypeValue = payment.CurrencyTypeValue;
                            var creditCardTypevalue = payment.CreditCardTypeValue;

                            if ( scheduledTransaction.FinancialPaymentDetail != null )
                            {
                                if ( currencyTypeValue == null && scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.HasValue )
                                {
                                    currencyTypeValue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.Value );
                                }

                                if ( creditCardTypevalue == null && scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.HasValue )
                                {
                                    creditCardTypevalue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.Value );
                                }

                                transaction.FinancialPaymentDetail.AccountNumberMasked = scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked;
                                transaction.FinancialPaymentDetail.NameOnCardEncrypted = scheduledTransaction.FinancialPaymentDetail.NameOnCardEncrypted;
                                transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationMonthEncrypted;
                                transaction.FinancialPaymentDetail.ExpirationYearEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationYearEncrypted;
                                transaction.FinancialPaymentDetail.BillingLocationId = scheduledTransaction.FinancialPaymentDetail.BillingLocationId;
                            }

                            if ( currencyTypeValue != null )
                            {
                                transaction.FinancialPaymentDetail.CurrencyTypeValueId = currencyTypeValue.Id;
                            }
                            if ( creditCardTypevalue != null )
                            {
                                transaction.FinancialPaymentDetail.CreditCardTypeValueId = creditCardTypevalue.Id;
                            }

                            // Try to allocate the amount of the transaction based on the current scheduled transaction accounts
                            decimal remainingAmount = Math.Abs( txnAmount );
                            foreach ( var detail in scheduledTransaction.ScheduledTransactionDetails.Where( d => d.Amount != 0.0M ) )
                            {
                                var transactionDetail = new FinancialTransactionDetail();
                                transactionDetail.AccountId = detail.AccountId;

                                if ( detail.Amount <= remainingAmount )
                                {
                                    // If the configured amount for this account is less than or equal to the remaining
                                    // amount, allocate the configured amount
                                    transactionDetail.Amount = detail.Amount;
                                    remainingAmount -= detail.Amount;
                                }
                                else
                                {
                                    // If the configured amount is greater than the remaining amount, only allocate
                                    // the remaining amount
                                    transaction.Summary += "Note: Downloaded transaction amount was less than the configured allocation amounts for the Scheduled Transaction.";
                                    transactionDetail.Amount = remainingAmount;
                                    transactionDetail.Summary = "Note: The downloaded amount was not enough to apply the configured amount to this account.";
                                    remainingAmount = 0.0M;
                                }

                                transaction.TransactionDetails.Add( transactionDetail );

                                if ( remainingAmount <= 0.0M )
                                {
                                    // If there's no amount left, break out of details
                                    break;
                                }
                            }

                            // If there's still amount left after allocating based on current config, add the remainder
                            // to the account that was configured for the most amount
                            if ( remainingAmount > 0.0M )
                            {
                                transaction.Summary += "Note: Downloaded transaction amount was greater than the configured allocation amounts for the Scheduled Transaction.";
                                var transactionDetail = transaction.TransactionDetails
                                    .OrderByDescending( d => d.Amount )
                                    .FirstOrDefault();
                                if ( transactionDetail == null && defaultAccount != null )
                                {
                                    transactionDetail = new FinancialTransactionDetail();
                                    transactionDetail.AccountId = defaultAccount.Id;
                                    transaction.TransactionDetails.Add( transactionDetail );
                                }
                                if ( transactionDetail != null )
                                {
                                    transactionDetail.Amount += remainingAmount;
                                    transactionDetail.Summary = "Note: Extra amount was applied to this account.";
                                }
                            }

                            // If the amount to apply was negative, update all details to be negative (absolute value was used when allocating to accounts)
                            if ( txnAmount < 0.0M )
                            {
                                foreach ( var txnDetail in transaction.TransactionDetails )
                                {
                                    txnDetail.Amount = 0 - txnDetail.Amount;
                                }
                            }

                            // Get the batch
                            var batch = batchService.Get(
                                batchNamePrefix,
                                currencyTypeValue,
                                creditCardTypevalue,
                                transaction.TransactionDateTime.Value,
                                gateway.GetBatchTimeOffset(),
                                batches );

                            var batchChanges = new List<string>();
                            if ( batch.Id != 0 )
                            {
                                initialControlAmounts.AddOrIgnore( batch.Guid, batch.ControlAmount );
                            }
                            batch.ControlAmount += transaction.TotalAmount;

                            batch.Transactions.Add( transaction );

                            if ( txnAmount > 0.0M && receiptEmail.HasValue )
                            {
                                newTransactions.Add( transaction );
                            }

                            // Add summary
                            if ( !batchSummary.ContainsKey( batch.Guid ) )
                            {
                                batchSummary.Add( batch.Guid, new List<Decimal>() );
                            }
                            batchSummary[batch.Guid].Add( txnAmount );

                            if ( txnAmount > 0.0M )
                            {
                                totalAdded++;
                            }
                            else
                            {
                                totalReversals++;
                            }
                        }
                        else
                        {
                            totalAlreadyDownloaded++;

                            foreach ( var txn in txns.Where( t => t.Status != payment.Status || t.StatusMessage != payment.StatusMessage ) )
                            {
                                txn.Status = payment.Status;
                                txn.StatusMessage = payment.StatusMessage;
                                totalStatusChanges++;
                            }
                        }
                    }
                    else
                    {
                        totalNoScheduledTransaction++;
                    }
                }

                rockContext.SaveChanges();

                // Queue a transaction to update the status of all affected scheduled transactions
                var updatePaymentStatusTxn = new Rock.Transactions.UpdatePaymentStatusTransaction( gateway.Id, scheduledTransactionIds );
                Rock.Transactions.RockQueue.TransactionQueue.Enqueue( updatePaymentStatusTxn );

                if ( receiptEmail.HasValue )
                {
                    // Queue a transaction to send receipts
                    var newTransactionIds = newTransactions.Select( t => t.Id ).ToList();
                    var sendPaymentReceiptsTxn = new Rock.Transactions.SendPaymentReceipts( receiptEmail.Value, newTransactionIds );
                    Rock.Transactions.RockQueue.TransactionQueue.Enqueue( sendPaymentReceiptsTxn );
                }
            }

            StringBuilder sb = new StringBuilder();
            sb.AppendFormat( "<li>{0} {1} downloaded.</li>", totalPayments.ToString( "N0" ),
                ( totalPayments == 1 ? "payment" : "payments" ) );

            if ( totalAlreadyDownloaded > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} previously downloaded and {2} already been added.</li>", totalAlreadyDownloaded.ToString( "N0" ),
                    ( totalAlreadyDownloaded == 1 ? "payment was" : "payments were" ),
                    ( totalAlreadyDownloaded == 1 ? "has" : "have" ) );
            }

            if ( totalStatusChanges > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} previously downloaded but had a change of status.</li>", totalStatusChanges.ToString( "N0" ),
                ( totalStatusChanges == 1 ? "payment was" : "payments were" ) );
            }

            if ( totalNoScheduledTransaction > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} could not be matched to an existing scheduled payment profile.</li>", totalNoScheduledTransaction.ToString( "N0" ),
                    ( totalNoScheduledTransaction == 1 ? "payment" : "payments" ) );
            }

            sb.AppendFormat( "<li>{0} {1} successfully added.</li>", totalAdded.ToString( "N0" ),
                ( totalAdded == 1 ? "payment was" : "payments were" ) );

            if ( totalReversals > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} added as a reversal to a previous transaction.</li>", totalReversals.ToString( "N0" ),
                    ( totalReversals == 1 ? "payment was" : "payments were" ) );
            }

            foreach ( var batchItem in batchSummary )
            {
                int items = batchItem.Value.Count;
                if ( items > 0 )
                {
                    var batch = batches
                        .Where( b => b.Guid.Equals( batchItem.Key ) )
                        .FirstOrDefault();

                    string batchName = string.Format( "'{0} ({1})'", batch.Name, batch.BatchStartDateTime.Value.ToString( "d" ) );
                    if ( !string.IsNullOrWhiteSpace( batchUrlFormat ) )
                    {
                        batchName = string.Format( "<a href='{0}'>{1}</a>", string.Format( batchUrlFormat, batch.Id ), batchName );
                    }

                    decimal sum = batchItem.Value.Sum();

                    string summaryformat = items == 1 ?
                        "<li>{0} transaction of {1} was added to the {2} batch.</li>" :
                        "<li>{0} transactions totaling {1} were added to the {2} batch</li>";

                    sb.AppendFormat( summaryformat, items.ToString( "N0" ), sum.FormatAsCurrency(), batchName );
                }
            }

            return sb.ToString();
        }
        /// <summary>
        /// Binds the payments grid.
        /// </summary>
        private void BindPaymentsGrid()
        {
            int? instanceId = hfRegistrationInstanceId.Value.AsIntegerOrNull();
            if ( instanceId.HasValue )
            {
                using ( var rockContext = new RockContext() )
                {
                    var currencyTypes = new Dictionary<int, string>();
                    var creditCardTypes = new Dictionary<int, string>();

                    // If configured for a registration and registration is null, return
                    int registrationEntityTypeId = EntityTypeCache.Read( typeof( Rock.Model.Registration ) ).Id;

                    // Get all the registrations for this instance
                    PaymentRegistrations = new RegistrationService( rockContext )
                        .Queryable( "PersonAlias.Person,Registrants.PersonAlias.Person" ).AsNoTracking()
                        .Where( r =>
                            r.RegistrationInstanceId == instanceId.Value &&
                            !r.IsTemporary )
                        .ToList();

                    // Get the Registration Ids
                    var registrationIds = PaymentRegistrations
                        .Select( r => r.Id )
                        .ToList();

                    // Get all the transactions relate to these registrations
                    var qry = new FinancialTransactionService( rockContext )
                        .Queryable().AsNoTracking()
                        .Where( t => t.TransactionDetails
                            .Any( d =>
                                d.EntityTypeId.HasValue &&
                                d.EntityTypeId.Value == registrationEntityTypeId &&
                                d.EntityId.HasValue &&
                                registrationIds.Contains( d.EntityId.Value ) ) );

                    // Date Range
                    var drp = new DateRangePicker();
                    drp.DelimitedValues = fPayments.GetUserPreference( "Date Range" );
                    if ( drp.LowerValue.HasValue )
                    {
                        qry = qry.Where( t => t.TransactionDateTime >= drp.LowerValue.Value );
                    }

                    if ( drp.UpperValue.HasValue )
                    {
                        DateTime upperDate = drp.UpperValue.Value.Date.AddDays( 1 );
                        qry = qry.Where( t => t.TransactionDateTime < upperDate );
                    }

                    SortProperty sortProperty = gPayments.SortProperty;
                    if ( sortProperty != null )
                    {
                        if ( sortProperty.Property == "TotalAmount" )
                        {
                            if ( sortProperty.Direction == SortDirection.Ascending )
                            {
                                qry = qry.OrderBy( t => t.TransactionDetails.Sum( d => (decimal?)d.Amount ) ?? 0.00M );
                            }
                            else
                            {
                                qry = qry.OrderByDescending( t => t.TransactionDetails.Sum( d => (decimal?)d.Amount ) ?? 0.0M );
                            }
                        }
                        else
                        {
                            qry = qry.Sort( sortProperty );
                        }
                    }
                    else
                    {
                        qry = qry.OrderByDescending( t => t.TransactionDateTime ).ThenByDescending( t => t.Id );
                    }

                    gPayments.SetLinqDataSource( qry.AsNoTracking() );
                    gPayments.DataBind();
                }
            }
        }
Exemple #16
0
        /// <summary>
        /// Maps the contribution.
        /// </summary>
        /// <param name="tableData">The table data.</param>
        /// <param name="selectedColumns">The selected columns.</param>
        private void MapContribution( IQueryable<Row> tableData, List<string> selectedColumns = null )
        {
            var lookupContext = new RockContext();
            int transactionEntityTypeId = EntityTypeCache.Read( "Rock.Model.FinancialTransaction" ).Id;
            var transactionTypeContributionId = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION ), lookupContext ).Id;

            var currencyTypes = DefinedTypeCache.Read( new Guid( Rock.SystemGuid.DefinedType.FINANCIAL_CURRENCY_TYPE ) );
            int currencyTypeACH = currencyTypes.DefinedValues.FirstOrDefault( dv => dv.Guid.Equals( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_ACH ) ) ).Id;
            int currencyTypeCash = currencyTypes.DefinedValues.FirstOrDefault( dv => dv.Guid.Equals( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CASH ) ) ).Id;
            int currencyTypeCheck = currencyTypes.DefinedValues.FirstOrDefault( dv => dv.Guid.Equals( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CHECK ) ) ).Id;
            int currencyTypeCreditCard = currencyTypes.DefinedValues.FirstOrDefault( dv => dv.Guid.Equals( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD ) ) ).Id;
            int? currencyTypeNonCash = currencyTypes.DefinedValues.Where( dv => dv.Value.Equals( "Non-Cash" ) ).Select( dv => (int?)dv.Id ).FirstOrDefault();
            if ( currencyTypeNonCash == null )
            {
                var newTenderNonCash = new DefinedValue();
                newTenderNonCash.Value = "Non-Cash";
                newTenderNonCash.Description = "Non-Cash";
                newTenderNonCash.DefinedTypeId = currencyTypes.Id;
                lookupContext.DefinedValues.Add( newTenderNonCash );
                lookupContext.SaveChanges();

                currencyTypeNonCash = newTenderNonCash.Id;
            }

            var creditCardTypes = DefinedTypeCache.Read( new Guid( Rock.SystemGuid.DefinedType.FINANCIAL_CREDIT_CARD_TYPE ) ).DefinedValues;

            int sourceTypeOnsite = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.FINANCIAL_SOURCE_TYPE_ONSITE_COLLECTION ), lookupContext ).Id;
            int sourceTypeWebsite = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.FINANCIAL_SOURCE_TYPE_WEBSITE ), lookupContext ).Id;
            int sourceTypeKiosk = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.FINANCIAL_SOURCE_TYPE_KIOSK ), lookupContext ).Id;

            var refundReasons = DefinedTypeCache.Read( new Guid( Rock.SystemGuid.DefinedType.FINANCIAL_TRANSACTION_REFUND_REASON ), lookupContext ).DefinedValues;

            var accountList = new FinancialAccountService( lookupContext ).Queryable().AsNoTracking().ToList();

            int? defaultBatchId = null;
            if ( ImportedBatches.ContainsKey( 0 ) )
            {
                defaultBatchId = ImportedBatches[0];
            }

            // Get all imported contributions
            var importedContributions = new FinancialTransactionService( lookupContext ).Queryable().AsNoTracking()
               .Where( c => c.ForeignId != null )
               .ToDictionary( t => (int)t.ForeignId, t => (int?)t.Id );

            // List for batching new contributions
            var newTransactions = new List<FinancialTransaction>();

            int completed = 0;
            int totalRows = tableData.Count();
            int percentage = ( totalRows - 1 ) / 100 + 1;
            ReportProgress( 0, string.Format( "Verifying contribution import ({0:N0} found, {1:N0} already exist).", totalRows, importedContributions.Count ) );
            foreach ( var row in tableData.Where( r => r != null ) )
            {
                int? individualId = row["Individual_ID"] as int?;
                int? householdId = row["Household_ID"] as int?;
                int? contributionId = row["ContributionID"] as int?;

                if ( contributionId != null && !importedContributions.ContainsKey( (int)contributionId ) )
                {
                    var transaction = new FinancialTransaction();
                    transaction.CreatedByPersonAliasId = ImportPersonAliasId;
                    transaction.ModifiedByPersonAliasId = ImportPersonAliasId;
                    transaction.TransactionTypeValueId = transactionTypeContributionId;
                    transaction.ForeignKey = contributionId.ToString();
                    transaction.ForeignId = contributionId;

                    int? giverAliasId = null;
                    var personKeys = GetPersonKeys( individualId, householdId );
                    if ( personKeys != null && personKeys.PersonAliasId > 0 )
                    {
                        giverAliasId = personKeys.PersonAliasId;
                        transaction.CreatedByPersonAliasId = giverAliasId;
                        transaction.AuthorizedPersonAliasId = giverAliasId;
                        transaction.ProcessedByPersonAliasId = giverAliasId;
                    }

                    string summary = row["Memo"] as string;
                    if ( summary != null )
                    {
                        transaction.Summary = summary;
                    }

                    int? batchId = row["BatchID"] as int?;
                    if ( batchId != null && ImportedBatches.Any( b => b.Key.Equals( batchId ) ) )
                    {
                        transaction.BatchId = ImportedBatches.FirstOrDefault( b => b.Key.Equals( batchId ) ).Value;
                    }
                    else
                    {
                        // use the default batch for any non-matching transactions
                        transaction.BatchId = defaultBatchId;
                    }

                    DateTime? receivedDate = row["Received_Date"] as DateTime?;
                    if ( receivedDate != null )
                    {
                        transaction.TransactionDateTime = receivedDate;
                        transaction.CreatedDateTime = receivedDate;
                        transaction.ModifiedDateTime = ImportDateTime;
                    }

                    string cardType = row["Card_Type"] as string;
                    string cardLastFour = row["Last_Four"] as string;
                    string contributionType = row["Contribution_Type_Name"].ToStringSafe().ToLower();
                    if ( contributionType != null )
                    {
                        // set default source to onsite, exceptions listed below
                        transaction.SourceTypeValueId = sourceTypeOnsite;

                        int? paymentCurrencyTypeId = null, creditCardTypeId = null;

                        if ( contributionType == "cash" )
                        {
                            paymentCurrencyTypeId = currencyTypeCash;
                        }
                        else if ( contributionType == "check" )
                        {
                            paymentCurrencyTypeId = currencyTypeCheck;
                        }
                        else if ( contributionType == "ach" )
                        {
                            paymentCurrencyTypeId = currencyTypeACH;
                            transaction.SourceTypeValueId = sourceTypeWebsite;
                        }
                        else if ( contributionType == "credit card" )
                        {
                            paymentCurrencyTypeId = currencyTypeCreditCard;
                            transaction.SourceTypeValueId = sourceTypeWebsite;

                            if ( cardType != null )
                            {
                                creditCardTypeId = creditCardTypes.Where( t => t.Value.Equals( cardType ) ).Select( t => (int?)t.Id ).FirstOrDefault();
                            }
                        }
                        else
                        {
                            paymentCurrencyTypeId = currencyTypeNonCash;
                        }

                        var paymentDetail = new FinancialPaymentDetail();
                        paymentDetail.CreatedDateTime = receivedDate;
                        paymentDetail.CreatedByPersonAliasId = giverAliasId;
                        paymentDetail.ModifiedDateTime = ImportDateTime;
                        paymentDetail.ModifiedByPersonAliasId = giverAliasId;
                        paymentDetail.CurrencyTypeValueId = paymentCurrencyTypeId;
                        paymentDetail.CreditCardTypeValueId = creditCardTypeId;
                        paymentDetail.AccountNumberMasked = cardLastFour;
                        paymentDetail.ForeignKey = contributionId.ToString();
                        paymentDetail.ForeignId = contributionId;

                        transaction.FinancialPaymentDetail = paymentDetail;
                    }

                    string checkNumber = row["Check_Number"] as string;
                    // if the check number is valid, put it in the transaction code
                    if ( checkNumber.AsType<int?>() != null )
                    {
                        transaction.TransactionCode = checkNumber;
                    }
                    // check for SecureGive kiosk transactions
                    else if ( !string.IsNullOrEmpty( checkNumber ) && checkNumber.StartsWith( "SG" ) )
                    {
                        transaction.SourceTypeValueId = sourceTypeKiosk;
                    }

                    string fundName = row["Fund_Name"] as string;
                    string subFund = row["Sub_Fund_Name"] as string;
                    string fundGLAccount = row["Fund_GL_Account"] as string;
                    string subFundGLAccount = row["Sub_Fund_GL_Account"] as string;
                    Boolean? isFundActive = row["Fund_Is_active"] as Boolean?;
                    decimal? statedValue = row["Stated_Value"] as decimal?;
                    decimal? amount = row["Amount"] as decimal?;
                    if ( fundName != null & amount != null )
                    {
                        int transactionAccountId;
                        var parentAccount = accountList.FirstOrDefault( a => a.Name.Equals( fundName ) && a.CampusId == null );
                        if ( parentAccount == null )
                        {
                            parentAccount = AddAccount( lookupContext, fundName, fundGLAccount, null, null, isFundActive );
                            accountList.Add( parentAccount );
                        }

                        if ( subFund != null )
                        {
                            int? campusFundId = null;
                            // assign a campus if the subfund is a campus fund
                            var campusFund = CampusList.FirstOrDefault( c => subFund.StartsWith( c.Name ) || subFund.StartsWith( c.ShortCode ) );
                            if ( campusFund != null )
                            {
                                // use full campus name as the subfund
                                subFund = campusFund.Name;
                                campusFundId = campusFund.Id;
                            }

                            // add info to easily find/assign this fund in the view
                            subFund = string.Format( "{0} {1}", subFund, fundName );

                            var childAccount = accountList.FirstOrDefault( c => c.Name.Equals( subFund ) && c.ParentAccountId == parentAccount.Id );
                            if ( childAccount == null )
                            {
                                // create a child account with a campusId if it was set
                                childAccount = AddAccount( lookupContext, subFund, subFundGLAccount, campusFundId, parentAccount.Id, isFundActive );
                                accountList.Add( childAccount );
                            }

                            transactionAccountId = childAccount.Id;
                        }
                        else
                        {
                            transactionAccountId = parentAccount.Id;
                        }

                        if ( amount == 0 && statedValue != null && statedValue != 0 )
                        {
                            amount = statedValue;
                        }

                        var transactionDetail = new FinancialTransactionDetail();
                        transactionDetail.Amount = (decimal)amount;
                        transactionDetail.CreatedDateTime = receivedDate;
                        transactionDetail.AccountId = transactionAccountId;
                        transaction.TransactionDetails.Add( transactionDetail );

                        if ( amount < 0 )
                        {
                            transaction.RefundDetails = new FinancialTransactionRefund();
                            transaction.RefundDetails.CreatedDateTime = receivedDate;
                            transaction.RefundDetails.RefundReasonValueId = refundReasons.Where( dv => summary != null && dv.Value.Contains( summary ) )
                                .Select( dv => (int?)dv.Id ).FirstOrDefault();
                            transaction.RefundDetails.RefundReasonSummary = summary;
                        }
                    }

                    newTransactions.Add( transaction );
                    completed++;
                    if ( completed % percentage < 1 )
                    {
                        int percentComplete = completed / percentage;
                        ReportProgress( percentComplete, string.Format( "{0:N0} contributions imported ({1}% complete).", completed, percentComplete ) );
                    }
                    else if ( completed % ReportingNumber < 1 )
                    {
                        SaveContributions( newTransactions );
                        newTransactions.Clear();
                        ReportPartialProgress();
                    }
                }
            }

            if ( newTransactions.Any() )
            {
                SaveContributions( newTransactions );
            }

            ReportProgress( 100, string.Format( "Finished contribution import: {0:N0} contributions imported.", completed ) );
        }
 /// <summary>
 /// Gets the transaction.
 /// </summary>
 /// <param name="transactionId">The transaction identifier.</param>
 /// <param name="rockContext">The rock context.</param>
 /// <returns></returns>
 private FinancialTransaction GetTransaction( int transactionId, RockContext rockContext = null )
 {
     rockContext = rockContext ?? new RockContext();
     var txn = new FinancialTransactionService( rockContext )
         .Queryable( "AuthorizedPerson,TransactionTypeValue,SourceTypeValue,GatewayEntityType,CurrencyTypeValue,TransactionDetails,Images.TransactionImageTypeValue,ScheduledTransaction,ProcessedByPersonAlias.Person" )
         .Where( t => t.Id == transactionId )
         .FirstOrDefault();
     return txn;
 }
        /// <summary>
        /// Handles the Click event of the lbSave 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 lbSave_Click( object sender, EventArgs e )
        {
            var rockContext = new RockContext();

            var txnService = new FinancialTransactionService( rockContext );
            var txnDetailService = new FinancialTransactionDetailService( rockContext );
            var txnImageService = new FinancialTransactionImageService( rockContext );
            var binaryFileService = new BinaryFileService( rockContext );

            FinancialTransaction txn = null;

            int? txnId = hfTransactionId.Value.AsIntegerOrNull();
            int? batchId = hfBatchId.Value.AsIntegerOrNull();

            if ( txnId.HasValue )
            {
                txn = txnService.Get( txnId.Value );
            }

            if ( txn == null && batchId.HasValue )
            {
                txn = new FinancialTransaction();
                txnService.Add( txn );
                txn.BatchId = batchId.Value;
            }

            if ( txn != null )
            {
                txn.AuthorizedPersonId = ppAuthorizedPerson.PersonId;
                txn.TransactionDateTime = dtTransactionDateTime.SelectedDateTime;
                txn.TransactionTypeValueId = ddlTransactionType.SelectedValue.AsInteger();
                txn.SourceTypeValueId = ddlSourceType.SelectedValueAsInt();

                Guid? gatewayGuid = cpPaymentGateway.SelectedValueAsGuid();
                if ( gatewayGuid.HasValue )
                {
                    var gatewayEntity = EntityTypeCache.Read( gatewayGuid.Value );
                    if ( gatewayEntity != null )
                    {
                        txn.GatewayEntityTypeId = gatewayEntity.Id;
                    }
                    else
                    {
                        txn.GatewayEntityTypeId = null;
                    }
                }
                else
                {
                    txn.GatewayEntityTypeId = null;
                }

                txn.TransactionCode = tbTransactionCode.Text;
                txn.CurrencyTypeValueId = ddlCurrencyType.SelectedValueAsInt();
                txn.CreditCardTypeValueId = ddlCreditCardType.SelectedValueAsInt();
                txn.Summary = tbSummary.Text;

                if ( !Page.IsValid || !txn.IsValid )
                {
                    return;
                }

                foreach ( var txnDetail in TransactionDetailsState )
                {
                    if ( !txnDetail.IsValid )
                    {
                        return;
                    }
                }

                foreach ( var txnImage in TransactionImagesState )
                {
                    if ( !txnImage.IsValid )
                    {
                        return;
                    }
                }

                rockContext.WrapTransaction( () =>
                {
                    // Save the transaction
                    rockContext.SaveChanges();

                    // Delete any transaction details that were removed
                    var txnDetailsInDB = txnDetailService.Queryable().Where( a => a.TransactionId.Equals( txn.Id ) ).ToList();
                    var deletedDetails = from txnDetail in txnDetailsInDB
                                         where !TransactionDetailsState.Select( d => d.Guid ).Contains( txnDetail.Guid )
                                         select txnDetail;
                    deletedDetails.ToList().ForEach( txnDetail =>
                    {
                        txnDetailService.Delete( txnDetail );
                    } );
                    rockContext.SaveChanges();

                    // Save Transaction Details
                    foreach ( var editorTxnDetail in TransactionDetailsState )
                    {
                        // Add or Update the activity type
                        var txnDetail = txn.TransactionDetails.FirstOrDefault( d => d.Guid.Equals( editorTxnDetail.Guid ) );
                        if ( txnDetail == null )
                        {
                            txnDetail = new FinancialTransactionDetail();
                            txnDetail.Guid = editorTxnDetail.Guid;
                            txn.TransactionDetails.Add( txnDetail );
                        }
                        txnDetail.AccountId = editorTxnDetail.AccountId;
                        txnDetail.Amount = editorTxnDetail.Amount;
                        txnDetail.Summary = editorTxnDetail.Summary;
                    }
                    rockContext.SaveChanges();

                    // Remove any images that do not have a binary file
                    foreach ( var txnImage in TransactionImagesState.Where( i => i.BinaryFileId == 0 ).ToList() )
                    {
                        TransactionImagesState.Remove( txnImage );
                    }

                    // Delete any transaction images that were removed
                    var txnImagesInDB = txnImageService.Queryable().Where( a => a.TransactionId.Equals( txn.Id ) ).ToList();
                    var deletedImages = from txnImage in txnImagesInDB
                                        where !TransactionImagesState.Select( d => d.Guid ).Contains( txnImage.Guid )
                                        select txnImage;
                    deletedImages.ToList().ForEach( txnImage =>
                    {
                        txnImageService.Delete( txnImage );
                    } );
                    rockContext.SaveChanges();

                    // Save Transaction Images
                    foreach ( var editorTxnImage in TransactionImagesState )
                    {
                        // Add or Update the activity type
                        var txnImage = txn.Images.FirstOrDefault( d => d.Guid.Equals( editorTxnImage.Guid ) );
                        if ( txnImage == null )
                        {
                            txnImage = new FinancialTransactionImage();
                            txnImage.Guid = editorTxnImage.Guid;
                            txn.Images.Add( txnImage );
                        }
                        txnImage.BinaryFileId = editorTxnImage.BinaryFileId;
                        txnImage.TransactionImageTypeValueId = editorTxnImage.TransactionImageTypeValueId;
                    }
                    rockContext.SaveChanges();

                    // Make sure updated binary files are not temporary
                    var savedBinaryFileIds = txn.Images.Select( i => i.BinaryFileId ).ToList();
                    foreach ( var binaryFile in binaryFileService.Queryable().Where( f => savedBinaryFileIds.Contains( f.Id ) ) )
                    {
                        binaryFile.IsTemporary = false;
                    }

                    // Delete any orphaned images
                    var orphanedBinaryFileIds = BinaryFileIds.Where( f => !savedBinaryFileIds.Contains( f ) );
                    foreach ( var binaryFile in binaryFileService.Queryable().Where( f => orphanedBinaryFileIds.Contains( f.Id ) ) )
                    {
                        binaryFileService.Delete( binaryFile );
                    }

                    rockContext.SaveChanges();

                } );

                // Requery the batch to support EF navigation properties
                var savedTxn = GetTransaction( txn.Id );
                ShowReadOnlyDetails( savedTxn );
            }
        }
Exemple #19
0
        /// <summary>
        /// Gets the expression.
        /// </summary>
        /// <param name="entityType">Type of the entity.</param>
        /// <param name="serviceInstance">The service instance.</param>
        /// <param name="parameterExpression">The parameter expression.</param>
        /// <param name="selection">The selection.</param>
        /// <returns></returns>
        public override Expression GetExpression( Type entityType, IService serviceInstance, ParameterExpression parameterExpression, string selection )
        {
            var values = selection.Split( '|' );

            ComparisonType comparisonType = values[0].ConvertToEnum<ComparisonType>( ComparisonType.EqualTo );
            decimal? amountValue = values[1].AsDecimalOrNull();

            var qry = new FinancialTransactionService( (RockContext)serviceInstance.Context ).Queryable();
            var totalAmountEqualQuery = qry.Where( p => p.TransactionDetails.Sum(a => a.Amount) == amountValue );

            BinaryExpression compareEqualExpression = FilterExpressionExtractor.Extract<Rock.Model.FinancialTransaction>( totalAmountEqualQuery, parameterExpression, "p" ) as BinaryExpression;
            BinaryExpression result = FilterExpressionExtractor.AlterComparisonType( comparisonType, compareEqualExpression, null );

            return result;
        }
Exemple #20
0
        /// <summary>
        /// Gets the expression.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="entityIdProperty">The entity identifier property.</param>
        /// <param name="selection">The selection.</param>
        /// <returns></returns>
        public override Expression GetExpression( RockContext context, MemberExpression entityIdProperty, string selection )
        {
            var totalAmountQuery = new FinancialTransactionService( context ).Queryable()
                .Select( p => p.TransactionDetails.Sum( a => a.Amount ) );

            var selectTotalAmountExpression = SelectExpressionExtractor.Extract( totalAmountQuery, entityIdProperty, "p" );

            return selectTotalAmountExpression;
        }
        public void RaisePostBackEvent( string eventArgument )
        {
            if ( _batch != null )
            {
                if ( eventArgument == "MoveTransactions" &&
                    _ddlMove != null &&
                    _ddlMove.SelectedValue != null &&
                    !String.IsNullOrWhiteSpace( _ddlMove.SelectedValue ) )
                {
                    var txnsSelected = new List<int>();

                    gTransactions.SelectedKeys.ToList().ForEach( b => txnsSelected.Add( b.ToString().AsInteger() ) );

                    if ( txnsSelected.Any() )
                    {
                        var rockContext = new RockContext();
                        var batchService = new FinancialBatchService( rockContext );

                        var newBatch = batchService.Get( _ddlMove.SelectedValue.AsInteger() );
                        var oldBatch = batchService.Get( _batch.Id );

                        if ( newBatch != null && newBatch.Status == BatchStatus.Open )
                        {
                            var txnService = new FinancialTransactionService( rockContext );
                            var txnsToUpdate = txnService.Queryable()
                                .Where( t => txnsSelected.Contains( t.Id ) )
                                .ToList();

                            foreach ( var txn in txnsToUpdate )
                            {
                                txn.BatchId = newBatch.Id;
                                oldBatch.ControlAmount -= txn.TotalAmount;
                                newBatch.ControlAmount += txn.TotalAmount;
                            }

                            rockContext.SaveChanges();

                            var pageRef = new Rock.Web.PageReference( RockPage.PageId );
                            pageRef.Parameters = new Dictionary<string, string>();
                            pageRef.Parameters.Add( "batchid", newBatch.Id.ToString() );
                            string newBatchLink = string.Format( "<a href='{0}'>{1}</a>",
                                pageRef.BuildUrl(), newBatch.Name );

                            RockPage.UpdateBlocks( "~/Blocks/Finance/BatchDetail.ascx" );

                            nbResult.Text = string.Format( "{0} transactions were moved to the '{1}' batch.",
                                txnsToUpdate.Count().ToString( "N0" ), newBatchLink );
                            nbResult.NotificationBoxType = NotificationBoxType.Success;
                            nbResult.Visible = true;
                        }
                        else
                        {
                            nbResult.Text = string.Format( "The selected batch does not exist, or is no longer open." );
                            nbResult.NotificationBoxType = NotificationBoxType.Danger;
                            nbResult.Visible = true;
                        }
                    }
                    else
                    {
                        nbResult.Text = string.Format( "There were not any transactions selected." );
                        nbResult.NotificationBoxType = NotificationBoxType.Warning;
                        nbResult.Visible = true;
                    }
                }

                _ddlMove.SelectedIndex = 0;
            }

            BindGrid();
        }
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid()
        {
            RockContext rockContext = new RockContext();
            FinancialTransactionService transService = new FinancialTransactionService( rockContext );
            var qry = transService.Queryable( "TransactionDetails.Account,FinancialPaymentDetail" );

            string currentPersonGivingId = CurrentPerson.GivingId;

            qry = qry.Where( t => t.AuthorizedPersonAlias != null &&
                            t.AuthorizedPersonAlias.Person != null &&
                            t.AuthorizedPersonAlias.Person.GivingId == currentPersonGivingId );

            // if the Account Checkboxlist is visible, filter to what was selected.  Otherwise, show all the accounts that the person contributed to
            if ( cblAccounts.Visible )
            {
                // get list of selected accounts
                List<int> selectedAccountIds = cblAccounts.Items.Cast<ListItem>()
                                                .Where( i => i.Selected == true )
                                                .Select( i => int.Parse( i.Value ) ).ToList();
                qry = qry.Where( t => t.TransactionDetails.Any( d => selectedAccountIds.Contains( d.AccountId ) ) );
            }

            if ( drpFilterDates.LowerValue.HasValue )
            {
                qry = qry.Where( t => t.TransactionDateTime.Value >= drpFilterDates.LowerValue.Value );
            }

            if ( drpFilterDates.UpperValue.HasValue )
            {
                var lastDate = drpFilterDates.UpperValue.Value.AddDays( 1 ); // add one day to ensure we get all transactions till midnight
                qry = qry.Where( t => t.TransactionDateTime.Value < lastDate );
            }

            // Transaction Types
            var transactionTypeValueIdList = GetAttributeValue( "TransactionTypes" ).SplitDelimitedValues().AsGuidList().Select( a => DefinedValueCache.Read( a ) ).Where( a => a != null ).Select( a => a.Id ).ToList();

            if ( transactionTypeValueIdList.Any() )
            {
                qry = qry.Where( t => transactionTypeValueIdList.Contains( t.TransactionTypeValueId ) );
            }

            qry = qry.OrderByDescending( a => a.TransactionDateTime );

            var txns = qry.ToList();

            // get account totals
            Dictionary<string, decimal> accountTotals = new Dictionary<string, decimal>();

            foreach ( var transaction in txns )
            {
                foreach ( var transactionDetail in transaction.TransactionDetails )
                {
                    if ( accountTotals.Keys.Contains( transactionDetail.Account.Name ) )
                    {
                        accountTotals[transactionDetail.Account.Name] += transactionDetail.Amount;
                    }
                    else
                    {
                        accountTotals.Add( transactionDetail.Account.Name, transactionDetail.Amount );
                    }
                }
            }

            lAccountSummary.Text = string.Empty;
            if ( accountTotals.Count > 0 )
            {
                pnlSummary.Visible = true;
                foreach ( var key in accountTotals.Keys )
                {
                    lAccountSummary.Text += string.Format( "<li>{0}: {2}{1}</li>", key, accountTotals[key], GlobalAttributesCache.Value( "CurrencySymbol" ) );
                }
            }
            else
            {
                pnlSummary.Visible = false;
            }

            gTransactions.EntityTypeId = EntityTypeCache.Read<FinancialTransaction>().Id;
            gTransactions.DataSource = txns.Select( t => new
            {
                t.Id,
                t.TransactionDateTime,
                CurrencyType = FormatCurrencyType( t ),
                Summary = FormatSummary( t ),
                t.TotalAmount
            } ).ToList();

            gTransactions.DataBind();
        }
        /// <summary>
        /// Processes the payments.
        /// </summary>
        /// <param name="gateway">The gateway.</param>
        /// <param name="batchNamePrefix">The batch name prefix.</param>
        /// <param name="payments">The payments.</param>
        /// <param name="batchUrlFormat">The batch URL format.</param>
        /// <param name="receiptEmail">The receipt email.</param>
        /// <returns></returns>
        public static string ProcessPayments( FinancialGateway gateway, string batchNamePrefix, List<Payment> payments, string batchUrlFormat = "", Guid? receiptEmail = null )
        {
            int totalPayments = 0;
            int totalAlreadyDownloaded = 0;
            int totalNoScheduledTransaction = 0;
            int totalAdded = 0;
            int totalReversals = 0;
            int totalStatusChanges = 0;

            var batches = new List<FinancialBatch>();
            var batchSummary = new Dictionary<Guid, List<Decimal>>();
            var initialControlAmounts = new Dictionary<Guid, decimal>();

            var txnPersonNames = new Dictionary<Guid, string>();

            var gatewayComponent = gateway.GetGatewayComponent();

            var newTransactions = new List<FinancialTransaction>();

            using ( var rockContext = new RockContext() )
            {
                var accountService = new FinancialAccountService( rockContext );
                var txnService = new FinancialTransactionService( rockContext );
                var batchService = new FinancialBatchService( rockContext );
                var scheduledTxnService = new FinancialScheduledTransactionService( rockContext );

                var contributionTxnType = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid() );

                var defaultAccount = accountService.Queryable()
                    .Where( a =>
                        a.IsActive &&
                        !a.ParentAccountId.HasValue &&
                        ( !a.StartDate.HasValue || a.StartDate.Value <= RockDateTime.Now ) &&
                        ( !a.EndDate.HasValue || a.EndDate.Value >= RockDateTime.Now )
                        )
                    .OrderBy( a => a.Order )
                    .FirstOrDefault();

                var batchTxnChanges = new Dictionary<Guid, List<string>>();
                var batchBatchChanges = new Dictionary<Guid, List<string>>();
                var scheduledTransactionIds = new List<int>();

                foreach ( var payment in payments.Where( p => p.Amount > 0.0M ) )
                {
                    totalPayments++;

                    var scheduledTransaction = scheduledTxnService.GetByScheduleId( payment.GatewayScheduleId );
                    if ( scheduledTransaction != null )
                    {
                        // Find existing payments with same transaction code
                        var txns = txnService
                            .Queryable( "TransactionDetails" )
                            .Where( t => t.TransactionCode == payment.TransactionCode )
                            .ToList();

                        // Calculate whether a transaction needs to be added
                        var txnAmount = CalculateTransactionAmount( payment, txns );
                        if ( txnAmount != 0.0M )
                        {
                            scheduledTransactionIds.Add( scheduledTransaction.Id );
                            if ( payment.ScheduleActive.HasValue )
                            {
                                scheduledTransaction.IsActive = payment.ScheduleActive.Value;
                            }

                            var transaction = new FinancialTransaction();
                            transaction.FinancialPaymentDetail = new FinancialPaymentDetail();

                            transaction.Guid = Guid.NewGuid();
                            transaction.TransactionCode = payment.TransactionCode;
                            transaction.TransactionDateTime = payment.TransactionDateTime;
                            transaction.Status = payment.Status;
                            transaction.StatusMessage = payment.StatusMessage;
                            transaction.ScheduledTransactionId = scheduledTransaction.Id;
                            transaction.AuthorizedPersonAliasId = scheduledTransaction.AuthorizedPersonAliasId;
                            transaction.SourceTypeValueId = scheduledTransaction.SourceTypeValueId;
                            txnPersonNames.Add( transaction.Guid, scheduledTransaction.AuthorizedPersonAlias.Person.FullName );
                            transaction.FinancialGatewayId = gateway.Id;
                            transaction.TransactionTypeValueId = contributionTxnType.Id;

                            if ( txnAmount < 0.0M )
                            {
                                transaction.Summary = "Reversal for previous transaction that failed during processing." + Environment.NewLine;
                            }

                            var currencyTypeValue = payment.CurrencyTypeValue;
                            var creditCardTypevalue = payment.CreditCardTypeValue;

                            if ( scheduledTransaction.FinancialPaymentDetail != null )
                            {
                                if ( currencyTypeValue == null && scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.HasValue )
                                {
                                    currencyTypeValue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.Value );
                                }

                                if ( creditCardTypevalue == null && scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.HasValue )
                                {
                                    creditCardTypevalue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.Value );
                                }

                                transaction.FinancialPaymentDetail.AccountNumberMasked = scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked;
                                transaction.FinancialPaymentDetail.NameOnCardEncrypted = scheduledTransaction.FinancialPaymentDetail.NameOnCardEncrypted;
                                transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationMonthEncrypted;
                                transaction.FinancialPaymentDetail.ExpirationYearEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationYearEncrypted;
                                transaction.FinancialPaymentDetail.BillingLocationId = scheduledTransaction.FinancialPaymentDetail.BillingLocationId;
                            }

                            if ( currencyTypeValue != null )
                            {
                                transaction.FinancialPaymentDetail.CurrencyTypeValueId = currencyTypeValue.Id;
                            }
                            if ( creditCardTypevalue != null )
                            {
                                transaction.FinancialPaymentDetail.CreditCardTypeValueId = creditCardTypevalue.Id;
                            }

                            // Try to allocate the amount of the transaction based on the current scheduled transaction accounts
                            decimal remainingAmount = Math.Abs( txnAmount );
                            foreach ( var detail in scheduledTransaction.ScheduledTransactionDetails.Where( d => d.Amount != 0.0M ) )
                            {
                                var transactionDetail = new FinancialTransactionDetail();
                                transactionDetail.AccountId = detail.AccountId;

                                if ( detail.Amount <= remainingAmount )
                                {
                                    // If the configured amount for this account is less than or equal to the remaining
                                    // amount, allocate the configured amount
                                    transactionDetail.Amount = detail.Amount;
                                    remainingAmount -= detail.Amount;
                                }
                                else
                                {
                                    // If the configured amount is greater than the remaining amount, only allocate
                                    // the remaining amount
                                    transaction.Summary += "Note: Downloaded transaction amount was less than the configured allocation amounts for the Scheduled Transaction.";
                                    transactionDetail.Amount = remainingAmount;
                                    transactionDetail.Summary = "Note: The downloaded amount was not enough to apply the configured amount to this account.";
                                    remainingAmount = 0.0M;
                                }

                                transaction.TransactionDetails.Add( transactionDetail );

                                if ( remainingAmount <= 0.0M )
                                {
                                    // If there's no amount left, break out of details
                                    break;
                                }
                            }

                            // If there's still amount left after allocating based on current config, add the remainder
                            // to the account that was configured for the most amount
                            if ( remainingAmount > 0.0M )
                            {
                                transaction.Summary += "Note: Downloaded transaction amount was greater than the configured allocation amounts for the Scheduled Transaction.";
                                var transactionDetail = transaction.TransactionDetails
                                    .OrderByDescending( d => d.Amount )
                                    .FirstOrDefault();
                                if ( transactionDetail == null && defaultAccount != null )
                                {
                                    transactionDetail = new FinancialTransactionDetail();
                                    transactionDetail.AccountId = defaultAccount.Id;
                                    transaction.TransactionDetails.Add( transactionDetail );
                                }
                                if ( transactionDetail != null )
                                {
                                    transactionDetail.Amount += remainingAmount;
                                    transactionDetail.Summary = "Note: Extra amount was applied to this account.";
                                }
                            }

                            // If the amount to apply was negative, update all details to be negative (absolute value was used when allocating to accounts)
                            if ( txnAmount < 0.0M )
                            {
                                foreach ( var txnDetail in transaction.TransactionDetails )
                                {
                                    txnDetail.Amount = 0 - txnDetail.Amount;
                                }
                            }

                            // Get the batch
                            var batch = batchService.Get(
                                batchNamePrefix,
                                currencyTypeValue,
                                creditCardTypevalue,
                                transaction.TransactionDateTime.Value,
                                gateway.GetBatchTimeOffset(),
                                batches );

                            var batchChanges = new List<string>();
                            if ( batch.Id != 0 )
                            {
                                initialControlAmounts.AddOrIgnore( batch.Guid, batch.ControlAmount );
                            }
                            batch.ControlAmount += transaction.TotalAmount;

                            batch.Transactions.Add( transaction );

                            if ( txnAmount > 0.0M && receiptEmail.HasValue )
                            {
                                newTransactions.Add( transaction );
                            }

                            // Add summary
                            if ( !batchSummary.ContainsKey( batch.Guid ) )
                            {
                                batchSummary.Add( batch.Guid, new List<Decimal>() );
                            }
                            batchSummary[batch.Guid].Add( txnAmount );

                            if ( txnAmount > 0.0M )
                            {
                                totalAdded++;
                            }
                            else
                            {
                                totalReversals++;
                            }
                        }
                        else
                        {
                            totalAlreadyDownloaded++;

                            foreach ( var txn in txns.Where( t => t.Status != payment.Status || t.StatusMessage != payment.StatusMessage ) )
                            {
                                txn.Status = payment.Status;
                                txn.StatusMessage = payment.StatusMessage;
                                totalStatusChanges++;
                            }
                        }
                    }
                    else
                    {
                        totalNoScheduledTransaction++;
                    }
                }

                rockContext.SaveChanges();

                // Queue a transaction to update the status of all affected scheduled transactions
                var updatePaymentStatusTxn = new Rock.Transactions.UpdatePaymentStatusTransaction( gateway.Id, scheduledTransactionIds );
                Rock.Transactions.RockQueue.TransactionQueue.Enqueue( updatePaymentStatusTxn );

                if ( receiptEmail.HasValue )
                {
                    // Queue a transaction to send receipts
                    var newTransactionIds = newTransactions.Select( t => t.Id ).ToList();
                    var sendPaymentReceiptsTxn = new Rock.Transactions.SendPaymentReceipts( receiptEmail.Value, newTransactionIds );
                    Rock.Transactions.RockQueue.TransactionQueue.Enqueue( sendPaymentReceiptsTxn );
                }
            }

            StringBuilder sb = new StringBuilder();
            sb.AppendFormat( "<li>{0} {1} downloaded.</li>", totalPayments.ToString( "N0" ),
                ( totalPayments == 1 ? "payment" : "payments" ) );

            if ( totalAlreadyDownloaded > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} previously downloaded and {2} already been added.</li>", totalAlreadyDownloaded.ToString( "N0" ),
                    ( totalAlreadyDownloaded == 1 ? "payment was" : "payments were" ),
                    ( totalAlreadyDownloaded == 1 ? "has" : "have" ) );
            }

            if ( totalStatusChanges > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} previously downloaded but had a change of status.</li>", totalStatusChanges.ToString( "N0" ),
                ( totalStatusChanges == 1 ? "payment was" : "payments were" ) );
            }

            if ( totalNoScheduledTransaction > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} could not be matched to an existing scheduled payment profile.</li>", totalNoScheduledTransaction.ToString( "N0" ),
                    ( totalNoScheduledTransaction == 1 ? "payment" : "payments" ) );
            }

            sb.AppendFormat( "<li>{0} {1} successfully added.</li>", totalAdded.ToString( "N0" ),
                ( totalAdded == 1 ? "payment was" : "payments were" ) );

            if ( totalReversals > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} added as a reversal to a previous transaction.</li>", totalReversals.ToString( "N0" ),
                    ( totalReversals == 1 ? "payment was" : "payments were" ) );
            }

            if ( totalStatusChanges > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} previously downloaded but had a change of status.</li>", totalStatusChanges.ToString( "N0" ),
                    ( totalStatusChanges == 1 ? "payment was" : "payments were" ) );
            }

            foreach ( var batchItem in batchSummary )
            {
                int items = batchItem.Value.Count;
                if ( items > 0 )
                {
                    var batch = batches
                        .Where( b => b.Guid.Equals( batchItem.Key ) )
                        .FirstOrDefault();

                    string batchName = string.Format( "'{0} ({1})'", batch.Name, batch.BatchStartDateTime.Value.ToString( "d" ) );
                    if ( !string.IsNullOrWhiteSpace( batchUrlFormat ) )
                    {
                        batchName = string.Format( "<a href='{0}'>{1}</a>", string.Format( batchUrlFormat, batch.Id ), batchName );
                    }

                    decimal sum = batchItem.Value.Sum();

                    string summaryformat = items == 1 ?
                        "<li>{0} transaction of {1} was added to the {2} batch.</li>" :
                        "<li>{0} transactions totaling {1} were added to the {2} batch</li>";

                    sb.AppendFormat( summaryformat, items.ToString( "N0" ), sum.FormatAsCurrency(), batchName );
                }
            }

            return sb.ToString();
        }
Exemple #24
0
        /// <summary>
        /// Processes the payments.
        /// </summary>
        /// <param name="gateway">The gateway.</param>
        /// <param name="batchNamePrefix">The batch name prefix.</param>
        /// <param name="payments">The payments.</param>
        /// <param name="batchUrlFormat">The batch URL format.</param>
        /// <returns></returns>
        public static string ProcessPayments(GatewayComponent gateway, string batchNamePrefix, List <Payment> payments, string batchUrlFormat = "")
        {
            int totalPayments               = 0;
            int totalAlreadyDownloaded      = 0;
            int totalNoScheduledTransaction = 0;
            int totalAdded = 0;

            var batches      = new List <FinancialBatch>();
            var batchSummary = new Dictionary <Guid, List <Payment> >();

            var rockContext         = new RockContext();
            var accountService      = new FinancialAccountService(rockContext);
            var txnService          = new FinancialTransactionService(rockContext);
            var batchService        = new FinancialBatchService(rockContext);
            var scheduledTxnService = new FinancialScheduledTransactionService(rockContext);

            var contributionTxnTypeId = DefinedValueCache.Read(Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid()).Id;

            var defaultAccount = accountService.Queryable()
                                 .Where(a =>
                                        a.IsActive &&
                                        !a.ParentAccountId.HasValue &&
                                        (!a.StartDate.HasValue || a.StartDate.Value <= RockDateTime.Now) &&
                                        (!a.EndDate.HasValue || a.EndDate.Value >= RockDateTime.Now)
                                        )
                                 .OrderBy(a => a.Order)
                                 .FirstOrDefault();

            foreach (var payment in payments.Where(p => p.Amount > 0.0M))
            {
                totalPayments++;

                // Only consider transactions that have not already been added
                if (txnService.GetByTransactionCode(payment.TransactionCode) == null)
                {
                    var scheduledTransaction = scheduledTxnService.GetByScheduleId(payment.GatewayScheduleId);
                    if (scheduledTransaction != null)
                    {
                        scheduledTransaction.IsActive = payment.ScheduleActive;

                        var transaction = new FinancialTransaction();
                        transaction.TransactionCode         = payment.TransactionCode;
                        transaction.TransactionDateTime     = payment.TransactionDateTime;
                        transaction.ScheduledTransactionId  = scheduledTransaction.Id;
                        transaction.AuthorizedPersonAliasId = scheduledTransaction.AuthorizedPersonAliasId;
                        transaction.GatewayEntityTypeId     = gateway.TypeId;
                        transaction.TransactionTypeValueId  = contributionTxnTypeId;

                        var currencyTypeValue = payment.CurrencyTypeValue;
                        if (currencyTypeValue == null && scheduledTransaction.CurrencyTypeValueId.HasValue)
                        {
                            currencyTypeValue = DefinedValueCache.Read(scheduledTransaction.CurrencyTypeValueId.Value);
                        }
                        if (currencyTypeValue != null)
                        {
                            transaction.CurrencyTypeValueId = currencyTypeValue.Id;
                        }

                        var creditCardTypevalue = payment.CreditCardTypeValue;
                        if (creditCardTypevalue == null && scheduledTransaction.CreditCardTypeValueId.HasValue)
                        {
                            creditCardTypevalue = DefinedValueCache.Read(scheduledTransaction.CreditCardTypeValueId.Value);
                        }
                        if (creditCardTypevalue != null)
                        {
                            transaction.CreditCardTypeValueId = creditCardTypevalue.Id;
                        }

                        //transaction.SourceTypeValueId = DefinedValueCache.Read( sourceGuid ).Id;

                        // Try to allocate the amount of the transaction based on the current scheduled transaction accounts
                        decimal remainingAmount = payment.Amount;
                        foreach (var detail in scheduledTransaction.ScheduledTransactionDetails.Where(d => d.Amount != 0.0M))
                        {
                            var transactionDetail = new FinancialTransactionDetail();
                            transactionDetail.AccountId = detail.AccountId;

                            if (detail.Amount <= remainingAmount)
                            {
                                // If the configured amount for this account is less than or equal to the remaining
                                // amount, allocate the configured amount
                                transactionDetail.Amount = detail.Amount;
                                remainingAmount         -= detail.Amount;
                            }
                            else
                            {
                                // If the configured amount is greater than the remaining amount, only allocate
                                // the remaining amount
                                transaction.Summary = "Note: Downloaded transaction amount was less than the configured allocation amounts for the Scheduled Transaction.";
                                detail.Amount       = remainingAmount;
                                detail.Summary      = "Note: The downloaded amount was not enough to apply the configured amount to this account.";
                                remainingAmount     = 0.0M;
                            }

                            transaction.TransactionDetails.Add(transactionDetail);

                            if (remainingAmount <= 0.0M)
                            {
                                // If there's no amount left, break out of details
                                break;
                            }
                        }

                        // If there's still amount left after allocating based on current config, add the remainder
                        // to the account that was configured for the most amount
                        if (remainingAmount > 0.0M)
                        {
                            transaction.Summary = "Note: Downloaded transaction amount was greater than the configured allocation amounts for the Scheduled Transaction.";
                            var transactionDetail = transaction.TransactionDetails
                                                    .OrderByDescending(d => d.Amount)
                                                    .First();
                            if (transactionDetail == null && defaultAccount != null)
                            {
                                transactionDetail           = new FinancialTransactionDetail();
                                transactionDetail.AccountId = defaultAccount.Id;
                            }
                            if (transactionDetail != null)
                            {
                                transactionDetail.Amount += remainingAmount;
                                transactionDetail.Summary = "Note: Extra amount was applied to this account.";
                            }
                        }

                        // Get the batch
                        var batch = batchService.Get(
                            batchNamePrefix,
                            currencyTypeValue,
                            creditCardTypevalue,
                            transaction.TransactionDateTime.Value,
                            gateway.BatchTimeOffset,
                            batches);

                        batch.ControlAmount += transaction.TotalAmount;
                        batch.Transactions.Add(transaction);

                        // Add summary
                        if (!batchSummary.ContainsKey(batch.Guid))
                        {
                            batchSummary.Add(batch.Guid, new List <Payment>());
                        }
                        batchSummary[batch.Guid].Add(payment);

                        totalAdded++;
                    }
                    else
                    {
                        totalNoScheduledTransaction++;
                    }
                }
                else
                {
                    totalAlreadyDownloaded++;
                }
            }

            rockContext.SaveChanges();

            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("<li>{0} {1} downloaded.</li>", totalPayments.ToString("N0"),
                            (totalPayments == 1 ? "payment" : "payments"));

            if (totalAlreadyDownloaded > 0)
            {
                sb.AppendFormat("<li>{0} {1} previously downloaded and {2} already been added.</li>", totalAlreadyDownloaded.ToString("N0"),
                                (totalAlreadyDownloaded == 1 ? "payment was" : "payments were"),
                                (totalAlreadyDownloaded == 1 ? "has" : "have"));
            }

            if (totalNoScheduledTransaction > 0)
            {
                sb.AppendFormat("<li>{0} {1} could not be matched to an existing scheduled payment profile.</li>", totalNoScheduledTransaction.ToString("N0"),
                                (totalNoScheduledTransaction == 1 ? "payment" : "payments"));
            }

            sb.AppendFormat("<li>{0} {1} successfully added.</li>", totalAdded.ToString("N0"),
                            (totalAdded == 1 ? "payment was" : "payments were"));

            foreach (var batchItem in batchSummary)
            {
                int items = batchItem.Value.Count;
                if (items > 0)
                {
                    var batch = batches
                                .Where(b => b.Guid.Equals(batchItem.Key))
                                .FirstOrDefault();

                    string batchName = string.Format("'{0} ({1})'", batch.Name, batch.BatchStartDateTime.Value.ToString("d"));
                    if (!string.IsNullOrWhiteSpace(batchUrlFormat))
                    {
                        batchName = string.Format("<a href='{0}'>{1}</a>", string.Format(batchUrlFormat, batch.Id), batchName);
                    }

                    decimal sum = batchItem.Value.Select(p => p.Amount).Sum();


                    string summaryformat = items == 1 ?
                                           "<li>{0} transaction of {1} was added to the {2} batch.</li>" :
                                           "<li>{0} transactions totaling {1} were added to the {2} batch</li>";

                    sb.AppendFormat(summaryformat, items.ToString("N0"), sum.ToString("C2"), batchName);
                }
            }

            return(sb.ToString());
        }
Exemple #25
0
        /// <summary>
        /// Deletes the family's addresses, phone numbers, photos, viewed records, and people.
        /// TODO: delete attendance codes for attendance data that's about to be deleted when
        /// we delete the person record.
        /// </summary>
        /// <param name="families">The families.</param>
        /// <param name="rockContext">The rock context.</param>
        private void DeleteExistingFamilyData( XElement families, RockContext rockContext )
        {
            PersonService personService = new PersonService( rockContext );
            PhoneNumberService phoneNumberService = new PhoneNumberService( rockContext );
            PersonViewedService personViewedService = new PersonViewedService( rockContext );
            PageViewService pageViewService = new PageViewService( rockContext );
            BinaryFileService binaryFileService = new BinaryFileService( rockContext );
            PersonAliasService personAliasService = new PersonAliasService( rockContext );
            PersonDuplicateService personDuplicateService = new PersonDuplicateService( rockContext );
            NoteService noteService = new NoteService( rockContext );
            AuthService authService = new AuthService( rockContext );
            CommunicationService communicationService = new CommunicationService( rockContext );
            CommunicationRecipientService communicationRecipientService = new CommunicationRecipientService( rockContext );
            FinancialBatchService financialBatchService = new FinancialBatchService( rockContext );
            FinancialTransactionService financialTransactionService = new FinancialTransactionService( rockContext );
            PersonPreviousNameService personPreviousNameService = new PersonPreviousNameService( rockContext );
            ConnectionRequestService connectionRequestService = new ConnectionRequestService( rockContext );
            ConnectionRequestActivityService connectionRequestActivityService = new ConnectionRequestActivityService( rockContext );

            // delete the batch data
            List<int> imageIds = new List<int>();
            foreach ( var batch in financialBatchService.Queryable().Where( b => b.Name.StartsWith( "SampleData" ) ) )
            {
                imageIds.AddRange( batch.Transactions.SelectMany( t => t.Images ).Select( i => i.BinaryFileId ).ToList() );
                financialTransactionService.DeleteRange( batch.Transactions );
                financialBatchService.Delete( batch );
            }

            // delete all transaction images
            foreach ( var image in binaryFileService.GetByIds( imageIds ) )
            {
                binaryFileService.Delete( image );
            }

            foreach ( var elemFamily in families.Elements( "family" ) )
            {
                Guid guid = elemFamily.Attribute( "guid" ).Value.Trim().AsGuid();

                GroupService groupService = new GroupService( rockContext );
                Group family = groupService.Get( guid );
                if ( family != null )
                {
                    var groupMemberService = new GroupMemberService( rockContext );
                    var members = groupMemberService.GetByGroupId( family.Id, true );

                    // delete the people records
                    string errorMessage;
                    List<int> photoIds = members.Select( m => m.Person ).Where( p => p.PhotoId != null ).Select( a => (int)a.PhotoId ).ToList();

                    foreach ( var person in members.Select( m => m.Person ) )
                    {
                        person.GivingGroup = null;
                        person.GivingGroupId = null;
                        person.PhotoId = null;

                        // delete phone numbers
                        foreach ( var phone in phoneNumberService.GetByPersonId( person.Id ) )
                        {
                            if ( phone != null )
                            {
                                phoneNumberService.Delete( phone );
                            }
                        }

                        // delete communication recipient
                        foreach ( var recipient in communicationRecipientService.Queryable().Where( r => r.PersonAlias.PersonId == person.Id ) )
                        {
                            communicationRecipientService.Delete( recipient );
                        }

                        // delete communication
                        foreach ( var communication in communicationService.Queryable().Where( c => c.SenderPersonAliasId == person.PrimaryAlias.Id ) )
                        {
                            communicationService.Delete( communication );
                        }

                        // delete person viewed records
                        foreach ( var view in personViewedService.GetByTargetPersonId( person.Id ) )
                        {
                            personViewedService.Delete( view );
                        }

                        // delete page viewed records
                        foreach ( var view in pageViewService.GetByPersonId( person.Id ) )
                        {
                            pageViewService.Delete( view );
                        }

                        // delete notes created by them or on their record.
                        foreach ( var note in noteService.Queryable().Where ( n => n.CreatedByPersonAlias.PersonId == person.Id
                            || (n.NoteType.EntityTypeId == _personEntityTypeId && n.EntityId == person.Id ) ) )
                        {
                            noteService.Delete( note );
                        }

                        // delete previous names on their records
                        foreach ( var previousName in personPreviousNameService.Queryable().Where( r => r.PersonAlias.PersonId == person.Id ) )
                        {
                            personPreviousNameService.Delete( previousName );
                        }

                        // delete any GroupMember records they have
                        foreach ( var groupMember in groupMemberService.Queryable().Where( gm => gm.PersonId == person.Id ) )
                        {
                            groupMemberService.Delete( groupMember );
                        }

                        //// delete any Authorization data
                        //foreach ( var auth in authService.Queryable().Where( a => a.PersonId == person.Id ) )
                        //{
                        //    authService.Delete( auth );
                        //}

                        // delete their aliases
                        foreach ( var alias in personAliasService.Queryable().Where( a => a.PersonId == person.Id ) )
                        {
                            foreach ( var duplicate in personDuplicateService.Queryable().Where( d => d.DuplicatePersonAliasId == alias.Id ) )
                            {
                                personDuplicateService.Delete( duplicate );
                            }

                            personAliasService.Delete( alias );
                        }

                        // delete any connection requests tied to them
                        foreach ( var request in connectionRequestService.Queryable().Where( r => r.PersonAlias.PersonId == person.Id || r.ConnectorPersonAlias.PersonId == person.Id ) )
                        {
                            connectionRequestActivityService.DeleteRange( request.ConnectionRequestActivities );
                            connectionRequestService.Delete( request );
                        }

                        // Save these changes so the CanDelete passes the check...
                        //rockContext.ChangeTracker.DetectChanges();
                        rockContext.SaveChanges( disablePrePostProcessing: true );

                        if ( personService.CanDelete( person, out errorMessage ) )
                        {
                            personService.Delete( person );
                            //rockContext.ChangeTracker.DetectChanges();
                            //rockContext.SaveChanges( disablePrePostProcessing: true );
                        }
                        else
                        {
                            throw new Exception( string.Format( "Trying to delete {0}, but: {1}", person.FullName, errorMessage ) );
                        }
                    }

                    //rockContext.ChangeTracker.DetectChanges();
                    rockContext.SaveChanges( disablePrePostProcessing: true );

                    // delete all member photos
                    foreach ( var photo in binaryFileService.GetByIds( photoIds ) )
                    {
                        binaryFileService.Delete( photo );
                    }

                    DeleteGroupAndMemberData( family, rockContext );
                }
            }
        }
        /// <summary>
        /// Handles the Click event of the btnNext 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 btnNext_Click( object sender, EventArgs e )
        {
            var rockContext = new RockContext();
            var financialTransactionService = new FinancialTransactionService( rockContext );
            var financialTransactionDetailService = new FinancialTransactionDetailService( rockContext );
            var financialPersonBankAccountService = new FinancialPersonBankAccountService( rockContext );
            int txnId = hfTransactionId.Value.AsInteger();
            var financialTransaction = financialTransactionService
                    .Queryable( "AuthorizedPersonAlias.Person,ProcessedByPersonAlias.Person" )
                    .FirstOrDefault( t => t.Id == txnId );

            // set the AuthorizedPersonId (the person who wrote the check, for example) to the if the SelectNew person (if selected) or person selected in the drop down (if there is somebody selected)
            int? authorizedPersonId = ppSelectNew.PersonId ?? ddlIndividual.SelectedValue.AsIntegerOrNull();

            var accountNumberSecured = hfCheckMicrHashed.Value;

            if ( cbTotalAmount.Text.AsDecimalOrNull().HasValue && !authorizedPersonId.HasValue )
            {
                nbSaveError.Text = "Transaction must be matched to a person when the amount is specified.";
                nbSaveError.Visible = true;
                return;
            }

            // if the transaction was previously matched, but user unmatched it, save it as an unmatched transaction and clear out the detail records (we don't want an unmatched transaction to have detail records)
            if ( financialTransaction != null &&
                financialTransaction.AuthorizedPersonAliasId.HasValue &&
                !authorizedPersonId.HasValue )
            {
                financialTransaction.AuthorizedPersonAliasId = null;
                foreach ( var detail in financialTransaction.TransactionDetails )
                {
                    financialTransactionDetailService.Delete( detail );
                }

                rockContext.SaveChanges();

                // if the transaction was unmatched, clear out the ProcessedBy fields since we didn't match the transaction and are moving on to process another transaction
                MarkTransactionAsNotProcessedByCurrentUser( hfTransactionId.Value.AsInteger() );
            }

            // if the transaction is matched to somebody, attempt to save it.  Otherwise, if the transaction was previously matched, but user unmatched it, save it as an unmatched transaction
            if ( financialTransaction != null && authorizedPersonId.HasValue )
            {
                bool requiresMicr = financialTransaction.CurrencyTypeValue.Guid == Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CHECK.AsGuid();
                if ( requiresMicr && string.IsNullOrWhiteSpace( accountNumberSecured ) )
                {
                    // should be showing already, but just in case
                    nbNoMicrWarning.Visible = true;
                    return;
                }

                if ( cbTotalAmount.Text.AsDecimalOrNull() == null )
                {
                    nbSaveError.Text = "Total amount must be allocated to accounts.";
                    nbSaveError.Visible = true;
                    return;
                }

                int? personAliasId = new PersonAliasService( rockContext ).GetPrimaryAliasId( authorizedPersonId.Value );

                // if this transaction has an accountnumber associated with it (in other words, it's a scanned check), ensure there is a financialPersonBankAccount record
                if ( !string.IsNullOrWhiteSpace( accountNumberSecured ) )
                {
                    var financialPersonBankAccount = financialPersonBankAccountService.Queryable().Where( a => a.AccountNumberSecured == accountNumberSecured && a.PersonAlias.PersonId == authorizedPersonId.Value ).FirstOrDefault();
                    if ( financialPersonBankAccount == null )
                    {
                        if ( personAliasId.HasValue )
                        {
                            financialPersonBankAccount = new FinancialPersonBankAccount();
                            financialPersonBankAccount.PersonAliasId = personAliasId.Value;
                            financialPersonBankAccount.AccountNumberSecured = accountNumberSecured;

                            var checkMicrClearText = Encryption.DecryptString( financialTransaction.CheckMicrEncrypted );
                            var parts = checkMicrClearText.Split( '_' );
                            if ( parts.Length >= 2 )
                            {
                                financialPersonBankAccount.AccountNumberMasked = parts[1].Masked();
                            }

                            financialPersonBankAccountService.Add( financialPersonBankAccount );
                        }
                    }
                }

                if ( personAliasId.HasValue )
                {
                    financialTransaction.AuthorizedPersonAliasId = personAliasId;
                }

                // just in case this transaction is getting re-edited either by the same user, or somebody else, clean out any existing TransactionDetail records
                foreach ( var detail in financialTransaction.TransactionDetails.ToList() )
                {
                    financialTransactionDetailService.Delete( detail );
                }

                foreach ( var accountBox in rptAccounts.ControlsOfTypeRecursive<CurrencyBox>() )
                {
                    var amount = accountBox.Text.AsDecimalOrNull();

                    if ( amount.HasValue && amount.Value >= 0 )
                    {
                        var financialTransactionDetail = new FinancialTransactionDetail();
                        financialTransactionDetail.TransactionId = financialTransaction.Id;
                        financialTransactionDetail.AccountId = accountBox.Attributes["data-account-id"].AsInteger();
                        financialTransactionDetail.Amount = amount.Value;
                        financialTransactionDetailService.Add( financialTransactionDetail );
                    }
                }

                financialTransaction.ProcessedByPersonAliasId = this.CurrentPersonAlias.Id;
                financialTransaction.ProcessedDateTime = RockDateTime.Now;

                rockContext.SaveChanges();
            }
            else
            {
                // if the transaction was not matched, clear out the ProcessedBy fields since we didn't match the transaction and are moving on to process another transaction
                MarkTransactionAsNotProcessedByCurrentUser( hfTransactionId.Value.AsInteger() );
            }

            NavigateToTransaction( Direction.Next );
        }
        /// <summary>
        /// Processes the payments and returns a summary in HTML format
        /// </summary>
        /// <param name="gateway">The gateway.</param>
        /// <param name="batchNamePrefix">The batch name prefix.</param>
        /// <param name="payments">The payments.</param>
        /// <param name="batchUrlFormat">The batch URL format.</param>
        /// <param name="receiptEmail">The receipt email.</param>
        /// <param name="failedPaymentEmail">The failed payment email.</param>
        /// <param name="failedPaymentWorkflowType">Type of the failed payment workflow.</param>
        /// <param name="verboseLogging">If <c>true</c> then additional details will be logged.</param>
        /// <returns></returns>
        public static string ProcessPayments(FinancialGateway gateway, string batchNamePrefix, List <Payment> payments, string batchUrlFormat,
                                             Guid?receiptEmail, Guid?failedPaymentEmail, Guid?failedPaymentWorkflowType, bool verboseLogging)
        {
            int totalPayments          = 0;
            int totalAlreadyDownloaded = 0;

            // If there is a payment without a transaction, but has one of the following status, don't report it as a 'unmatched' transaction.
            // If they have one of these statuses, and can't be matched, the user probably closed the browser or walked away before completing the transaction.
            string[] ignorableUnMatchedStatuses = new string[2] {
                "in_progress", "abandoned"
            };

            List <Payment> paymentsWithoutTransaction = new List <Payment>();
            int            totalAdded         = 0;
            int            totalReversals     = 0;
            int            totalFailures      = 0;
            int            totalStatusChanges = 0;

            var batchSummary = new Dictionary <Guid, List <Decimal> >();

            var newTransactionsForReceiptEmails = new List <FinancialTransaction>();

            var failedPayments = new List <FinancialTransaction>();

            var contributionTxnType = DefinedValueCache.Get(Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid());

            int?defaultAccountId = null;

            using (var rockContext2 = new RockContext())
            {
                defaultAccountId = new FinancialAccountService(rockContext2).Queryable()
                                   .Where(a =>
                                          a.IsActive &&
                                          !a.ParentAccountId.HasValue &&
                                          (!a.StartDate.HasValue || a.StartDate.Value <= RockDateTime.Now) &&
                                          (!a.EndDate.HasValue || a.EndDate.Value >= RockDateTime.Now)
                                          )
                                   .OrderBy(a => a.Order)
                                   .Select(a => a.Id)
                                   .FirstOrDefault();
            }

            var batchTxnChanges         = new Dictionary <Guid, List <string> >();
            var batchBatchChanges       = new Dictionary <Guid, List <string> >();
            var scheduledTransactionIds = new List <int>();
            List <FinancialTransaction> transactionsWithAttributes = new List <FinancialTransaction>();

            foreach (var payment in payments.Where(p => p.Amount > 0.0M))
            {
                using (var rockContext = new RockContext())
                {
                    totalPayments++;

                    var financialTransactionService = new FinancialTransactionService(rockContext);

                    FinancialTransaction        originalTxn = null;
                    List <FinancialTransaction> txns        = null;

                    // Find existing payments with same transaction code as long as it is not blank.
                    if (payment.TransactionCode.IsNotNullOrWhiteSpace())
                    {
                        txns = financialTransactionService
                               .Queryable("TransactionDetails")
                               .Where(t =>
                                      t.FinancialGatewayId.HasValue &&
                                      t.FinancialGatewayId.Value == gateway.Id &&
                                      t.TransactionCode == payment.TransactionCode)
                               .ToList();

                        originalTxn = txns.Any() ? txns.OrderBy(t => t.Id).First() : null;
                    }

                    FinancialScheduledTransaction scheduledTransaction = null;

                    // We don't want to match a blank schedule ID, so if we don't have one then scheduledTransaction will stay NULL
                    if (payment.GatewayScheduleId.IsNotNullOrWhiteSpace())
                    {
                        scheduledTransaction = new FinancialScheduledTransactionService(rockContext).GetByScheduleId(payment.GatewayScheduleId, gateway.Id);
                    }

                    // Calculate whether a transaction needs to be added
                    var txnAmount = CalculateTransactionAmount(payment, txns);
                    if (txnAmount != 0.0M || (payment.IsFailure && originalTxn == null && scheduledTransaction != null))
                    {
                        // Verify that the payment is for an existing scheduled transaction, or has the same transaction code as an existing payment
                        if (scheduledTransaction != null || originalTxn != null)
                        {
                            var transaction = new FinancialTransaction();
                            transaction.Guid                   = Guid.NewGuid();
                            transaction.TransactionCode        = payment.TransactionCode;
                            transaction.TransactionDateTime    = payment.TransactionDateTime;
                            transaction.Status                 = payment.Status;
                            transaction.IsSettled              = payment.IsSettled;
                            transaction.SettledGroupId         = payment.SettledGroupId;
                            transaction.SettledDate            = payment.SettledDate;
                            transaction.StatusMessage          = payment.StatusMessage;
                            transaction.FinancialPaymentDetail = new FinancialPaymentDetail();

                            if (payment.ForeignKey.IsNotNullOrWhiteSpace())
                            {
                                transaction.ForeignKey = payment.ForeignKey;
                            }

                            FinancialPaymentDetail    financialPaymentDetail = null;
                            List <ITransactionDetail> originalTxnDetails     = new List <ITransactionDetail>();

                            if (scheduledTransaction != null)
                            {
                                scheduledTransactionIds.Add(scheduledTransaction.Id);
                                if (payment.ScheduleActive.HasValue)
                                {
                                    scheduledTransaction.IsActive = payment.ScheduleActive.Value;
                                }

                                transaction.ScheduledTransactionId  = scheduledTransaction.Id;
                                transaction.AuthorizedPersonAliasId = scheduledTransaction.AuthorizedPersonAliasId;
                                transaction.SourceTypeValueId       = scheduledTransaction.SourceTypeValueId;
                                financialPaymentDetail = scheduledTransaction.FinancialPaymentDetail;
                                scheduledTransaction.ScheduledTransactionDetails.ToList().ForEach(d => originalTxnDetails.Add(d));
                            }
                            else
                            {
                                transaction.AuthorizedPersonAliasId = originalTxn.AuthorizedPersonAliasId;
                                transaction.SourceTypeValueId       = originalTxn.SourceTypeValueId;
                                financialPaymentDetail = originalTxn.FinancialPaymentDetail;
                                originalTxn.TransactionDetails.ToList().ForEach(d => originalTxnDetails.Add(d));
                            }

                            transaction.FinancialGatewayId     = gateway.Id;
                            transaction.TransactionTypeValueId = contributionTxnType.Id;

                            if (txnAmount < 0.0M)
                            {
                                transaction.Summary = "Reversal created for previous transaction(s) to correct the total transaction amount." + Environment.NewLine;
                            }

                            // Set the attributes of the transaction
                            if (payment.Attributes != null && payment.Attributes.Count > 0)
                            {
                                transaction.LoadAttributes();
                                foreach (var attribute in payment.Attributes)
                                {
                                    transaction.SetAttributeValue(attribute.Key, attribute.Value);
                                }
                                transactionsWithAttributes.Add(transaction);
                            }

                            var currencyTypeValue   = payment.CurrencyTypeValue;
                            var creditCardTypevalue = payment.CreditCardTypeValue;

                            if (financialPaymentDetail != null)
                            {
                                if (currencyTypeValue == null && financialPaymentDetail.CurrencyTypeValueId.HasValue)
                                {
                                    currencyTypeValue = DefinedValueCache.Get(financialPaymentDetail.CurrencyTypeValueId.Value);
                                }

                                if (creditCardTypevalue == null && financialPaymentDetail.CreditCardTypeValueId.HasValue)
                                {
                                    creditCardTypevalue = DefinedValueCache.Get(financialPaymentDetail.CreditCardTypeValueId.Value);
                                }

                                transaction.FinancialPaymentDetail.AccountNumberMasked = financialPaymentDetail.AccountNumberMasked;
                                transaction.FinancialPaymentDetail.NameOnCard          = financialPaymentDetail.NameOnCard;
                                transaction.FinancialPaymentDetail.ExpirationMonth     = financialPaymentDetail.ExpirationMonth;
                                transaction.FinancialPaymentDetail.ExpirationYear      = financialPaymentDetail.ExpirationYear;
                                transaction.FinancialPaymentDetail.BillingLocationId   = financialPaymentDetail.BillingLocationId;
                                if (financialPaymentDetail.GatewayPersonIdentifier.IsNullOrWhiteSpace())
                                {
                                    // if Rock doesn't have the GatewayPersonIdentifier, get it from the downloaded payment (if it has a value)
                                    transaction.FinancialPaymentDetail.GatewayPersonIdentifier = payment.GatewayPersonIdentifier;
                                }
                                else
                                {
                                    transaction.FinancialPaymentDetail.GatewayPersonIdentifier = financialPaymentDetail.GatewayPersonIdentifier;
                                }

                                transaction.FinancialPaymentDetail.FinancialPersonSavedAccountId = financialPaymentDetail.FinancialPersonSavedAccountId;
                            }

                            if (currencyTypeValue != null)
                            {
                                transaction.FinancialPaymentDetail.CurrencyTypeValueId = currencyTypeValue.Id;
                            }
                            if (creditCardTypevalue != null)
                            {
                                transaction.FinancialPaymentDetail.CreditCardTypeValueId = creditCardTypevalue.Id;
                            }

                            // Try to allocate the amount of the transaction based on the current scheduled transaction accounts
                            decimal remainingAmount = Math.Abs(txnAmount);
                            foreach (var detail in originalTxnDetails.Where(d => d.Amount != 0.0M))
                            {
                                if (remainingAmount <= 0.0M)
                                {
                                    // If there's no amount left, break out of details
                                    break;
                                }

                                var transactionDetail = new FinancialTransactionDetail();
                                transactionDetail.AccountId         = detail.AccountId;
                                transactionDetail.EntityTypeId      = detail.EntityTypeId;
                                transactionDetail.EntityId          = detail.EntityId;
                                transactionDetail.FeeCoverageAmount = detail.FeeCoverageAmount;

                                if (detail.Amount <= remainingAmount)
                                {
                                    // If the configured amount for this account is less than or equal to the remaining
                                    // amount, allocate the configured amount
                                    transactionDetail.Amount = detail.Amount;
                                    remainingAmount         -= detail.Amount;
                                }
                                else
                                {
                                    // If the configured amount is greater than the remaining amount, only allocate
                                    // the remaining amount
                                    transaction.Summary      += "Note: Downloaded transaction amount was less than the configured allocation amounts for the Scheduled Transaction.";
                                    transactionDetail.Amount  = remainingAmount;
                                    transactionDetail.Summary = "Note: The downloaded amount was not enough to apply the configured amount to this account.";
                                    remainingAmount           = 0.0M;
                                }

                                transaction.TransactionDetails.Add(transactionDetail);
                            }

                            // If there's still amount left after allocating based on current config, add the remainder
                            // to the account that was configured for the most amount
                            if (remainingAmount > 0.0M)
                            {
                                transaction.Summary += "Note: Downloaded transaction amount was greater than the configured allocation amounts for the Scheduled Transaction.";
                                var transactionDetail = transaction.TransactionDetails
                                                        .OrderByDescending(d => d.Amount)
                                                        .FirstOrDefault();
                                if (transactionDetail == null && defaultAccountId.HasValue)
                                {
                                    transactionDetail           = new FinancialTransactionDetail();
                                    transactionDetail.AccountId = defaultAccountId.Value;
                                    transaction.TransactionDetails.Add(transactionDetail);
                                }
                                if (transactionDetail != null)
                                {
                                    transactionDetail.Amount += remainingAmount;
                                    transactionDetail.Summary = "Note: Extra amount was applied to this account.";
                                }
                            }

                            // If the amount to apply was negative, update all details to be negative (absolute value was used when allocating to accounts)
                            if (txnAmount < 0.0M)
                            {
                                foreach (var txnDetail in transaction.TransactionDetails)
                                {
                                    txnDetail.Amount = 0 - txnDetail.Amount;
                                }
                            }

                            // Get the batch
                            var batchService = new FinancialBatchService(rockContext);
                            var batch        = batchService.Get(
                                batchNamePrefix,
                                string.Empty,
                                currencyTypeValue,
                                creditCardTypevalue,
                                transaction.TransactionDateTime.Value,
                                gateway.GetBatchTimeOffset(),
                                gateway.BatchDayOfWeek);

                            if (batch.Id == 0)
                            {
                                // get a batch Id
                                rockContext.SaveChanges();
                            }

                            transaction.BatchId = batch.Id;
                            financialTransactionService.Add(transaction);
                            batchService.IncrementControlAmount(batch.Id, transaction.TotalAmount, null);

                            if (receiptEmail.HasValue && txnAmount > 0.0M)
                            {
                                newTransactionsForReceiptEmails.Add(transaction);
                            }

                            if (
                                payment.IsFailure &&
                                (
                                    (txnAmount == 0.0M && scheduledTransaction != null && originalTxn == null) ||
                                    (txnAmount < 0.0M && originalTxn != null)
                                ))
                            {
                                failedPayments.Add(transaction);
                            }

                            // Add summary
                            if (!batchSummary.ContainsKey(batch.Guid))
                            {
                                batchSummary.Add(batch.Guid, new List <Decimal>());
                            }
                            batchSummary[batch.Guid].Add(txnAmount);

                            totalAdded++;

                            if (txnAmount < 0.0M)
                            {
                                totalReversals++;
                            }
                            else if (txnAmount == 0.0M)
                            {
                                totalFailures++;
                            }
                        }
                        else
                        {
                            // If the payment can't be matched (and we aren't ignoring it due to its status), add it to the payment without a transactions that we'll report.
                            if (!ignorableUnMatchedStatuses.Contains(payment.Status, System.StringComparer.OrdinalIgnoreCase))
                            {
                                paymentsWithoutTransaction.Add(payment);
                            }
                        }
                    }
                    else
                    {
                        totalAlreadyDownloaded++;
                    }

                    if (txns != null)
                    {
                        foreach (var txn in txns
                                 .Where(t =>
                                        t.Status != payment.Status ||
                                        t.StatusMessage != payment.StatusMessage ||
                                        t.IsSettled != payment.IsSettled ||
                                        t.SettledGroupId != payment.SettledGroupId ||
                                        t.SettledDate != payment.SettledDate))
                        {
                            txn.IsSettled      = payment.IsSettled;
                            txn.SettledGroupId = payment.SettledGroupId;
                            txn.SettledDate    = payment.SettledDate;
                            txn.Status         = payment.Status;
                            txn.StatusMessage  = payment.StatusMessage;
                            totalStatusChanges++;
                        }
                    }

                    rockContext.SaveChanges();
                }
            }
            if (transactionsWithAttributes.Count > 0)
            {
                foreach (var transaction in transactionsWithAttributes)
                {
                    using (var rockContext3 = new RockContext())
                    {
                        transaction.SaveAttributeValues(rockContext3);
                        rockContext3.SaveChanges();
                    }
                }
            }

            // Queue a transaction to update the status of all affected scheduled transactions
            var updatePaymentStatusTxn = new UpdatePaymentStatusTransaction(gateway.Id, scheduledTransactionIds);

            RockQueue.TransactionQueue.Enqueue(updatePaymentStatusTxn);

            if (receiptEmail.HasValue && newTransactionsForReceiptEmails.Any())
            {
                // Queue a transaction to send receipts
                var newTransactionIds      = newTransactionsForReceiptEmails.Select(t => t.Id).ToList();
                var sendPaymentReceiptsTxn = new SendPaymentReceipts(receiptEmail.Value, newTransactionIds);
                RockQueue.TransactionQueue.Enqueue(sendPaymentReceiptsTxn);
            }

            // Queue transactions to launch failed payment workflow
            if (failedPayments.Any())
            {
                if (failedPaymentEmail.HasValue)
                {
                    // Queue a transaction to send payment failure
                    var newTransactionIds     = failedPayments.Select(t => t.Id).ToList();
                    var sendPaymentFailureTxn = new SendPaymentReceipts(failedPaymentEmail.Value, newTransactionIds);
                    RockQueue.TransactionQueue.Enqueue(sendPaymentFailureTxn);
                }

                if (failedPaymentWorkflowType.HasValue)
                {
                    // Queue a transaction to launch workflow
                    var workflowDetails    = failedPayments.Select(p => new LaunchWorkflowDetails(p)).ToList();
                    var launchWorkflowsTxn = new LaunchWorkflowsTransaction(failedPaymentWorkflowType.Value, workflowDetails);
                    RockQueue.TransactionQueue.Enqueue(launchWorkflowsTxn);
                }
            }

            StringBuilder sb = new StringBuilder();

            sb.AppendFormat("<li>{0} {1} downloaded.</li>", totalPayments.ToString("N0"),
                            (totalPayments == 1 ? "payment" : "payments"));

            if (totalAlreadyDownloaded > 0)
            {
                sb.AppendFormat("<li>{0} {1} previously downloaded and {2} already been added.</li>", totalAlreadyDownloaded.ToString("N0"),
                                (totalAlreadyDownloaded == 1 ? "payment was" : "payments were"),
                                (totalAlreadyDownloaded == 1 ? "has" : "have"));
            }

            if (totalStatusChanges > 0)
            {
                sb.AppendFormat("<li>{0} {1} previously downloaded but had a change of status.</li>", totalStatusChanges.ToString("N0"),
                                (totalStatusChanges == 1 ? "payment was" : "payments were"));
            }

            if (paymentsWithoutTransaction.Any())
            {
                var scheduledPaymentList = paymentsWithoutTransaction.Where(a => a.GatewayScheduleId.IsNotNullOrWhiteSpace()).Select(a => a.GatewayScheduleId).ToList();
                if (scheduledPaymentList.Any())
                {
                    if (verboseLogging)
                    {
                        sb.Append($@"<li>The following {scheduledPaymentList.Count.ToString( "N0" )} gateway payments could not be matched to an existing scheduled payment profile:
<pre>{scheduledPaymentList.AsDelimited( "\n" )}</pre>
</li>");
                    }
                    else
                    {
                        sb.Append($"<li>{scheduledPaymentList.Count.ToString( "N0" )} gateway payments could not be matched to an existing scheduled payment profile.</li>");
                    }
                }

                var previousTransactionList = paymentsWithoutTransaction.Where(a => a.GatewayScheduleId.IsNullOrWhiteSpace()).Select(a => a.TransactionCode).ToList();

                if (previousTransactionList.Any())
                {
                    if (verboseLogging)
                    {
                        sb.Append($@"<li>The following {previousTransactionList.Count.ToString( "N0" )} gateway payments could not be matched to a previous transaction:
<pre>{previousTransactionList.AsDelimited( "\n" )}</pre>
</li>");
                    }
                    else
                    {
                        sb.Append($"<li>{previousTransactionList.Count.ToString( "N0" )} gateway payments could not be matched to a previous transaction.</li>");
                    }
                }
            }

            sb.AppendFormat("<li>{0} {1} added.</li>", totalAdded.ToString("N0"),
                            (totalAdded == 1 ? "new payment was" : "new payments were"));

            if (totalReversals > 0)
            {
                sb.AppendFormat("<li>{0} {1} added as a reversal to a previous transaction.</li>", totalReversals.ToString("N0"),
                                (totalReversals == 1 ? "payment was" : "payments were"));
            }

            if (totalFailures > 0)
            {
                sb.AppendFormat("<li>{0} {1} recorded as a failed transaction.</li>", totalFailures.ToString("N0"),
                                (totalFailures == 1 ? "payment was" : "payments were"));
            }

            using (var rockContext = new RockContext())
            {
                var batches = new FinancialBatchService(rockContext)
                              .Queryable().AsNoTracking()
                              .Where(b => batchSummary.Keys.Contains(b.Guid))
                              .ToList();

                foreach (var batchItem in batchSummary)
                {
                    int items = batchItem.Value.Count;
                    if (items > 0)
                    {
                        var batch = batches
                                    .Where(b => b.Guid.Equals(batchItem.Key))
                                    .FirstOrDefault();

                        string batchName = string.Format("'{0} ({1})'", batch.Name, batch.BatchStartDateTime.Value.ToString("d"));
                        if (!string.IsNullOrWhiteSpace(batchUrlFormat))
                        {
                            batchName = string.Format("<a href='{0}'>{1}</a>", string.Format(batchUrlFormat, batch.Id), batchName);
                        }

                        decimal sum = batchItem.Value.Sum();

                        string summaryformat = items == 1 ?
                                               "<li>{0} transaction of {1} was added to the {2} batch.</li>" :
                                               "<li>{0} transactions totaling {1} were added to the {2} batch</li>";

                        sb.AppendFormat(summaryformat, items.ToString("N0"), sum.FormatAsCurrency(), batchName);
                    }
                }
            }

            return(sb.ToString());
        }
        /// <summary>
        /// Gets the expression.
        /// </summary>
        /// <param name="entityType">Type of the entity.</param>
        /// <param name="serviceInstance">The service instance.</param>
        /// <param name="parameterExpression">The parameter expression.</param>
        /// <param name="selection">The selection.</param>
        /// <returns></returns>
        public override Expression GetExpression( Type entityType, IService serviceInstance, ParameterExpression parameterExpression, string selection )
        {
            var rockContext = (RockContext)serviceInstance.Context;

            string[] selectionValues = selection.Split( '|' );
            if ( selectionValues.Length < 3 )
            {
                return null;
            }

            DateTime? startDate = selectionValues[0].AsDateTime();
            DateTime? endDate = selectionValues[1].AsDateTime();
            var accountGuids = selectionValues[2].Split( ',' ).Select( a => a.AsGuid() ).ToList();
            var accountIdList = new FinancialAccountService( (RockContext)serviceInstance.Context ).GetByGuids( accountGuids ).Select( a => a.Id ).ToList();

            int transactionTypeContributionId = Rock.Web.Cache.DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid() ).Id;

            var financialTransactionsQry = new FinancialTransactionService( rockContext ).Queryable()
                .Where( xx => xx.TransactionTypeValueId == transactionTypeContributionId );

            if ( accountIdList.Any() )
            {
                if ( accountIdList.Count == 1 )
                {
                    int accountId = accountIdList.First();
                    financialTransactionsQry = financialTransactionsQry.Where( xx => xx.TransactionDetails.Any( a => a.AccountId == accountId ) );
                }
                else
                {
                    financialTransactionsQry = financialTransactionsQry.Where( xx => xx.TransactionDetails.Any( a => accountIdList.Contains( a.AccountId ) ) );
                }
            }

            var firstContributionDateQry = financialTransactionsQry
                .GroupBy( xx => xx.AuthorizedPersonAlias.PersonId )
                .Select( ss => new
                {
                    PersonId = ss.Key,
                    FirstTransactionDateTime = ss.Min( a => a.TransactionDateTime )
                } );

            if ( startDate.HasValue )
            {
                firstContributionDateQry = firstContributionDateQry.Where( xx => xx.FirstTransactionDateTime >= startDate.Value );
            }

            if ( endDate.HasValue )
            {
                firstContributionDateQry = firstContributionDateQry.Where( xx => xx.FirstTransactionDateTime < endDate );
            }

            var innerQry = firstContributionDateQry.Select( xx => xx.PersonId ).AsQueryable();

            var qry = new PersonService( rockContext ).Queryable()
                .Where( p => innerQry.Any( xx => xx == p.Id ) );

            Expression extractedFilterExpression = FilterExpressionExtractor.Extract<Rock.Model.Person>( qry, parameterExpression, "p" );

            return extractedFilterExpression;
        }
Exemple #29
0
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid()
        {
            var rockContext = new RockContext();
            var transactionIdsQry = new FinancialTransactionService( rockContext ).Queryable();
            var transactionDetailService = new FinancialTransactionDetailService( rockContext );
            var qry = transactionDetailService.Queryable();

            var targetPerson = this.ContextEntity<Person>();
            if ( targetPerson != null )
            {
                if ( targetPerson.GivingGroupId.HasValue )
                {
                    // gives as part of giving group
                    var groupMemberQry = new GroupMemberService( rockContext ).GetByGroupId( targetPerson.GivingGroupId.Value );
                    qry = qry.Where( a => groupMemberQry.Any( b => b.PersonId == a.Transaction.AuthorizedPersonAlias.PersonId ) );
                }
                else
                {
                    // gives individually
                    qry = qry.Where( a => a.Transaction.AuthorizedPersonAlias.PersonId == targetPerson.Id );
                }
            }

            var qryTransactionsDetailsByYear = qry
                .Where( a => a.Transaction.TransactionDateTime.HasValue )
                .GroupBy( a => SqlFunctions.DatePart( "year", a.Transaction.TransactionDateTime.Value ) );

            var qryAccountSummariesByYear = qryTransactionsDetailsByYear.Select( a => new
            {
                Year = a.Key,
                Accounts = a.GroupBy( b => b.Account ).Select( c => new
                {
                    Account = c.Key,
                    TotalAmount = c.Sum( d => d.Amount )
                } ).OrderBy( e => e.Account.Name )
            } ).OrderByDescending( a => a.Year );

            var summaryList = qryAccountSummariesByYear.ToList();

            var mergeObjects = GlobalAttributesCache.GetMergeFields( this.CurrentPerson );

            var yearsMergeObjects = new List<Dictionary<string, object>>();
            foreach ( var item in summaryList )
            {
                var accountsList = new List<object>();
                foreach ( var a in item.Accounts )
                {
                    var accountDictionary = new Dictionary<string, object>();
                    accountDictionary.Add( "Account", a.Account );
                    accountDictionary.Add( "TotalAmount", a.TotalAmount );
                    accountsList.Add( accountDictionary );
                }

                var yearDictionary = new Dictionary<string, object>();
                yearDictionary.Add( "Year", item.Year );
                yearDictionary.Add( "SummaryRows", accountsList );

                yearsMergeObjects.Add( yearDictionary );
            }

            mergeObjects.Add( "Rows", yearsMergeObjects );

            lLavaOutput.Text = string.Empty;
            if ( GetAttributeValue( "EnableDebug" ).AsBooleanOrNull().GetValueOrDefault( false ) )
            {
                lLavaOutput.Text = mergeObjects.lavaDebugInfo( rockContext );
            }

            string template = GetAttributeValue( "LavaTemplate" );

            lLavaOutput.Text += template.ResolveMergeFields( mergeObjects ).ResolveClientIds( upnlContent.ClientID );
        }
        /// <summary>
        /// Gets the warnings.
        /// </summary>
        /// <returns></returns>
        private List<string> GetWarnings( FinancialBatch batch )
        {
            var warningList = new List<string>();
            if ( batch.Status == BatchStatus.Open )
            {
                var transactionService = new FinancialTransactionService( new RockContext() );
                var transactionList = transactionService.Queryable().Where( trans => trans.BatchId == batch.Id && trans.AuthorizedPersonId == null ).ToList();
                if ( transactionList.Count > 0 )
                {
                    warningList.Add( "UNTIED" );
                }
            }

            return warningList;
        }
        /// <summary>
        /// Processes the confirmation.
        /// </summary>
        /// <param name="errorMessage">The error message.</param>
        /// <returns></returns>
        private bool ProcessConfirmation( out string errorMessage )
        {
            var rockContext = new RockContext();
            if ( string.IsNullOrWhiteSpace( TransactionCode ) )
            {
                var transactionGuid = hfTransactionGuid.Value.AsGuid();

                bool isACHTxn = hfPaymentTab.Value == "ACH";
                var financialGateway = isACHTxn ? _achGateway : _ccGateway;
                var gateway = isACHTxn ? _achGatewayComponent : _ccGatewayComponent;

                if ( gateway == null )
                {
                    errorMessage = "There was a problem creating the payment gateway information";
                    return false;
                }

                // only create/update the person if they are giving as a person. If they are giving as a Business, the person record already exists
                Person person = GetPerson( !phGiveAsOption.Visible || tglGiveAsOption.Checked );
                if ( person == null )
                {
                    errorMessage = "There was a problem creating the person information";
                    return false;
                }

                if ( !person.PrimaryAliasId.HasValue )
                {
                    errorMessage = "There was a problem creating the person's primary alias";
                    return false;
                }

                Person BusinessOrPerson = GetPersonOrBusiness( person );

                PaymentInfo paymentInfo = GetTxnPaymentInfo( BusinessOrPerson, out errorMessage );
                if ( paymentInfo == null )
                {
                    return false;
                }

                PaymentSchedule schedule = GetSchedule();
                FinancialPaymentDetail paymentDetail = null;
                if ( schedule != null )
                {
                    schedule.PersonId = person.Id;

                    var scheduledTransactionAlreadyExists = new FinancialScheduledTransactionService( rockContext ).Queryable().FirstOrDefault( a => a.Guid == transactionGuid );
                    if ( scheduledTransactionAlreadyExists != null )
                    {
                        // hopefully shouldn't happen, but just in case the scheduledtransaction already went thru, show the success screen
                        ShowSuccess( gateway, person, paymentInfo, schedule, scheduledTransactionAlreadyExists.FinancialPaymentDetail, rockContext );
                        return true;
                    }

                    var scheduledTransaction = gateway.AddScheduledPayment( financialGateway, schedule, paymentInfo, out errorMessage );
                    if ( scheduledTransaction == null )
                    {
                        return false;
                    }

                    // manually assign the Guid that we generated at the beginning of the transaction UI entry to help make duplicate scheduled transactions impossible
                    scheduledTransaction.Guid = transactionGuid;

                    SaveScheduledTransaction( financialGateway, gateway, BusinessOrPerson, paymentInfo, schedule, scheduledTransaction, rockContext );
                    paymentDetail = scheduledTransaction.FinancialPaymentDetail.Clone( false );
                }
                else
                {
                    var transactionAlreadyExists = new FinancialTransactionService( rockContext ).Queryable().FirstOrDefault( a => a.Guid == transactionGuid );
                    if ( transactionAlreadyExists != null )
                    {
                        // hopefully shouldn't happen, but just in case the transaction already went thru, show the success screen
                        ShowSuccess( gateway, person, paymentInfo, null, transactionAlreadyExists.FinancialPaymentDetail, rockContext );
                        return true;
                    }

                    var transaction = gateway.Charge( financialGateway, paymentInfo, out errorMessage );
                    if ( transaction == null )
                    {
                        return false;
                    }

                    // manually assign the Guid that we generated at the beginning of the transaction UI entry to help make duplicate transactions impossible
                    transaction.Guid = transactionGuid;

                    SaveTransaction( financialGateway, gateway, BusinessOrPerson, paymentInfo, transaction, rockContext );
                    paymentDetail = transaction.FinancialPaymentDetail.Clone( false );
                }

                ShowSuccess( gateway, person, paymentInfo, schedule, paymentDetail, rockContext );

                return true;
            }
            else
            {
                pnlDupWarning.Visible = true;
                errorMessage = string.Empty;
                return false;
            }
        }
Exemple #32
0
        /// <summary>
        /// Handles the Click event of the btnNext 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 btnNext_Click( object sender, EventArgs e )
        {
            var changes = new List<string>();

            var rockContext = new RockContext();
            var financialTransactionService = new FinancialTransactionService( rockContext );
            var financialTransactionDetailService = new FinancialTransactionDetailService( rockContext );
            var financialPersonBankAccountService = new FinancialPersonBankAccountService( rockContext );
            int txnId = hfTransactionId.Value.AsInteger();
            var financialTransaction = financialTransactionService
                    .Queryable( "AuthorizedPersonAlias.Person,ProcessedByPersonAlias.Person" )
                    .FirstOrDefault( t => t.Id == txnId );

            // set the AuthorizedPersonId (the person who wrote the check, for example) to the if the SelectNew person (if selected) or person selected in the drop down (if there is somebody selected)
            int? authorizedPersonId = ppSelectNew.PersonId ?? ddlIndividual.SelectedValue.AsIntegerOrNull();

            var accountNumberSecured = hfCheckMicrHashed.Value;

            if ( cbTotalAmount.Text.AsDecimalOrNull().HasValue && !authorizedPersonId.HasValue )
            {
                nbSaveError.Text = "Transaction must be matched to a person when the amount is specified.";
                nbSaveError.Visible = true;
                return;
            }

            // if the transaction was previously matched, but user unmatched it, save it as an unmatched transaction and clear out the detail records (we don't want an unmatched transaction to have detail records)
            if ( financialTransaction != null &&
                financialTransaction.AuthorizedPersonAliasId.HasValue &&
                !authorizedPersonId.HasValue )
            {
                financialTransaction.AuthorizedPersonAliasId = null;
                foreach ( var detail in financialTransaction.TransactionDetails )
                {
                    History.EvaluateChange( changes, detail.Account != null ? detail.Account.Name : "Unknown", detail.Amount.ToString( "C2" ), string.Empty );
                    financialTransactionDetailService.Delete( detail );
                }

                changes.Add( "Unmatched transaction" );

                HistoryService.SaveChanges(
                    rockContext,
                    typeof( FinancialBatch ),
                    Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(),
                    financialTransaction.BatchId.Value,
                    changes,
                    string.Format( "Transaction Id: {0}", financialTransaction.Id ),
                    typeof( FinancialTransaction ),
                    financialTransaction.Id,
                    false
                );

                rockContext.SaveChanges();

                // if the transaction was unmatched, clear out the ProcessedBy fields since we didn't match the transaction and are moving on to process another transaction
                MarkTransactionAsNotProcessedByCurrentUser( hfTransactionId.Value.AsInteger() );
            }

            // if the transaction is matched to somebody, attempt to save it.  Otherwise, if the transaction was previously matched, but user unmatched it, save it as an unmatched transaction
            if ( financialTransaction != null && authorizedPersonId.HasValue )
            {
                bool requiresMicr =
                    financialTransaction.FinancialPaymentDetail != null &&
                    financialTransaction.FinancialPaymentDetail.CurrencyTypeValue != null &&
                    financialTransaction.FinancialPaymentDetail.CurrencyTypeValue.Guid == Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CHECK.AsGuid();

                if ( cbTotalAmount.Text.AsDecimalOrNull() == null )
                {
                    nbSaveError.Text = "Total amount must be allocated to accounts.";
                    nbSaveError.Visible = true;
                    return;
                }

                var personAlias = new PersonAliasService( rockContext ).GetPrimaryAlias( authorizedPersonId.Value );
                int? personAliasId = personAlias != null ? personAlias.Id : (int?)null;

                // if this transaction has an accountnumber associated with it (in other words, it's a valid scanned check), ensure there is a financialPersonBankAccount record
                if ( financialTransaction.MICRStatus == MICRStatus.Success && !string.IsNullOrWhiteSpace( accountNumberSecured ) )
                {
                    var financialPersonBankAccount = financialPersonBankAccountService.Queryable().Where( a => a.AccountNumberSecured == accountNumberSecured && a.PersonAlias.PersonId == authorizedPersonId.Value ).FirstOrDefault();
                    if ( financialPersonBankAccount == null )
                    {
                        if ( personAliasId.HasValue )
                        {
                            financialPersonBankAccount = new FinancialPersonBankAccount();
                            financialPersonBankAccount.PersonAliasId = personAliasId.Value;
                            financialPersonBankAccount.AccountNumberSecured = accountNumberSecured;

                            var checkMicrClearText = Encryption.DecryptString( financialTransaction.CheckMicrParts );
                            var parts = checkMicrClearText.Split( '_' );
                            if ( parts.Length >= 2 )
                            {
                                financialPersonBankAccount.AccountNumberMasked = parts[1].Masked();
                            }

                            financialPersonBankAccountService.Add( financialPersonBankAccount );
                        }
                    }
                }

                string prevPerson = ( financialTransaction.AuthorizedPersonAlias != null && financialTransaction.AuthorizedPersonAlias.Person != null ) ?
                    financialTransaction.AuthorizedPersonAlias.Person.FullName : string.Empty;
                string newPerson = string.Empty;
                if ( personAliasId.HasValue )
                {
                    newPerson = personAlias.Person.FullName;
                    financialTransaction.AuthorizedPersonAliasId = personAliasId;
                }

                History.EvaluateChange( changes, "Person", prevPerson, newPerson );

                // just in case this transaction is getting re-edited either by the same user, or somebody else, clean out any existing TransactionDetail records
                foreach ( var detail in financialTransaction.TransactionDetails.ToList() )
                {
                    financialTransactionDetailService.Delete( detail );
                    History.EvaluateChange( changes, detail.Account != null ? detail.Account.Name : "Unknown", detail.Amount.ToString( "C2" ), string.Empty );
                }

                foreach ( var accountBox in rptAccounts.ControlsOfTypeRecursive<CurrencyBox>() )
                {
                    var amount = accountBox.Text.AsDecimalOrNull();

                    if ( amount.HasValue && amount.Value >= 0 )
                    {
                        var financialTransactionDetail = new FinancialTransactionDetail();
                        financialTransactionDetail.TransactionId = financialTransaction.Id;
                        financialTransactionDetail.AccountId = accountBox.Attributes["data-account-id"].AsInteger();
                        financialTransactionDetail.Amount = amount.Value;
                        financialTransactionDetailService.Add( financialTransactionDetail );

                        History.EvaluateChange( changes, accountBox.Label, 0.0M.ToString( "C2" ), amount.Value.ToString( "C2" ) );

                    }
                }

                financialTransaction.ProcessedByPersonAliasId = this.CurrentPersonAlias.Id;
                financialTransaction.ProcessedDateTime = RockDateTime.Now;

                changes.Add( "Matched transaction" );

                HistoryService.SaveChanges(
                    rockContext,
                    typeof( FinancialBatch ),
                    Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(),
                    financialTransaction.BatchId.Value,
                    changes,
                    personAlias != null && personAlias.Person != null ? personAlias.Person.FullName : string.Format( "Transaction Id: {0}", financialTransaction.Id ),
                    typeof( FinancialTransaction ),
                    financialTransaction.Id,
                    false
                );

                rockContext.SaveChanges();
            }
            else
            {
                // if the transaction was not matched, clear out the ProcessedBy fields since we didn't match the transaction and are moving on to process another transaction
                MarkTransactionAsNotProcessedByCurrentUser( hfTransactionId.Value.AsInteger() );
            }

            NavigateToTransaction( Direction.Next );
        }
        /// <summary>
        /// Handles the Click event of the lbSaveAccount 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 lbSaveAccount_Click( object sender, EventArgs e )
        {
            if ( string.IsNullOrWhiteSpace( TransactionCode ) )
            {
                nbSaveAccount.Text = "Sorry, the account information cannot be saved as there's not a valid transaction code to reference";
                nbSaveAccount.Visible = true;
                return;
            }

            using ( var rockContext = new RockContext() )
            {
                if ( phCreateLogin.Visible )
                {
                    if ( string.IsNullOrWhiteSpace( txtUserName.Text ) || string.IsNullOrWhiteSpace( txtPassword.Text ) )
                    {
                        nbSaveAccount.Title = "Missing Informaton";
                        nbSaveAccount.Text = "A username and password are required when saving an account";
                        nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                        nbSaveAccount.Visible = true;
                        return;
                    }

                    if ( new UserLoginService( rockContext ).GetByUserName( txtUserName.Text ) != null )
                    {
                        nbSaveAccount.Title = "Invalid Username";
                        nbSaveAccount.Text = "The selected Username is already being used.  Please select a different Username";
                        nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                        nbSaveAccount.Visible = true;
                        return;
                    }

                    if ( !UserLoginService.IsPasswordValid( txtPassword.Text ) )
                    {
                        nbSaveAccount.Title = string.Empty;
                        nbSaveAccount.Text = UserLoginService.FriendlyPasswordRules();
                        nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                        nbSaveAccount.Visible = true;
                        return;
                    }

                    if ( txtPasswordConfirm.Text != txtPassword.Text )
                    {
                        nbSaveAccount.Title = "Invalid Password";
                        nbSaveAccount.Text = "The password and password confirmation do not match";
                        nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                        nbSaveAccount.Visible = true;
                        return;
                    }
                }

                if ( !string.IsNullOrWhiteSpace( txtSaveAccount.Text ) )
                {
                    bool isACHTxn = hfPaymentTab.Value == "ACH";
                    var financialGateway = isACHTxn ? _achGateway : _ccGateway;
                    var gateway = isACHTxn ? _achGatewayComponent : _ccGatewayComponent;

                    if ( gateway != null )
                    {
                        var ccCurrencyType = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD ) );
                        var achCurrencyType = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_ACH ) );

                        string errorMessage = string.Empty;

                        var person = GetPerson( false );
                        string referenceNumber = string.Empty;
                        FinancialPaymentDetail paymentDetail = null;
                        int? currencyTypeValueId = isACHTxn ? achCurrencyType.Id : ccCurrencyType.Id;

                        if ( string.IsNullOrWhiteSpace( ScheduleId ) )
                        {
                            var transaction = new FinancialTransactionService( rockContext ).GetByTransactionCode( TransactionCode );
                            if ( transaction != null && transaction.AuthorizedPersonAlias != null )
                            {
                                if ( transaction.FinancialGateway != null )
                                {
                                    transaction.FinancialGateway.LoadAttributes( rockContext );
                                }
                                referenceNumber = gateway.GetReferenceNumber( transaction, out errorMessage );
                                paymentDetail = transaction.FinancialPaymentDetail;
                            }
                        }
                        else
                        {
                            var scheduledTransaction = new FinancialScheduledTransactionService( rockContext ).GetByScheduleId( ScheduleId );
                            if ( scheduledTransaction != null )
                            {
                                if ( scheduledTransaction.FinancialGateway != null )
                                {
                                    scheduledTransaction.FinancialGateway.LoadAttributes( rockContext );
                                }
                                referenceNumber = gateway.GetReferenceNumber( scheduledTransaction, out errorMessage );
                                paymentDetail = scheduledTransaction.FinancialPaymentDetail;
                            }
                        }

                        if ( person != null && paymentDetail != null )
                        {
                            if ( phCreateLogin.Visible )
                            {
                                var user = UserLoginService.Create(
                                    rockContext,
                                    person,
                                    Rock.Model.AuthenticationServiceType.Internal,
                                    EntityTypeCache.Read( Rock.SystemGuid.EntityType.AUTHENTICATION_DATABASE.AsGuid() ).Id,
                                    txtUserName.Text,
                                    txtPassword.Text,
                                    false );

                                var mergeFields = Rock.Lava.LavaHelper.GetCommonMergeFields( this.RockPage, this.CurrentPerson );
                                mergeFields.Add( "ConfirmAccountUrl", RootPath + "ConfirmAccount" );

                                var personDictionary = person.ToLiquid() as Dictionary<string, object>;
                                mergeFields.Add( "Person", personDictionary );

                                mergeFields.Add( "User", user );

                                var recipients = new List<Rock.Communication.RecipientData>();
                                recipients.Add( new Rock.Communication.RecipientData( person.Email, mergeFields ) );

                                Rock.Communication.Email.Send( GetAttributeValue( "ConfirmAccountTemplate" ).AsGuid(), recipients, ResolveRockUrl( "~/" ), ResolveRockUrl( "~~/" ), false );
                            }

                            if ( errorMessage.Any() )
                            {
                                nbSaveAccount.Title = "Invalid Transaction";
                                nbSaveAccount.Text = "Sorry, the account information cannot be saved. " + errorMessage;
                                nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                                nbSaveAccount.Visible = true;
                            }
                            else
                            {
                                var savedAccount = new FinancialPersonSavedAccount();
                                savedAccount.PersonAliasId = person.PrimaryAliasId;
                                savedAccount.ReferenceNumber = referenceNumber;
                                savedAccount.Name = txtSaveAccount.Text;
                                savedAccount.TransactionCode = TransactionCode;
                                savedAccount.FinancialGatewayId = financialGateway.Id;
                                savedAccount.FinancialPaymentDetail = new FinancialPaymentDetail();
                                savedAccount.FinancialPaymentDetail.AccountNumberMasked = paymentDetail.AccountNumberMasked;
                                savedAccount.FinancialPaymentDetail.CurrencyTypeValueId = paymentDetail.CurrencyTypeValueId;
                                savedAccount.FinancialPaymentDetail.CreditCardTypeValueId = paymentDetail.CreditCardTypeValueId;
                                savedAccount.FinancialPaymentDetail.NameOnCardEncrypted = paymentDetail.NameOnCardEncrypted;
                                savedAccount.FinancialPaymentDetail.ExpirationMonthEncrypted = paymentDetail.ExpirationMonthEncrypted;
                                savedAccount.FinancialPaymentDetail.ExpirationYearEncrypted = paymentDetail.ExpirationYearEncrypted;
                                savedAccount.FinancialPaymentDetail.BillingLocationId = paymentDetail.BillingLocationId;

                                var savedAccountService = new FinancialPersonSavedAccountService( rockContext );
                                savedAccountService.Add( savedAccount );
                                rockContext.SaveChanges();

                                cbSaveAccount.Visible = false;
                                txtSaveAccount.Visible = false;
                                phCreateLogin.Visible = false;
                                divSaveActions.Visible = false;

                                nbSaveAccount.Title = "Success";
                                nbSaveAccount.Text = "The account has been saved for future use";
                                nbSaveAccount.NotificationBoxType = NotificationBoxType.Success;
                                nbSaveAccount.Visible = true;
                            }
                        }
                        else
                        {
                            nbSaveAccount.Title = "Invalid Transaction";
                            nbSaveAccount.Text = "Sorry, the account information cannot be saved as there's not a valid transaction code to reference.";
                            nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                            nbSaveAccount.Visible = true;
                        }
                    }
                    else
                    {
                        nbSaveAccount.Title = "Invalid Gateway";
                        nbSaveAccount.Text = "Sorry, the financial gateway information for this type of transaction is not valid.";
                        nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                        nbSaveAccount.Visible = true;
                    }
                }
                else
                {
                    nbSaveAccount.Title = "Missing Account Name";
                    nbSaveAccount.Text = "Please enter a name to use for this account.";
                    nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                    nbSaveAccount.Visible = true;
                }
            }
        }
Exemple #34
0
        /// <summary>
        /// Handles the Click event of the btnSaveFinancialTransaction 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 lbSaveTransaction_Click(object sender, EventArgs e)
        {
            var rockContext = new RockContext();
            var financialTransactionService = new Rock.Model.FinancialTransactionService(rockContext);

            Rock.Model.FinancialTransaction financialTransaction = null;
            int financialTransactionId = !string.IsNullOrEmpty(hfIdTransValue.Value) ? int.Parse(hfIdTransValue.Value) : 0;

            // null if not associated with a batch
            int?batchId = hfBatchId.Value.AsInteger();

            if (financialTransactionId == 0)
            {
                financialTransaction = new Rock.Model.FinancialTransaction();
                financialTransactionService.Add(financialTransaction);
                financialTransaction.BatchId = batchId;
            }
            else
            {
                financialTransaction = financialTransactionService.Get(financialTransactionId);
            }

            if (ppAuthorizedPerson.PersonId != null)
            {
                financialTransaction.AuthorizedPersonId = ppAuthorizedPerson.PersonId;
            }
            else
            {
                financialTransaction.AuthorizedPersonId = null;
            }

            if (ddlCurrencyType.SelectedItem.ToString() == "Credit Card")
            {
                financialTransaction.CreditCardTypeValueId = int.Parse(ddlCreditCardType.SelectedValue);
            }
            else
            {
                financialTransaction.CreditCardTypeValueId = null;
            }

            financialTransaction.CurrencyTypeValueId = int.Parse(ddlCurrencyType.SelectedValue);
            if (!string.IsNullOrEmpty(ddlPaymentGateway.SelectedValue))
            {
                var gatewayEntity = Rock.Web.Cache.EntityTypeCache.Read(new Guid(ddlPaymentGateway.SelectedValue));
                if (gatewayEntity != null)
                {
                    financialTransaction.GatewayEntityTypeId = gatewayEntity.Id;
                }
            }

            financialTransaction.SourceTypeValueId      = int.Parse(ddlSourceType.SelectedValue);
            financialTransaction.TransactionTypeValueId = int.Parse(ddlTransactionType.SelectedValue);

            financialTransaction.Summary             = tbSummary.Text;
            financialTransaction.TransactionCode     = tbTransactionCode.Text;
            financialTransaction.TransactionDateTime = dtTransactionDateTime.SelectedDateTime;

            rockContext.SaveChanges();

            if (batchId != null)
            {
                Dictionary <string, string> qryString = new Dictionary <string, string>();
                qryString["financialBatchid"] = hfBatchId.Value;
                NavigateToParentPage(qryString);
            }
            else
            {
                NavigateToParentPage();
            }
        }
        /// <summary>
        /// Handles the Delete event of the gTransactions control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="Rock.Web.UI.Controls.RowEventArgs" /> instance containing the event data.</param>
        protected void gTransactions_Delete( object sender, Rock.Web.UI.Controls.RowEventArgs e )
        {
            var rockContext = new RockContext();
            var transactionService = new FinancialTransactionService( rockContext );
            var transaction = transactionService.Get( e.RowKeyId );
            if ( transaction != null )
            {
                string errorMessage;
                if ( !transactionService.CanDelete( transaction, out errorMessage ) )
                {
                    mdGridWarning.Show( errorMessage, ModalAlertType.Information );
                    return;
                }

                // prevent deleting a Transaction that is in closed batch
                if (transaction.Batch != null )
                {
                    if ( transaction.Batch.Status == BatchStatus.Closed )
                    {
                        mdGridWarning.Show( string.Format( "This {0} is assigned to a closed {1}", FinancialTransaction.FriendlyTypeName, FinancialBatch.FriendlyTypeName ), ModalAlertType.Information );
                        return;
                    }
                }

                if ( transaction.BatchId.HasValue )
                {
                    string caption = ( transaction.AuthorizedPersonAlias != null && transaction.AuthorizedPersonAlias.Person != null ) ?
                        transaction.AuthorizedPersonAlias.Person.FullName :
                        string.Format( "Transaction: {0}", transaction.Id );

                    HistoryService.SaveChanges(
                        rockContext,
                        typeof( FinancialBatch ),
                        Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(),
                        transaction.BatchId.Value,
                        new List<string> { "Deleted transaction" },
                        caption,
                        typeof( FinancialTransaction ),
                        transaction.Id,
                        false
                    );
                }

                transactionService.Delete( transaction );

                rockContext.SaveChanges();

                RockPage.UpdateBlocks( "~/Blocks/Finance/BatchDetail.ascx" );
            }

            BindGrid();
        }
Exemple #36
0
        /// <summary>
        /// Navigates to the next (or previous) transaction to edit
        /// </summary>
        private void NavigateToTransaction( Direction direction )
        {
            // put a lock around the entire NavigateToTransaction logic so that the navigation and "other person editing" logic will work consistently even if multiple people are editing the same batch
            lock ( transactionMatchingLockObject )
            {
                hfDoFadeIn.Value = "1";
                nbSaveError.Visible = false;
                int? fromTransactionId = hfTransactionId.Value.AsIntegerOrNull();
                int? toTransactionId = null;
                List<int> historyList = hfBackNextHistory.Value.Split( new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries ).AsIntegerList().Where( a => a > 0 ).ToList();
                int position = hfHistoryPosition.Value.AsIntegerOrNull() ?? -1;

                if ( direction == Direction.Prev )
                {
                    position--;
                }
                else
                {
                    position++;
                }

                if ( historyList.Count > position )
                {
                    if ( position >= 0 )
                    {
                        toTransactionId = historyList[position];
                    }
                    else
                    {
                        // if we trying to go previous when we are already at the start of the list, wrap around to the last item in the list
                        toTransactionId = historyList.Last();
                        position = historyList.Count - 1;
                    }
                }

                hfHistoryPosition.Value = position.ToString();

                int batchId = hfBatchId.Value.AsInteger();
                var rockContext = new RockContext();
                var financialPersonBankAccountService = new FinancialPersonBankAccountService( rockContext );
                var financialTransactionService = new FinancialTransactionService( rockContext );
                var qryTransactionsToMatch = financialTransactionService.Queryable()
                    .Where( a => a.AuthorizedPersonAliasId == null && a.ProcessedByPersonAliasId == null );

                if ( batchId != 0 )
                {
                    qryTransactionsToMatch = qryTransactionsToMatch.Where( a => a.BatchId == batchId );
                }

                // if a specific transactionId was specified (because we are navigating thru history), load that one. Otherwise, if a batch is specified, get the first unmatched transaction in that batch
                if ( toTransactionId.HasValue )
                {
                    qryTransactionsToMatch = financialTransactionService
                        .Queryable( "AuthorizedPersonAlias.Person,ProcessedByPersonAlias.Person" )
                        .Where( a => a.Id == toTransactionId );
                }

                if ( historyList.Any() && !toTransactionId.HasValue )
                {
                    // since we are looking for a transaction we haven't viewed or matched yet, look for the next one in the database that we haven't seen yet
                    qryTransactionsToMatch = qryTransactionsToMatch.Where( a => !historyList.Contains( a.Id ) );
                }

                qryTransactionsToMatch = qryTransactionsToMatch.OrderBy( a => a.CreatedDateTime ).ThenBy( a => a.Id );

                FinancialTransaction transactionToMatch = qryTransactionsToMatch.FirstOrDefault();
                if ( transactionToMatch == null )
                {
                    // we exhausted the transactions that aren't processed and aren't in our history list, so remove those those restrictions and show all transactions that haven't been matched yet
                    var qryRemainingTransactionsToMatch = financialTransactionService
                        .Queryable( "AuthorizedPersonAlias.Person,ProcessedByPersonAlias.Person" )
                        .Where( a => a.AuthorizedPersonAliasId == null );

                    if ( batchId != 0 )
                    {
                        qryRemainingTransactionsToMatch = qryRemainingTransactionsToMatch.Where( a => a.BatchId == batchId );
                    }

                    // get the first transaction that we haven't visited yet, or the next one we have visited after one we are on, or simple the first unmatched one
                    transactionToMatch = qryRemainingTransactionsToMatch.Where( a => a.Id > fromTransactionId && !historyList.Contains( a.Id ) ).FirstOrDefault()
                        ?? qryRemainingTransactionsToMatch.Where( a => a.Id > fromTransactionId ).FirstOrDefault()
                        ?? qryRemainingTransactionsToMatch.FirstOrDefault();
                    if ( transactionToMatch != null )
                    {
                        historyList.Add( transactionToMatch.Id );
                        position = historyList.LastIndexOf( transactionToMatch.Id );
                        hfHistoryPosition.Value = position.ToString();
                    }
                }
                else
                {
                    if ( !toTransactionId.HasValue )
                    {
                        historyList.Add( transactionToMatch.Id );
                    }
                }

                nbNoUnmatchedTransactionsRemaining.Visible = transactionToMatch == null;
                pnlEdit.Visible = transactionToMatch != null;
                nbIsInProcess.Visible = false;
                if ( transactionToMatch != null )
                {
                    if ( transactionToMatch.ProcessedByPersonAlias != null )
                    {
                        if ( transactionToMatch.AuthorizedPersonAliasId.HasValue )
                        {
                            nbIsInProcess.Text = string.Format( "Warning. This transaction was matched by {0} at {1} ({2})", transactionToMatch.ProcessedByPersonAlias.Person, transactionToMatch.ProcessedDateTime.ToString(), transactionToMatch.ProcessedDateTime.ToRelativeDateString() );
                            nbIsInProcess.Visible = true;
                        }
                        else
                        {
                            // display a warning if some other user has this marked as InProcess (and it isn't matched)
                            if ( transactionToMatch.ProcessedByPersonAliasId != CurrentPersonAliasId )
                            {
                                nbIsInProcess.Text = string.Format( "Warning. This transaction is getting processed by {0} as of {1} ({2})", transactionToMatch.ProcessedByPersonAlias.Person, transactionToMatch.ProcessedDateTime.ToString(), transactionToMatch.ProcessedDateTime.ToRelativeDateString() );
                                nbIsInProcess.Visible = true;
                            }
                        }
                    }

                    // Unless somebody else is processing it, immediately mark the transaction as getting processed by the current person so that other potentional transaction matching sessions will know that it is currently getting looked at
                    if ( !transactionToMatch.ProcessedByPersonAliasId.HasValue )
                    {
                        transactionToMatch.ProcessedByPersonAlias = null;
                        transactionToMatch.ProcessedByPersonAliasId = CurrentPersonAliasId;
                        transactionToMatch.ProcessedDateTime = RockDateTime.Now;
                        rockContext.SaveChanges();
                    }

                    hfTransactionId.Value = transactionToMatch.Id.ToString();

                    // get the first 2 images (should be no more than 2, but just in case)
                    var transactionImages = transactionToMatch.Images.OrderBy( a => a.Order ).Take( 2 ).ToList();

                    ddlIndividual.Items.Clear();
                    ddlIndividual.Items.Add( new ListItem( null, null ) );
                    // clear any previously shown badges
                    ddlIndividual.Attributes.Remove( "disabled" );
                    badgeIndividualCount.InnerText = "";

                    // if this transaction has a CheckMicrParts, try to find matching person(s)
                    string checkMicrHashed = null;

                    if ( !string.IsNullOrWhiteSpace( transactionToMatch.CheckMicrParts ) )
                    {
                        try
                        {
                            var checkMicrClearText = Encryption.DecryptString( transactionToMatch.CheckMicrParts );
                            var parts = checkMicrClearText.Split( '_' );
                            if ( parts.Length >= 2 )
                            {
                                checkMicrHashed = FinancialPersonBankAccount.EncodeAccountNumber( parts[0], parts[1] );
                            }
                        }
                        catch
                        {
                            // intentionally ignore exception when decripting CheckMicrParts since we'll be checking for null below
                        }
                    }

                    hfCheckMicrHashed.Value = checkMicrHashed;

                    if ( !string.IsNullOrWhiteSpace( checkMicrHashed ) )
                    {
                        var matchedPersons = financialPersonBankAccountService.Queryable().Where( a => a.AccountNumberSecured == checkMicrHashed ).Select( a => a.PersonAlias.Person ).Distinct();
                        foreach ( var person in matchedPersons.OrderBy( a => a.LastName ).ThenBy( a => a.NickName ) )
                        {
                            ddlIndividual.Items.Add( new ListItem( person.FullNameReversed, person.Id.ToString() ) );
                        }
                    }

                    bool requiresMicr =
                        transactionToMatch.FinancialPaymentDetail != null &&
                        transactionToMatch.FinancialPaymentDetail.CurrencyTypeValue != null &&
                        transactionToMatch.FinancialPaymentDetail.CurrencyTypeValue.Guid == Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CHECK.AsGuid();

                    // if this is a check, and the MICR could not be accurately read, show the warning
                    nbBadMicrWarning.Visible = requiresMicr && transactionToMatch.MICRStatus == MICRStatus.Fail;

                    if ( ddlIndividual.Items.Count == 2 )
                    {
                        // only one person (and the None selection) are in the list, so init to the person
                        ddlIndividual.SelectedIndex = 1;
                    }
                    else
                    {
                        // either zero or multiple people are in the list, so default to none so they are forced to choose
                        ddlIndividual.SelectedIndex = 0;
                    }

                    if ( transactionToMatch.AuthorizedPersonAlias != null && transactionToMatch.AuthorizedPersonAlias.Person != null )
                    {
                        var person = transactionToMatch.AuthorizedPersonAlias.Person;

                        // if the drop down does not contains the AuthorizedPerson of this transaction, add them to the drop down
                        // note, this can easily happen for non-check transactions
                        if ( !ddlIndividual.Items.OfType<ListItem>().Any( a => a.Value == person.Id.ToString() ) )
                        {
                            ddlIndividual.Items.Add( new ListItem( person.FullNameReversed, person.Id.ToString() ) );
                        }

                        ddlIndividual.SelectedValue = person.Id.ToString();
                    }

                    if ( ddlIndividual.Items.Count != 1 )
                    {
                        badgeIndividualCount.InnerText = ( ddlIndividual.Items.Count - 1 ).ToStringSafe();
                    }
                    else
                    {
                        ddlIndividual.Attributes["disabled"] = "disabled";
                        _focusControl = ppSelectNew;
                    }

                    ddlIndividual_SelectedIndexChanged( null, null );

                    ppSelectNew.SetValue( null );
                    if ( transactionToMatch.TransactionDetails.Any() )
                    {
                        cbTotalAmount.Text = transactionToMatch.TotalAmount.ToString();
                    }
                    else
                    {
                        cbTotalAmount.Text = string.Empty;
                    }

                    // update accountboxes
                    foreach ( var accountBox in rptAccounts.ControlsOfTypeRecursive<CurrencyBox>() )
                    {
                        accountBox.Text = string.Empty;
                    }

                    foreach ( var detail in transactionToMatch.TransactionDetails )
                    {
                        var accountBox = rptAccounts.ControlsOfTypeRecursive<CurrencyBox>().Where( a => a.Attributes["data-account-id"].AsInteger() == detail.AccountId ).FirstOrDefault();
                        if ( accountBox != null )
                        {
                            accountBox.Text = detail.Amount.ToString();
                        }
                    }

                    if ( transactionToMatch.Images.Any() )
                    {
                        var primaryImage = transactionToMatch.Images
                            .OrderBy( i => i.Order )
                            .FirstOrDefault();
                        imgPrimary.ImageUrl = string.Format( "~/GetImage.ashx?id={0}", primaryImage.BinaryFileId );
                        imgPrimary.Visible = true;
                        nbNoTransactionImageWarning.Visible = false;

                        rptrImages.DataSource = transactionToMatch.Images
                            .Where( i => !i.Id.Equals( primaryImage.Id ) )
                            .OrderBy( i => i.Order )
                            .ToList();
                        rptrImages.DataBind();
                    }
                    else
                    {
                        imgPrimary.Visible = false;
                        rptrImages.DataSource = null;
                        rptrImages.DataBind();
                        nbNoTransactionImageWarning.Visible = true;
                    }
                }
                else
                {
                    hfTransactionId.Value = string.Empty;
                }

                // display how many unmatched transactions are remaining
                var qryTransactionCount = financialTransactionService.Queryable();
                if ( batchId != 0 )
                {
                    qryTransactionCount = qryTransactionCount.Where( a => a.BatchId == batchId );
                }

                // get count of transactions that haven't been matched (not including the one we are currently editing)
                int currentTranId = hfTransactionId.Value.AsInteger();
                int matchedRemainingCount = qryTransactionCount.Count( a => a.AuthorizedPersonAliasId != null && a.Id != currentTranId );
                int totalBatchItemCount = qryTransactionCount.Count();

                int percentComplete = (int)Math.Round( (double)(100 * matchedRemainingCount) / totalBatchItemCount );

                lProgressBar.Text = String.Format( @"<div class='progress'>
                    <div class='progress-bar progress-bar-info' role='progressbar' aria-valuenow='{0}' aria-valuemin='0' aria-valuemax='100' style='width: {0}%;'>
                        {0}%
                    </div>
                </div>", percentComplete);

                hfBackNextHistory.Value = historyList.AsDelimited( "," );

                if ( _focusControl == null )
                {
                    _focusControl = rptAccounts.ControlsOfTypeRecursive<Rock.Web.UI.Controls.CurrencyBox>().FirstOrDefault();
                }
            }
        }
        /// <summary>
        /// Binds the grid.
        /// </summary>
        private void BindGrid( bool isExporting = false )
        {
            _currencyTypes = new Dictionary<int,string>();
            _creditCardTypes = new Dictionary<int,string>();

            // If configured for a registration and registration is null, return
            int registrationEntityTypeId = EntityTypeCache.Read( typeof( Rock.Model.Registration ) ).Id;
            if ( ContextTypesRequired.Any( e => e.Id == registrationEntityTypeId ) && _registration == null )
            {
                return;
            }

            // If configured for a person and person is null, return
            int personEntityTypeId = EntityTypeCache.Read( "Rock.Model.Person" ).Id;
            if ( ContextTypesRequired.Any( e => e.Id == personEntityTypeId ) && _person == null )
            {
                return;
            }

            // If configured for a batch and batch is null, return
            int batchEntityTypeId = EntityTypeCache.Read( "Rock.Model.FinancialBatch" ).Id;
            if ( ContextTypesRequired.Any( e => e.Id == batchEntityTypeId ) && _batch == null )
            {
                return;
            }

            // If configured for a batch and batch is null, return
            int scheduledTxnEntityTypeId = EntityTypeCache.Read( "Rock.Model.FinancialScheduledTransaction" ).Id;
            if ( ContextTypesRequired.Any( e => e.Id == scheduledTxnEntityTypeId ) && _scheduledTxn == null )
            {
                return;
            }

            // Qry
            var rockContext = new RockContext();
            var qry = new FinancialTransactionService( rockContext ).Queryable();

            // Transaction Types
            var transactionTypeValueIdList = GetAttributeValue( "TransactionTypes" ).SplitDelimitedValues().AsGuidList().Select( a => DefinedValueCache.Read( a ) ).Where( a => a != null ).Select( a => a.Id ).ToList();

            if ( transactionTypeValueIdList.Any() )
            {
                qry = qry.Where( t => transactionTypeValueIdList.Contains( t.TransactionTypeValueId ) );
            }

            // Set up the selection filter
            if ( _batch != null )
            {
                // If transactions are for a batch, the filter is hidden so only check the batch id
                qry = qry.Where( t => t.BatchId.HasValue && t.BatchId.Value == _batch.Id );

                // If the batch is closed, do not allow any editing of the transactions
                if ( _batch.Status != BatchStatus.Closed && _canEdit )
                {
                    gTransactions.IsDeleteEnabled = _canEdit;
                }
                else
                {
                    gTransactions.IsDeleteEnabled = false;
                }
            }
            else if ( _scheduledTxn != null )
            {
                // If transactions are for a batch, the filter is hidden so only check the batch id
                qry = qry.Where( t => t.ScheduledTransactionId.HasValue && t.ScheduledTransactionId.Value == _scheduledTxn.Id );

                gTransactions.IsDeleteEnabled = false;
            }
            else if ( _registration != null )
            {
                qry = qry
                    .Where( t => t.TransactionDetails
                        .Any( d =>
                            d.EntityTypeId.HasValue &&
                            d.EntityTypeId.Value == registrationEntityTypeId &&
                            d.EntityId.HasValue &&
                            d.EntityId.Value == _registration.Id ) );

                gTransactions.IsDeleteEnabled = false;
            }
            else    // Person
            {
                // otherwise set the selection based on filter settings
                if ( _person != null )
                {
                    // get the transactions for the person or all the members in the person's giving group (Family)
                    qry = qry.Where( t => t.AuthorizedPersonAlias.Person.GivingId == _person.GivingId );
                }

                // Date Range
                var drp = new DateRangePicker();
                drp.DelimitedValues = gfTransactions.GetUserPreference( "Date Range" );
                if ( drp.LowerValue.HasValue )
                {
                    qry = qry.Where( t => t.TransactionDateTime >= drp.LowerValue.Value );
                }

                if ( drp.UpperValue.HasValue )
                {
                    DateTime upperDate = drp.UpperValue.Value.Date.AddDays( 1 );
                    qry = qry.Where( t => t.TransactionDateTime < upperDate );
                }

                // Amount Range
                var nre = new NumberRangeEditor();
                nre.DelimitedValues = gfTransactions.GetUserPreference( "Amount Range" );
                if ( nre.LowerValue.HasValue )
                {
                    qry = qry.Where( t => t.TransactionDetails.Sum( d => d.Amount ) >= nre.LowerValue.Value );
                }

                if ( nre.UpperValue.HasValue )
                {
                    qry = qry.Where( t => t.TransactionDetails.Sum( d => d.Amount ) <= nre.UpperValue.Value );
                }

                // Transaction Code
                string transactionCode = gfTransactions.GetUserPreference( "Transaction Code" );
                if ( !string.IsNullOrWhiteSpace( transactionCode ) )
                {
                    qry = qry.Where( t => t.TransactionCode == transactionCode.Trim() );
                }

                // Account Id
                var accountIds = (gfTransactions.GetUserPreference( "Account" ) ?? "").SplitDelimitedValues().AsIntegerList().Where( a => a > 0 ).ToList();
                {
                    if ( accountIds.Any() )
                    {
                        qry = qry.Where( t => t.TransactionDetails.Any( d => accountIds.Contains( d.AccountId ) || (d.Account.ParentAccountId.HasValue && accountIds.Contains(d.Account.ParentAccountId.Value) ) ) );
                    }
                }

                // Transaction Type
                int transactionTypeId = int.MinValue;
                if ( int.TryParse( gfTransactions.GetUserPreference( "Transaction Type" ), out transactionTypeId ) )
                {
                    qry = qry.Where( t => t.TransactionTypeValueId == transactionTypeId );
                }

                // Currency Type
                int currencyTypeId = int.MinValue;
                if ( int.TryParse( gfTransactions.GetUserPreference( "Currency Type" ), out currencyTypeId ) )
                {
                    qry = qry.Where( t => t.FinancialPaymentDetail != null && t.FinancialPaymentDetail.CurrencyTypeValueId == currencyTypeId );
                }

                // Credit Card Type
                int creditCardTypeId = int.MinValue;
                if ( int.TryParse( gfTransactions.GetUserPreference( "Credit Card Type" ), out creditCardTypeId ) )
                {
                    qry = qry.Where( t => t.FinancialPaymentDetail != null && t.FinancialPaymentDetail.CreditCardTypeValueId == creditCardTypeId );
                }

                // Source Type
                int sourceTypeId = int.MinValue;
                if ( int.TryParse( gfTransactions.GetUserPreference( "Source Type" ), out sourceTypeId ) )
                {
                    qry = qry.Where( t => t.SourceTypeValueId == sourceTypeId );
                }

                // Campus
                if ( this.ContextEntity() == null )
                {
                    var campus = CampusCache.Read( gfTransactions.GetUserPreference( "Campus" ).AsInteger() );
                    if ( campus != null )
                    {
                        qry = qry.Where( b => b.Batch != null && b.Batch.CampusId == campus.Id );
                    }
                }
            }

            SortProperty sortProperty = gTransactions.SortProperty;
            if ( sortProperty != null )
            {
                if ( sortProperty.Property == "TotalAmount" )
                {
                    if ( sortProperty.Direction == SortDirection.Ascending )
                    {
                        qry = qry.OrderBy( t => t.TransactionDetails.Sum( d => (decimal?)d.Amount ) ?? 0.00M );
                    }
                    else
                    {
                        qry = qry.OrderByDescending( t => t.TransactionDetails.Sum( d => (decimal?)d.Amount ) ?? 0.0M );
                    }
                }
                else
                {
                    qry = qry.Sort( sortProperty );
                }
            }
            else
            {
                // Default sort by Id if the transations are seen via the batch,
                // otherwise sort by descending date time.
                if ( ContextTypesRequired.Any( e => e.Id == batchEntityTypeId ) )
                {
                    qry = qry.OrderBy( t => t.Id );
                }
                else
                {
                    qry = qry.OrderByDescending( t => t.TransactionDateTime ).ThenByDescending( t => t.Id );
                }
            }

            var lTransactionImageField = gTransactions.ColumnsOfType<RockLiteralField>().FirstOrDefault( a => a.ID == "lTransactionImage" );
            var summaryField = gTransactions.ColumnsOfType<RockBoundField>().FirstOrDefault( a => a.DataField == "Summary" );
            var showImages = bddlOptions.SelectedValue.AsIntegerOrNull() == 1;
            if ( lTransactionImageField != null)
            {
                lTransactionImageField.Visible = showImages;
            }

            if ( summaryField != null )
            {
                summaryField.Visible = !showImages;
            }

            if ( showImages )
            {
                qry = qry.Include( a => a.Images );
            }

            _isExporting = isExporting;

            gTransactions.SetLinqDataSource( qry.AsNoTracking() );
            gTransactions.DataBind();

            _isExporting = false;

            if ( _batch == null &&
                _scheduledTxn == null &&
                _registration == null &&
                _person == null )
            {
                pnlSummary.Visible = true;

                // No context - show account summary
                var qryTransactionDetails = qry.SelectMany( a => a.TransactionDetails );
                var qryFinancialAccount = new FinancialAccountService( rockContext ).Queryable();
                var accountSummaryQry = qryTransactionDetails.GroupBy( a => a.AccountId ).Select( a => new
                {
                    AccountId = a.Key,
                    TotalAmount = (decimal?)a.Sum( d => d.Amount )
                } ).Join( qryFinancialAccount, k1 => k1.AccountId, k2 => k2.Id, ( td, fa ) => new { td.TotalAmount, fa.Name, fa.Order } )
                .OrderBy( a => a.Order );

                var summaryList = accountSummaryQry.ToList();
                var grandTotalAmount = ( summaryList.Count > 0 ) ? summaryList.Sum( a => a.TotalAmount ?? 0 ) : 0;
                lGrandTotal.Text = grandTotalAmount.FormatAsCurrency();
                rptAccountSummary.DataSource = summaryList.Select( a => new { a.Name, TotalAmount = a.TotalAmount.FormatAsCurrency() } ).ToList();
                rptAccountSummary.DataBind();
            }
            else
            {
                pnlSummary.Visible = false;
            }
        }
        /// <summary>
        /// Processes the payments.
        /// </summary>
        /// <param name="gateway">The gateway.</param>
        /// <param name="batchNamePrefix">The batch name prefix.</param>
        /// <param name="payments">The payments.</param>
        /// <param name="batchUrlFormat">The batch URL format.</param>
        /// <returns></returns>
        public static string ProcessPayments( FinancialGateway gateway, string batchNamePrefix, List<Payment> payments, string batchUrlFormat = "" )
        {
            int totalPayments = 0;
            int totalAlreadyDownloaded = 0;
            int totalNoScheduledTransaction = 0;
            int totalAdded = 0;

            var batches = new List<FinancialBatch>();
            var batchSummary = new Dictionary<Guid, List<Payment>>();
            var initialControlAmounts = new Dictionary<Guid, decimal>();

            var allBatchChanges = new Dictionary<Guid, List<string>>();
            var allTxnChanges = new Dictionary<Guid, List<string>>();
            var txnPersonNames = new Dictionary<Guid, string>();

            using ( var rockContext = new RockContext() )
            {
                var accountService = new FinancialAccountService( rockContext );
                var txnService = new FinancialTransactionService( rockContext );
                var batchService = new FinancialBatchService( rockContext );
                var scheduledTxnService = new FinancialScheduledTransactionService( rockContext );

                var contributionTxnType = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid() );

                var defaultAccount = accountService.Queryable()
                    .Where( a =>
                        a.IsActive &&
                        !a.ParentAccountId.HasValue &&
                        ( !a.StartDate.HasValue || a.StartDate.Value <= RockDateTime.Now ) &&
                        ( !a.EndDate.HasValue || a.EndDate.Value >= RockDateTime.Now )
                        )
                    .OrderBy( a => a.Order )
                    .FirstOrDefault();

                var batchTxnChanges = new Dictionary<Guid, List<string>>();
                var batchBatchChanges = new Dictionary<Guid, List<string>>();

                foreach ( var payment in payments.Where( p => p.Amount > 0.0M ) )
                {
                    totalPayments++;

                    // Only consider transactions that have not already been added
                    if ( txnService.GetByTransactionCode( payment.TransactionCode ) == null )
                    {
                        var scheduledTransaction = scheduledTxnService.GetByScheduleId( payment.GatewayScheduleId );
                        if ( scheduledTransaction != null )
                        {
                            scheduledTransaction.IsActive = payment.ScheduleActive;

                            var txnChanges = new List<string>();

                            var transaction = new FinancialTransaction();
                            transaction.FinancialPaymentDetail = new FinancialPaymentDetail();

                            transaction.Guid = Guid.NewGuid();
                            allTxnChanges.Add( transaction.Guid, txnChanges );
                            txnChanges.Add( "Created Transaction (Downloaded from Gateway)" );

                            transaction.TransactionCode = payment.TransactionCode;
                            History.EvaluateChange( txnChanges, "Transaction Code", string.Empty, transaction.TransactionCode );

                            transaction.TransactionDateTime = payment.TransactionDateTime;
                            History.EvaluateChange( txnChanges, "Date/Time", null, transaction.TransactionDateTime );

                            transaction.ScheduledTransactionId = scheduledTransaction.Id;

                            transaction.AuthorizedPersonAliasId = scheduledTransaction.AuthorizedPersonAliasId;
                            History.EvaluateChange( txnChanges, "Person", string.Empty, scheduledTransaction.AuthorizedPersonAlias.Person.FullName );
                            txnPersonNames.Add( transaction.Guid, scheduledTransaction.AuthorizedPersonAlias.Person.FullName );

                            transaction.FinancialGatewayId = gateway.Id;
                            History.EvaluateChange( txnChanges, "Gateway", string.Empty, gateway.Name );

                            transaction.TransactionTypeValueId = contributionTxnType.Id;
                            History.EvaluateChange( txnChanges, "Type", string.Empty, contributionTxnType.Value );

                            var currencyTypeValue = payment.CurrencyTypeValue;
                            var creditCardTypevalue = payment.CreditCardTypeValue;

                            if ( scheduledTransaction.FinancialPaymentDetail != null )
                            {
                                if ( currencyTypeValue == null && scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.HasValue )
                                {
                                    currencyTypeValue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.Value );
                                }

                                if ( creditCardTypevalue == null && scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.HasValue )
                                {
                                    creditCardTypevalue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.Value );
                                }

                                transaction.FinancialPaymentDetail.AccountNumberMasked = scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked;
                                transaction.FinancialPaymentDetail.NameOnCardEncrypted = scheduledTransaction.FinancialPaymentDetail.NameOnCardEncrypted;
                                transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationMonthEncrypted;
                                transaction.FinancialPaymentDetail.ExpirationYearEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationYearEncrypted;
                            }

                            if ( currencyTypeValue != null )
                            {
                                transaction.FinancialPaymentDetail.CurrencyTypeValueId = currencyTypeValue.Id;
                                History.EvaluateChange( txnChanges, "Currency Type", string.Empty, currencyTypeValue.Value );
                            }
                            if ( creditCardTypevalue != null )
                            {
                                transaction.FinancialPaymentDetail.CreditCardTypeValueId = creditCardTypevalue.Id;
                                History.EvaluateChange( txnChanges, "Credit Card Type", string.Empty, creditCardTypevalue.Value );
                            }

                            //transaction.SourceTypeValueId = DefinedValueCache.Read( sourceGuid ).Id;

                            // Try to allocate the amount of the transaction based on the current scheduled transaction accounts
                            decimal remainingAmount = payment.Amount;
                            foreach ( var detail in scheduledTransaction.ScheduledTransactionDetails.Where( d => d.Amount != 0.0M ) )
                            {
                                var transactionDetail = new FinancialTransactionDetail();
                                transactionDetail.AccountId = detail.AccountId;

                                if ( detail.Amount <= remainingAmount )
                                {
                                    // If the configured amount for this account is less than or equal to the remaining
                                    // amount, allocate the configured amount
                                    transactionDetail.Amount = detail.Amount;
                                    remainingAmount -= detail.Amount;
                                }
                                else
                                {
                                    // If the configured amount is greater than the remaining amount, only allocate
                                    // the remaining amount
                                    transaction.Summary = "Note: Downloaded transaction amount was less than the configured allocation amounts for the Scheduled Transaction.";
                                    detail.Amount = remainingAmount;
                                    detail.Summary = "Note: The downloaded amount was not enough to apply the configured amount to this account.";
                                    remainingAmount = 0.0M;
                                }

                                transaction.TransactionDetails.Add( transactionDetail );

                                History.EvaluateChange( txnChanges, detail.Account.Name, 0.0M.ToString( "C2" ), transactionDetail.Amount.ToString( "C2" ) );
                                History.EvaluateChange( txnChanges, "Summary", string.Empty, transactionDetail.Summary );

                                if ( remainingAmount <= 0.0M )
                                {
                                    // If there's no amount left, break out of details
                                    break;
                                }
                            }

                            // If there's still amount left after allocating based on current config, add the remainder
                            // to the account that was configured for the most amount
                            if ( remainingAmount > 0.0M )
                            {
                                transaction.Summary = "Note: Downloaded transaction amount was greater than the configured allocation amounts for the Scheduled Transaction.";
                                var transactionDetail = transaction.TransactionDetails
                                    .OrderByDescending( d => d.Amount )
                                    .First();
                                if ( transactionDetail == null && defaultAccount != null )
                                {
                                    transactionDetail = new FinancialTransactionDetail();
                                    transactionDetail.AccountId = defaultAccount.Id;
                                }
                                if ( transactionDetail != null )
                                {
                                    transactionDetail.Amount += remainingAmount;
                                    transactionDetail.Summary = "Note: Extra amount was applied to this account.";
                                }

                                History.EvaluateChange( txnChanges, defaultAccount.Name, 0.0M.ToString( "C2" ), transactionDetail.Amount.ToString( "C2" ) );
                                History.EvaluateChange( txnChanges, "Summary", string.Empty, transactionDetail.Summary );
                            }

                            // Get the batch
                            var batch = batchService.Get(
                                batchNamePrefix,
                                currencyTypeValue,
                                creditCardTypevalue,
                                transaction.TransactionDateTime.Value,
                                gateway.GetBatchTimeOffset(),
                                batches );

                            var batchChanges = new List<string>();
                            if ( batch.Id != 0 )
                            {
                                initialControlAmounts.AddOrIgnore( batch.Guid, batch.ControlAmount );
                            }
                            batch.ControlAmount += transaction.TotalAmount;

                            batch.Transactions.Add( transaction );

                            // Add summary
                            if ( !batchSummary.ContainsKey( batch.Guid ) )
                            {
                                batchSummary.Add( batch.Guid, new List<Payment>() );
                            }
                            batchSummary[batch.Guid].Add( payment );

                            totalAdded++;
                        }
                        else
                        {
                            totalNoScheduledTransaction++;
                        }
                    }
                    else
                    {
                        totalAlreadyDownloaded++;
                    }
                }

                foreach ( var batch in batches )
                {
                    var batchChanges = new List<string>();
                    allBatchChanges.Add( batch.Guid, batchChanges );

                    if ( batch.Id == 0 )
                    {
                        batchChanges.Add( "Generated the batch" );
                        History.EvaluateChange( batchChanges, "Batch Name", string.Empty, batch.Name );
                        History.EvaluateChange( batchChanges, "Status", null, batch.Status );
                        History.EvaluateChange( batchChanges, "Start Date/Time", null, batch.BatchStartDateTime );
                        History.EvaluateChange( batchChanges, "End Date/Time", null, batch.BatchEndDateTime );
                    }

                    if ( initialControlAmounts.ContainsKey( batch.Guid ) )
                    {
                        History.EvaluateChange( batchChanges, "Control Amount", initialControlAmounts[batch.Guid].ToString( "C2" ), batch.ControlAmount.ToString( "C2" ) );
                    }
                }

                rockContext.WrapTransaction( () =>
                {
                    rockContext.SaveChanges();

                    foreach ( var batch in batches )
                    {
                        HistoryService.SaveChanges(
                            rockContext,
                            typeof( FinancialBatch ),
                            Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(),
                            batch.Id,
                            allBatchChanges[batch.Guid]
                        );

                        foreach ( var transaction in batch.Transactions )
                        {
                            if ( allTxnChanges.ContainsKey( transaction.Guid ) )
                            {
                                HistoryService.SaveChanges(
                                    rockContext,
                                    typeof( FinancialBatch ),
                                    Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(),
                                    batch.Id,
                                    allTxnChanges[transaction.Guid],
                                    txnPersonNames[transaction.Guid],
                                    typeof( FinancialTransaction ),
                                    transaction.Id
                                );
                            }
                        }
                    }

                    rockContext.SaveChanges();
                } );
            }

            StringBuilder sb = new StringBuilder();
            sb.AppendFormat( "<li>{0} {1} downloaded.</li>", totalPayments.ToString( "N0" ),
                ( totalPayments == 1 ? "payment" : "payments" ) );

            if ( totalAlreadyDownloaded > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} previously downloaded and {2} already been added.</li>", totalAlreadyDownloaded.ToString( "N0" ),
                    ( totalAlreadyDownloaded == 1 ? "payment was" : "payments were" ),
                    ( totalAlreadyDownloaded == 1 ? "has" : "have" ) );
            }

            if ( totalNoScheduledTransaction > 0 )
            {
                sb.AppendFormat( "<li>{0} {1} could not be matched to an existing scheduled payment profile.</li>", totalNoScheduledTransaction.ToString( "N0" ),
                    ( totalNoScheduledTransaction == 1 ? "payment" : "payments" ) );
            }

            sb.AppendFormat( "<li>{0} {1} successfully added.</li>", totalAdded.ToString( "N0" ),
                ( totalAdded == 1 ? "payment was" : "payments were" ) );

            foreach ( var batchItem in batchSummary )
            {
                int items = batchItem.Value.Count;
                if (items > 0)
                {
                    var batch = batches
                        .Where( b => b.Guid.Equals( batchItem.Key ) )
                        .FirstOrDefault();

                    string batchName = string.Format("'{0} ({1})'", batch.Name, batch.BatchStartDateTime.Value.ToString("d"));
                    if ( !string.IsNullOrWhiteSpace( batchUrlFormat ) )
                    {
                        batchName = string.Format( "<a href='{0}'>{1}</a>", string.Format( batchUrlFormat, batch.Id ), batchName );
                    }

                    decimal sum = batchItem.Value.Select( p => p.Amount ).Sum();

                    string summaryformat = items == 1 ?
                        "<li>{0} transaction of {1} was added to the {2} batch.</li>" :
                        "<li>{0} transactions totaling {1} were added to the {2} batch</li>";

                    sb.AppendFormat( summaryformat, items.ToString( "N0" ), sum.ToString( "C2" ), batchName );
                }
            }

            return sb.ToString();
        }
        /// <summary>
        /// Shows the financial batch summary.
        /// </summary>
        /// <param name="batch">The financial batch.</param>
        private void ShowReadonlyDetails( FinancialBatch batch )
        {
            SetEditMode( false );

            if ( batch != null )
            {
                hfBatchId.SetValue( batch.Id );

                SetHeadingInfo( batch, batch.Name );

                string campusName = string.Empty;
                if ( batch.CampusId.HasValue )
                {
                    var campus = CampusCache.Read( batch.CampusId.Value );
                    if ( campus != null )
                    {
                        campusName = campus.ToString();
                    }
                }

                var rockContext = new RockContext();
                var financialTransactionService = new FinancialTransactionService( rockContext );
                var batchTransactions = financialTransactionService.Queryable().Where( a => a.BatchId.HasValue && a.BatchId.Value == batch.Id );

                var financialTransactionDetailService = new FinancialTransactionDetailService( rockContext );
                var qryTransactionDetails = financialTransactionDetailService.Queryable().Where( a => a.Transaction.BatchId == batch.Id );
                decimal txnTotal = qryTransactionDetails.Select( a => (decimal?)a.Amount ).Sum() ?? 0;

                decimal variance = txnTotal - batch.ControlAmount;
                string amountFormat = string.Format(
                    "{0} / {1} / " + ( variance == 0.0M ? "{2}" : "<span class='label label-danger'>{2}</span>" ),
                    txnTotal.FormatAsCurrency(),
                    batch.ControlAmount.FormatAsCurrency(),
                    variance.FormatAsCurrency() );

                lDetails.Text = new DescriptionList()
                    .Add( "Date Range", new DateRange( batch.BatchStartDateTime, batch.BatchEndDateTime ).ToString( "g" ) )
                    .Add( "Transaction / Control / Variance", amountFormat )
                    .Add( "Accounting Code", batch.AccountingSystemCode )
                    .Add( "Notes", batch.Note )
                    .Html;

                // Account Summary
                gAccounts.DataSource = qryTransactionDetails
                    .GroupBy( d => new
                    {
                        AccountId = d.AccountId,
                        AccountName = d.Account.Name
                    } )
                    .Select( s => new
                    {
                        Id = s.Key.AccountId,
                        Name = s.Key.AccountName,
                        Amount = s.Sum( a => (decimal?)a.Amount ) ?? 0.0M
                    } )
                    .OrderBy( s => s.Name )
                    .ToList();

                gAccounts.DataBind();

                // Currency Summary
                gCurrencyTypes.DataSource = batchTransactions
                    .GroupBy( c => new
                    {
                        CurrencyTypeValueId = c.FinancialPaymentDetailId.HasValue ? c.FinancialPaymentDetail.CurrencyTypeValueId : 0,
                    } )
                    .Select( s => new
                    {
                        CurrencyTypeValueId = s.Key.CurrencyTypeValueId,
                        Amount = s.Sum( a => (decimal?)a.TransactionDetails.Sum( t => t.Amount ) ) ?? 0.0M
                    } )
                    .ToList()
                    .Select( s => new
                    {
                        Id = s.CurrencyTypeValueId,
                        Name = DefinedValueCache.GetName( s.CurrencyTypeValueId ),
                        Amount = s.Amount
                    } ).OrderBy( a => a.Name ).ToList();

                gCurrencyTypes.DataBind();
            }
        }
        /// <summary>
        /// Handles the Click event of the lbSaveAccount 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 lbSaveAccount_Click( object sender, EventArgs e )
        {
            var rockContext = new RockContext();

            if ( string.IsNullOrWhiteSpace( TransactionCode ) )
            {
                nbSaveAccount.Text = "Sorry, the account information cannot be saved as there's not a valid transaction code to reference";
                nbSaveAccount.Visible = true;
                return;
            }

            if ( phCreateLogin.Visible )
            {
                if ( string.IsNullOrWhiteSpace( txtUserName.Text ) || string.IsNullOrWhiteSpace( txtPassword.Text ) )
                {
                    nbSaveAccount.Title = "Missing Informaton";
                    nbSaveAccount.Text = "A username and password are required when saving an account";
                    nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                    nbSaveAccount.Visible = true;
                    return;
                }

                if ( new UserLoginService( rockContext ).GetByUserName( txtUserName.Text ) != null )
                {
                    nbSaveAccount.Title = "Invalid Username";
                    nbSaveAccount.Text = "The selected Username is already being used.  Please select a different Username";
                    nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                    nbSaveAccount.Visible = true;
                    return;
                }

                if ( txtPasswordConfirm.Text != txtPassword.Text )
                {
                    nbSaveAccount.Title = "Invalid Password";
                    nbSaveAccount.Text = "The password and password confirmation do not match";
                    nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                    nbSaveAccount.Visible = true;
                    return;
                }
            }

            if ( !string.IsNullOrWhiteSpace( txtSaveAccount.Text ) )
            {
                GatewayComponent gateway = hfPaymentTab.Value == "ACH" ? _achGateway : _ccGateway;
                var ccCurrencyType = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_CREDIT_CARD ) );
                var achCurrencyType = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.CURRENCY_TYPE_ACH ) );

                string errorMessage = string.Empty;

                PersonAlias authorizedPersonAlias = null;
                string referenceNumber = string.Empty;
                int? currencyTypeValueId = hfPaymentTab.Value == "ACH" ? achCurrencyType.Id : ccCurrencyType.Id;

                if ( string.IsNullOrWhiteSpace( ScheduleId ) )
                {
                    var transaction = new FinancialTransactionService( rockContext ).GetByTransactionCode( TransactionCode );
                    if ( transaction != null && transaction.AuthorizedPersonAlias != null )
                    {
                        authorizedPersonAlias = transaction.AuthorizedPersonAlias;
                        referenceNumber = gateway.GetReferenceNumber( transaction, out errorMessage );
                    }
                }
                else
                {
                    var scheduledTransaction = new FinancialScheduledTransactionService( rockContext ).GetByScheduleId( ScheduleId );
                    if ( scheduledTransaction != null  )
                    {
                        authorizedPersonAlias = scheduledTransaction.AuthorizedPersonAlias;
                        referenceNumber = gateway.GetReferenceNumber( scheduledTransaction, out errorMessage );
                    }
                }

                if ( authorizedPersonAlias != null && authorizedPersonAlias.Person != null )
                {
                    if ( phCreateLogin.Visible )
                    {
                        var user = UserLoginService.Create(
                            rockContext,
                            authorizedPersonAlias.Person,
                            Rock.Model.AuthenticationServiceType.Internal,
                            EntityTypeCache.Read( Rock.SystemGuid.EntityType.AUTHENTICATION_DATABASE.AsGuid() ).Id,
                            txtUserName.Text,
                            txtPassword.Text,
                            false );

                        var mergeObjects = GlobalAttributesCache.GetMergeFields( null );
                        mergeObjects.Add( "ConfirmAccountUrl", RootPath + "ConfirmAccount" );

                        var personDictionary = authorizedPersonAlias.Person.ToLiquid() as Dictionary<string, object>;
                        mergeObjects.Add( "Person", personDictionary );

                        mergeObjects.Add( "User", user );

                        var recipients = new List<Rock.Communication.RecipientData>();
                        recipients.Add( new Rock.Communication.RecipientData( authorizedPersonAlias.Person.Email, mergeObjects ) );

                        Rock.Communication.Email.Send( GetAttributeValue( "ConfirmAccountTemplate" ).AsGuid(), recipients, ResolveRockUrl( "~/" ), ResolveRockUrl( "~~/" ) );
                    }

                    var paymentInfo = GetPaymentInfo();

                    if ( errorMessage.Any() )
                    {
                        nbSaveAccount.Title = "Invalid Transaction";
                        nbSaveAccount.Text = "Sorry, the account information cannot be saved. " + errorMessage;
                        nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                        nbSaveAccount.Visible = true;
                    }
                    else
                    {
                        if ( authorizedPersonAlias != null )
                        {
                            var savedAccount = new FinancialPersonSavedAccount();
                            savedAccount.PersonAliasId = authorizedPersonAlias.Id;
                            savedAccount.ReferenceNumber = referenceNumber;
                            savedAccount.Name = txtSaveAccount.Text;
                            savedAccount.MaskedAccountNumber = paymentInfo.MaskedNumber;
                            savedAccount.TransactionCode = TransactionCode;
                            savedAccount.GatewayEntityTypeId = gateway.TypeId;
                            savedAccount.CurrencyTypeValueId = currencyTypeValueId;
                            savedAccount.CreditCardTypeValueId = CreditCardTypeValueId;

                            var savedAccountService = new FinancialPersonSavedAccountService( rockContext );
                            savedAccountService.Add( savedAccount );
                            rockContext.SaveChanges();

                            cbSaveAccount.Visible = false;
                            txtSaveAccount.Visible = false;
                            phCreateLogin.Visible = false;
                            divSaveActions.Visible = false;

                            nbSaveAccount.Title = "Success";
                            nbSaveAccount.Text = "The account has been saved for future use";
                            nbSaveAccount.NotificationBoxType = NotificationBoxType.Success;
                            nbSaveAccount.Visible = true;
                        }
                    }
                }
                else
                {
                    nbSaveAccount.Title = "Invalid Transaction";
                    nbSaveAccount.Text = "Sorry, the account information cannot be saved as there's not a valid transaction code to reference";
                    nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                    nbSaveAccount.Visible = true;
                }
            }
            else
            {
                nbSaveAccount.Title = "Missing Account Name";
                nbSaveAccount.Text = "Please enter a name to use for this account";
                nbSaveAccount.NotificationBoxType = NotificationBoxType.Danger;
                nbSaveAccount.Visible = true;
            }
        }
Exemple #41
0
        /// <summary>
        /// Marks the transaction as not processed by the current user
        /// </summary>
        /// <param name="transactionId">The transaction identifier.</param>
        private void MarkTransactionAsNotProcessedByCurrentUser( int transactionId )
        {
            var rockContext = new RockContext();
            var financialTransactionService = new FinancialTransactionService( rockContext );
            var financialTransaction = financialTransactionService.Get( transactionId );

            if ( financialTransaction != null &&
                financialTransaction.ProcessedByPersonAliasId == CurrentPersonAliasId &&
                financialTransaction.AuthorizedPersonAliasId == null )
            {
                // if the current user marked this as processed, and it wasn't matched, clear out the processedby fields.  Otherwise, assume the other person is still editing it
                financialTransaction.ProcessedByPersonAliasId = null;
                financialTransaction.ProcessedDateTime = null;
                rockContext.SaveChanges();
            }
        }
        /// <summary>
        /// Handles the Click event of the lbSave 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 lbSave_Click( object sender, EventArgs e )
        {
            var rockContext = new RockContext();

            var txnService = new FinancialTransactionService( rockContext );
            var txnDetailService = new FinancialTransactionDetailService( rockContext );
            var txnImageService = new FinancialTransactionImageService( rockContext );
            var binaryFileService = new BinaryFileService( rockContext );

            FinancialTransaction txn = null;

            int? txnId = hfTransactionId.Value.AsIntegerOrNull();
            int? batchId = hfBatchId.Value.AsIntegerOrNull();

            if ( txnId.HasValue )
            {
                txn = txnService.Get( txnId.Value );
            }

            if ( txn == null )
            {
                txn = new FinancialTransaction();
                txnService.Add( txn );
                txn.BatchId = batchId;
            }

            if ( txn != null )
            {
                if ( ppAuthorizedPerson.PersonId.HasValue )
                {
                    txn.AuthorizedPersonAliasId = ppAuthorizedPerson.PersonAliasId;
                }

                txn.TransactionDateTime = dtTransactionDateTime.SelectedDateTime;
                txn.TransactionTypeValueId = ddlTransactionType.SelectedValue.AsInteger();
                txn.SourceTypeValueId = ddlSourceType.SelectedValueAsInt();

                Guid? gatewayGuid = cpPaymentGateway.SelectedValueAsGuid();
                if ( gatewayGuid.HasValue )
                {
                    var gatewayEntity = EntityTypeCache.Read( gatewayGuid.Value );
                    if ( gatewayEntity != null )
                    {
                        txn.GatewayEntityTypeId = gatewayEntity.Id;
                    }
                    else
                    {
                        txn.GatewayEntityTypeId = null;
                    }
                }
                else
                {
                    txn.GatewayEntityTypeId = null;
                }

                txn.TransactionCode = tbTransactionCode.Text;
                txn.CurrencyTypeValueId = ddlCurrencyType.SelectedValueAsInt();
                txn.CreditCardTypeValueId = ddlCreditCardType.SelectedValueAsInt();
                txn.Summary = tbSummary.Text;

                if ( !Page.IsValid || !txn.IsValid )
                {
                    return;
                }

                foreach ( var txnDetail in TransactionDetailsState )
                {
                    if ( !txnDetail.IsValid )
                    {
                        return;
                    }
                }

                rockContext.WrapTransaction( () =>
                {
                    // Save the transaction
                    rockContext.SaveChanges();

                    // Delete any transaction details that were removed
                    var txnDetailsInDB = txnDetailService.Queryable().Where( a => a.TransactionId.Equals( txn.Id ) ).ToList();
                    var deletedDetails = from txnDetail in txnDetailsInDB
                                         where !TransactionDetailsState.Select( d => d.Guid ).Contains( txnDetail.Guid )
                                         select txnDetail;
                    deletedDetails.ToList().ForEach( txnDetail =>
                    {
                        txnDetailService.Delete( txnDetail );
                    } );
                    rockContext.SaveChanges();

                    // Save Transaction Details
                    foreach ( var editorTxnDetail in TransactionDetailsState )
                    {
                        // Add or Update the activity type
                        var txnDetail = txn.TransactionDetails.FirstOrDefault( d => d.Guid.Equals( editorTxnDetail.Guid ) );
                        if ( txnDetail == null )
                        {
                            txnDetail = new FinancialTransactionDetail();
                            txnDetail.Guid = editorTxnDetail.Guid;
                            txn.TransactionDetails.Add( txnDetail );
                        }
                        txnDetail.AccountId = editorTxnDetail.AccountId;
                        txnDetail.Amount = UseSimpleAccountMode ? tbSingleAccountAmount.Text.AsDecimal() : editorTxnDetail.Amount;
                        txnDetail.Summary = editorTxnDetail.Summary;
                    }
                    rockContext.SaveChanges();

                    // Delete any transaction images that were removed
                    var orphanedBinaryFileIds = new List<int>();
                    var txnImagesInDB = txnImageService.Queryable().Where( a => a.TransactionId.Equals( txn.Id ) ).ToList();
                    foreach ( var txnImage in txnImagesInDB.Where( i => !TransactionImagesState.Contains( i.BinaryFileId ) ) )
                    {
                        orphanedBinaryFileIds.Add( txnImage.BinaryFileId );
                        txnImageService.Delete( txnImage );
                    }

                    // Save Transaction Images
                    int imageOrder = 0;
                    foreach ( var binaryFileId in TransactionImagesState )
                    {
                        // Add or Update the activity type
                        var txnImage = txnImagesInDB.FirstOrDefault( i => i.BinaryFileId == binaryFileId );
                        if ( txnImage == null )
                        {
                            txnImage = new FinancialTransactionImage();
                            txnImage.TransactionId = txn.Id;
                            txn.Images.Add( txnImage );
                        }
                        txnImage.BinaryFileId = binaryFileId;
                        txnImage.Order = imageOrder;
                        imageOrder++;
                    }
                    rockContext.SaveChanges();

                    // Make sure updated binary files are not temporary
                    foreach ( var binaryFile in binaryFileService.Queryable().Where( f => TransactionImagesState.Contains( f.Id ) ) )
                    {
                        binaryFile.IsTemporary = false;
                    }

                    // Delete any orphaned images
                    foreach ( var binaryFile in binaryFileService.Queryable().Where( f => orphanedBinaryFileIds.Contains( f.Id ) ) )
                    {
                        binaryFileService.Delete( binaryFile );
                    }

                    rockContext.SaveChanges();

                } );

                // Save selected options to session state in order to prefill values for next added txn
                Session["NewTxnDefault_BatchId"] = txn.BatchId;
                Session["NewTxnDefault_TransactionDateTime"] = txn.TransactionDateTime;
                Session["NewTxnDefault_TransactionType"] = txn.TransactionTypeValueId;
                Session["NewTxnDefault_SourceType"] = txn.SourceTypeValueId;
                Session["NewTxnDefault_CurrencyType"] = txn.CurrencyTypeValueId;
                Session["NewTxnDefault_CreditCardType"] = txn.CreditCardTypeValueId;
                if ( TransactionDetailsState.Count() == 1 )
                {
                    Session["NewTxnDefault_Account"] = TransactionDetailsState.First().AccountId;
                }
                else
                {
                    Session.Remove("NewTxnDefault_Account");
                }

                // Requery the batch to support EF navigation properties
                var savedTxn = GetTransaction( txn.Id );
                ShowReadOnlyDetails( savedTxn );
            }
        }