/// <summary> /// Handles the Click event of the btnSave 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 btnSave_Click(object sender, EventArgs e) { FinancialAccount account; var rockContext = new RockContext(); var accountService = new Rock.Model.FinancialAccountService(rockContext); int accountId = hfAccountId.Value.AsInteger(); if (accountId == 0) { account = new Rock.Model.FinancialAccount(); accountService.Add(account); account.CreatedByPersonAliasId = CurrentPersonAliasId; account.CreatedDateTime = RockDateTime.Now; } else { account = accountService.Get(accountId); } account.Name = tbName.Text; account.IsActive = cbIsActive.Checked; account.IsPublic = cbIsPublic.Checked; account.Description = tbDescription.Text; account.PublicDescription = cePublicDescription.Text; account.ParentAccountId = apParentAccount.SelectedValueAsInt(); account.AccountTypeValueId = ddlAccountType.SelectedValueAsInt(); account.PublicName = tbPublicName.Text; account.Url = tbUrl.Text; account.CampusId = cpCampus.SelectedValueAsInt(); account.GlCode = tbGLCode.Text; account.StartDate = dtpStartDate.SelectedDate; account.EndDate = dtpEndDate.SelectedDate; account.IsTaxDeductible = cbIsTaxDeductible.Checked; account.ModifiedDateTime = RockDateTime.Now; account.ModifiedByPersonAliasId = CurrentPersonAliasId; account.LoadAttributes(rockContext); Rock.Attribute.Helper.GetEditValues(phAttributes, account); // if the account IsValid is false, and the UI controls didn't report any errors, it is probably because the custom rules of account didn't pass. // So, make sure a message is displayed in the validation summary cvAccount.IsValid = account.IsValid; if (!cvAccount.IsValid) { cvAccount.ErrorMessage = account.ValidationResults.Select(a => a.ErrorMessage).ToList().AsDelimited("<br />"); return; } rockContext.SaveChanges(); account.SaveAttributeValues(rockContext); NavigateToParentPage(); }
/// <summary> /// Handles the Click event of the btnSave 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 btnSave_Click(object sender, EventArgs e) { FinancialAccount account; var rockContext = new RockContext(); var accountService = new Rock.Model.FinancialAccountService(rockContext); int accountId = hfAccountId.Value.AsInteger(); if (accountId == 0) { account = new Rock.Model.FinancialAccount(); accountService.Add(account); } else { account = accountService.Get(accountId); } account.Name = tbName.Text; account.IsActive = cbIsActive.Checked; account.IsPublic = cbIsPublic.Checked; account.Description = tbDescription.Text; account.PublicDescription = cePublicDescription.Text; account.ParentAccountId = apParentAccount.SelectedValueAsInt(); account.AccountTypeValueId = ddlAccountType.SelectedValueAsInt(); account.PublicName = tbPublicName.Text; account.Url = tbUrl.Text; account.CampusId = cpCampus.SelectedValueAsInt(); account.GlCode = tbGLCode.Text; account.StartDate = dtpStartDate.SelectedDate; account.EndDate = dtpEndDate.SelectedDate; account.IsTaxDeductible = cbIsTaxDeductible.Checked; rockContext.SaveChanges(); NavigateToParentPage(); }
/// <summary> /// Handles the Click event of the btnSaveAccount 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 btnSaveAccount_Click(object sender, EventArgs e) { using (new Rock.Data.UnitOfWorkScope()) { var accountService = new Rock.Model.FinancialAccountService(); Rock.Model.FinancialAccount modifiedAccount; int accountId = (hfAccountId.Value) != null?Int32.Parse(hfAccountId.Value) : 0; if (accountId == 0) { modifiedAccount = new Rock.Model.FinancialAccount(); accountService.Add(modifiedAccount, CurrentPersonId); } else { modifiedAccount = accountService.Get(accountId); } modifiedAccount.Name = tbName.Text; modifiedAccount.PublicName = tbPublicName.Text; modifiedAccount.Description = tbDescription.Text; modifiedAccount.Order = Int32.Parse(tbOrder.Text); modifiedAccount.GlCode = tbGLCode.Text; modifiedAccount.IsActive = cbIsActive.Checked; modifiedAccount.IsTaxDeductible = cbIsTaxDeductible.Checked; modifiedAccount.StartDate = dtpStartDate.SelectedDate; modifiedAccount.EndDate = dtpEndDate.SelectedDate; modifiedAccount.ParentAccountId = apParentAccount.SelectedValueAsInt(); modifiedAccount.AccountTypeValueId = ddlAccountType.SelectedValueAsInt(); accountService.Save(modifiedAccount, CurrentPersonId); } //BindCategoryFilter(); BindGrid(); pnlAccountDetails.Visible = false; pnlAccountList.Visible = true; }
/// <summary> /// Handles the Click event of the btnSave 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 btnSave_Click( object sender, EventArgs e ) { FinancialAccount account; using ( new Rock.Data.UnitOfWorkScope() ) { var accountService = new Rock.Model.FinancialAccountService(); int accountId = Int32.Parse( hfAccountId.Value ); if ( accountId == 0 ) { account = new Rock.Model.FinancialAccount(); accountService.Add( account, CurrentPersonId ); } else { account = accountService.Get( accountId ); } account.Name = tbName.Text; account.IsActive = cbIsActive.Checked; account.Description = tbDescription.Text; account.ParentAccountId = apParentAccount.SelectedValueAsInt(); account.AccountTypeValueId = ddlAccountType.SelectedValueAsInt(); account.PublicName = tbPublicName.Text; account.GlCode = tbGLCode.Text; account.StartDate = dtpStartDate.SelectedDate; account.EndDate = dtpEndDate.SelectedDate; account.IsTaxDeductible = cbIsTaxDeductible.Checked; accountService.Save( account, CurrentPersonId ); } NavigateToParentPage(); }
/// <summary> /// Handles the Click event of the btnSave 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 btnSave_Click(object sender, EventArgs e) { FinancialAccount account; using (new Rock.Data.UnitOfWorkScope()) { var accountService = new Rock.Model.FinancialAccountService(); int accountId = Int32.Parse(hfAccountId.Value); if (accountId == 0) { account = new Rock.Model.FinancialAccount(); accountService.Add(account, CurrentPersonId); } else { account = accountService.Get(accountId); } account.Name = tbName.Text; account.IsActive = cbIsActive.Checked; account.Description = tbDescription.Text; account.ParentAccountId = apParentAccount.SelectedValueAsInt(); account.AccountTypeValueId = ddlAccountType.SelectedValueAsInt(); account.PublicName = tbPublicName.Text; account.GlCode = tbGLCode.Text; account.StartDate = dtpStartDate.SelectedDate; account.EndDate = dtpEndDate.SelectedDate; account.IsTaxDeductible = cbIsTaxDeductible.Checked; accountService.Save(account, CurrentPersonId); } NavigateToParentPage(); }
/// <summary> /// Handles the Click event of the btnSave 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 btnSave_Click( object sender, EventArgs e ) { FinancialAccount account; var rockContext = new RockContext(); var accountService = new Rock.Model.FinancialAccountService( rockContext ); int accountId = hfAccountId.Value.AsInteger(); if ( accountId == 0 ) { account = new Rock.Model.FinancialAccount(); accountService.Add( account ); } else { account = accountService.Get( accountId ); } account.Name = tbName.Text; account.IsActive = cbIsActive.Checked; account.Description = tbDescription.Text; account.ParentAccountId = apParentAccount.SelectedValueAsInt(); account.AccountTypeValueId = ddlAccountType.SelectedValueAsInt(); account.PublicName = tbPublicName.Text; account.CampusId = cpCampus.SelectedValueAsInt(); account.GlCode = tbGLCode.Text; account.StartDate = dtpStartDate.SelectedDate; account.EndDate = dtpEndDate.SelectedDate; account.IsTaxDeductible = cbIsTaxDeductible.Checked; rockContext.SaveChanges(); NavigateToParentPage(); }
/// <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> /// 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> /// Handles the Click event of the btnSave 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 btnSave_Click(object sender, EventArgs e) { FinancialAccount account; var rockContext = new RockContext(); var accountService = new Rock.Model.FinancialAccountService(rockContext); int accountId = hfAccountId.Value.AsInteger(); if (accountId == 0) { account = new Rock.Model.FinancialAccount(); accountService.Add(account); account.CreatedByPersonAliasId = CurrentPersonAliasId; account.CreatedDateTime = RockDateTime.Now; } else { account = accountService.Get(accountId); } account.Name = tbName.Text; account.IsActive = cbIsActive.Checked; account.IsPublic = cbIsPublic.Checked; account.Description = tbDescription.Text; account.PublicDescription = cePublicDescription.Text; account.ParentAccountId = apParentAccount.SelectedValueAsInt(); account.AccountTypeValueId = dvpAccountType.SelectedValueAsInt(); account.PublicName = tbPublicName.Text; account.Url = tbUrl.Text; account.CampusId = cpCampus.SelectedValueAsInt(); account.GlCode = tbGLCode.Text; account.StartDate = dtpStartDate.SelectedDate; account.EndDate = dtpEndDate.SelectedDate; account.IsTaxDeductible = cbIsTaxDeductible.Checked; account.ModifiedDateTime = RockDateTime.Now; account.ModifiedByPersonAliasId = CurrentPersonAliasId; account.LoadAttributes(rockContext); Rock.Attribute.Helper.GetEditValues(phAttributes, account); // if the account IsValid is false, and the UI controls didn't report any errors, it is probably because the custom rules of account didn't pass. // So, make sure a message is displayed in the validation summary cvAccount.IsValid = account.IsValid; if (!cvAccount.IsValid) { cvAccount.ErrorMessage = account.ValidationResults.Select(a => a.ErrorMessage).ToList().AsDelimited("<br />"); return; } if (accountId == 0) { // just added, but we'll need an Id to set account participants rockContext.SaveChanges(); accountId = new FinancialAccountService(new RockContext()).GetId(account.Guid) ?? 0; } var accountParticipantsPersonAliasIdsByPurposeKey = AccountParticipantState.GroupBy(a => a.PurposeKey).ToDictionary(k => k.Key, v => v.Select(x => x.PersonAliasId).ToList()); foreach (var purposeKey in accountParticipantsPersonAliasIdsByPurposeKey.Keys) { var accountParticipantsPersonAliasIds = accountParticipantsPersonAliasIdsByPurposeKey.GetValueOrNull(purposeKey); if (accountParticipantsPersonAliasIds?.Any() == true) { var accountParticipants = new PersonAliasService(rockContext).GetByIds(accountParticipantsPersonAliasIds).ToList(); accountService.SetAccountParticipants(accountId, accountParticipants, purposeKey); } } rockContext.SaveChanges(); account.SaveAttributeValues(rockContext); var qryParams = new Dictionary <string, string>(); qryParams["AccountId"] = account.Id.ToString(); qryParams["ExpandedIds"] = PageParameter("ExpandedIds"); NavigateToPage(RockPage.Guid, qryParams); }
/// <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> /// Handles the Click event of the btnSave 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 btnSave_Click( object sender, EventArgs e ) { FinancialAccount account; var rockContext = new RockContext(); var accountService = new Rock.Model.FinancialAccountService( rockContext ); int accountId = hfAccountId.Value.AsInteger(); if ( accountId == 0 ) { account = new Rock.Model.FinancialAccount(); accountService.Add( account ); } else { account = accountService.Get( accountId ); } account.Name = tbName.Text; account.IsActive = cbIsActive.Checked; account.IsPublic = cbIsPublic.Checked; account.Description = tbDescription.Text; account.PublicDescription = cePublicDescription.Text; account.ParentAccountId = apParentAccount.SelectedValueAsInt(); account.AccountTypeValueId = ddlAccountType.SelectedValueAsInt(); account.PublicName = tbPublicName.Text; account.Url = tbUrl.Text; account.CampusId = cpCampus.SelectedValueAsInt(); account.GlCode = tbGLCode.Text; account.StartDate = dtpStartDate.SelectedDate; account.EndDate = dtpEndDate.SelectedDate; account.IsTaxDeductible = cbIsTaxDeductible.Checked; // if the account IsValid is false, and the UI controls didn't report any errors, it is probably because the custom rules of account didn't pass. // So, make sure a message is displayed in the validation summary cvAccount.IsValid = account.IsValid; if ( !cvAccount.IsValid ) { cvAccount.ErrorMessage = account.ValidationResults.Select( a => a.ErrorMessage ).ToList().AsDelimited( "<br />" ); return; } rockContext.SaveChanges(); NavigateToParentPage(); }
/// <summary> /// Gets the giving automation source transaction query. /// This is used by <see cref="Rock.Jobs.GivingAutomation"/>. /// </summary> /// <returns></returns> public IQueryable <FinancialTransaction> GetGivingAutomationSourceTransactionQuery() { var query = Queryable().AsNoTracking(); var settings = GivingAutomationSettings.LoadGivingAutomationSettings(); // Filter by transaction type (defaults to contributions only) var transactionTypeIds = settings.TransactionTypeGuids.Select(DefinedValueCache.Get).Select(dv => dv.Id).ToList(); if (transactionTypeIds.Count() == 1) { var transactionTypeId = transactionTypeIds[0]; query = query.Where(t => t.TransactionTypeValueId == transactionTypeId); } else { query = query.Where(t => transactionTypeIds.Contains(t.TransactionTypeValueId)); } List <int> accountIds; if (settings.FinancialAccountGuids?.Any() == true) { accountIds = new FinancialAccountService(this.Context as RockContext).GetByGuids(settings.FinancialAccountGuids).Select(a => a.Id).ToList(); } else { accountIds = new List <int>(); } // Filter accounts, defaults to tax deductible only if (!accountIds.Any()) { query = query.Where(t => t.TransactionDetails.Any(td => td.Account.IsTaxDeductible)); } else if (settings.AreChildAccountsIncluded == true) { if (accountIds.Count() == 1) { var accountId = accountIds[0]; query = query.Where(t => t.TransactionDetails.Any(td => td.AccountId == accountId || (td.Account.ParentAccountId.HasValue && accountId == td.Account.ParentAccountId.Value))); } else { query = query.Where(t => t.TransactionDetails.Any(td => accountIds.Contains(td.AccountId) || (td.Account.ParentAccountId.HasValue && accountIds.Contains(td.Account.ParentAccountId.Value)))); } } else { if (accountIds.Count() == 1) { var accountId = accountIds[0]; query = query.Where(t => t.TransactionDetails.Any(td => accountId == td.AccountId)); } else { query = query.Where(t => t.TransactionDetails.Any(td => accountIds.Contains(td.AccountId))); } } // We'll need to factor in partial amount refunds... // Exclude transactions that have full refunds. // If it does have a refund, include the transaction if it is just a partial refund query = query.Where(t => // Limit to ones that don't have refunds, or has a partial refund !t.Refunds.Any() || // If it does have refunds, we can exclude the transaction if the refund amount is the same amount (full refund) // Otherwise, we'll have to include the transaction and figure out the partial amount left after the refund. ( ( // total original amount t.TransactionDetails.Sum(xx => xx.Amount) // total amount of any refund(s) for this transaction + t.Refunds.Sum(r => r.FinancialTransaction.TransactionDetails.Sum(d => ( decimal? )d.Amount) ?? 0.00M) ) != 0.00M ) ); // Remove transactions with $0 or negative amounts. If those are refunds, those will factored in above query = query.Where(t => t.TransactionDetails.Any(d => d.Amount > 0M)); return(query); }
/// <summary> /// Gets the giving automation source transaction query. /// This is used by <see cref="Rock.Jobs.GivingAutomation"/>. /// </summary> /// <returns></returns> public IQueryable <FinancialTransaction> GetGivingAutomationSourceTransactionQuery() { var query = Queryable().AsNoTracking(); var settings = GivingAutomationSettings.LoadGivingAutomationSettings(); // Filter by transaction type (defaults to contributions only) var transactionTypeIds = settings.TransactionTypeGuids.Select(DefinedValueCache.Get).Select(dv => dv.Id).ToList(); if (transactionTypeIds.Count() == 1) { var transactionTypeId = transactionTypeIds[0]; query = query.Where(t => t.TransactionTypeValueId == transactionTypeId); } else { query = query.Where(t => transactionTypeIds.Contains(t.TransactionTypeValueId)); } List <int> accountIds; if (settings.FinancialAccountGuids?.Any() == true) { accountIds = new FinancialAccountService(this.Context as RockContext).GetByGuids(settings.FinancialAccountGuids).Select(a => a.Id).ToList(); } else { accountIds = new List <int>(); } // Filter accounts, defaults to tax deductible only if (!accountIds.Any()) { query = query.Where(t => t.TransactionDetails.Any(td => td.Account.IsTaxDeductible)); } else if (settings.AreChildAccountsIncluded == true) { if (accountIds.Count() == 1) { var accountId = accountIds[0]; query = query.Where(t => t.TransactionDetails.Any(td => td.AccountId == accountId || (td.Account.ParentAccountId.HasValue && accountId == td.Account.ParentAccountId.Value))); } else { query = query.Where(t => t.TransactionDetails.Any(td => accountIds.Contains(td.AccountId) || (td.Account.ParentAccountId.HasValue && accountIds.Contains(td.Account.ParentAccountId.Value)))); } } else { if (accountIds.Count() == 1) { var accountId = accountIds[0]; query = query.Where(t => t.TransactionDetails.Any(td => accountId == td.AccountId)); } else { query = query.Where(t => t.TransactionDetails.Any(td => accountIds.Contains(td.AccountId))); } } // Remove transactions that have refunds query = query.Where(t => !t.Refunds.Any()); // Remove transactions with $0 or negative amounts query = query.Where(t => t.TransactionDetails.Any(d => d.Amount > 0M)); return(query); }