/// <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); }
/// <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); }
/// <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(); }
/// <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(); }
/// <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); } }
/// <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(); } } }
/// <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 ); } }
/// <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; }
/// <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(); }
/// <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()); }
/// <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; }
/// <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; } }
/// <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; } } }
/// <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(); }
/// <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; } }
/// <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 ); } }