/// <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 FinancialTransaction ProcessRefund(FinancialTransaction transaction, decimal?amount, int?reasonValueId, string summary, bool process, string batchNameSuffix, out string errorMessage)
{
errorMessage = string.Empty;
// Validate parameters
if (transaction == null)
{
errorMessage = "A valid transaction is required";
return(null);
}
if (transaction.Batch == null)
{
errorMessage = "Transaction must belong to a batch";
return(null);
}
if (!amount.HasValue || amount.Value <= 0.0m)
{
amount = transaction.TotalAmount;
}
if (!amount.HasValue || amount.Value <= 0.0m)
{
errorMessage = string.Format("Amount must be greater than {0}", 0.0m.FormatAsCurrency());
return(null);
}
FinancialTransaction refundTransaction = null;
// If processing the refund through gateway, get the gateway component and process a "Credit" transaction.
if (process)
{
if (transaction.FinancialGateway == null || transaction.TransactionCode.IsNullOrWhiteSpace())
{
errorMessage = "When processing the refund through the Gateway, the transaction must have a valid Gateway and Transaction Code";
return(null);
}
var gatewayComponent = transaction.FinancialGateway.GetGatewayComponent();
if (gatewayComponent == null)
{
errorMessage = "Could not get the Gateway component in order to process the refund";
return(null);
}
refundTransaction = gatewayComponent.Credit(transaction, amount.Value, summary, out errorMessage);
if (refundTransaction == null)
{
return(null);
}
}
else
{
refundTransaction = new FinancialTransaction();
}
refundTransaction.AuthorizedPersonAliasId = transaction.AuthorizedPersonAliasId;
refundTransaction.TransactionDateTime = RockDateTime.Now;
refundTransaction.FinancialGatewayId = transaction.FinancialGatewayId;
refundTransaction.TransactionTypeValueId = transaction.TransactionTypeValueId;
refundTransaction.SourceTypeValueId = transaction.SourceTypeValueId;
if (transaction.FinancialPaymentDetail != null)
{
refundTransaction.FinancialPaymentDetail = new FinancialPaymentDetail();
refundTransaction.FinancialPaymentDetail.AccountNumberMasked = transaction.FinancialPaymentDetail.AccountNumberMasked;
refundTransaction.FinancialPaymentDetail.BillingLocationId = transaction.FinancialPaymentDetail.BillingLocationId;
refundTransaction.FinancialPaymentDetail.CreditCardTypeValueId = transaction.FinancialPaymentDetail.CreditCardTypeValueId;
refundTransaction.FinancialPaymentDetail.CurrencyTypeValueId = transaction.FinancialPaymentDetail.CurrencyTypeValueId;
refundTransaction.FinancialPaymentDetail.ExpirationMonthEncrypted = transaction.FinancialPaymentDetail.ExpirationMonthEncrypted;
refundTransaction.FinancialPaymentDetail.ExpirationYearEncrypted = transaction.FinancialPaymentDetail.ExpirationYearEncrypted;
refundTransaction.FinancialPaymentDetail.NameOnCardEncrypted = transaction.FinancialPaymentDetail.NameOnCardEncrypted;
}
decimal remainingBalance = amount.Value;
foreach (var account in transaction.TransactionDetails.Where(a => a.Amount > 0))
{
var transactionDetail = new FinancialTransactionDetail();
transactionDetail.AccountId = account.AccountId;
transactionDetail.EntityId = account.EntityId;
transactionDetail.EntityTypeId = account.EntityTypeId;
refundTransaction.TransactionDetails.Add(transactionDetail);
if (remainingBalance >= account.Amount)
{
transactionDetail.Amount = 0 - account.Amount;
remainingBalance -= account.Amount;
}
else
{
transactionDetail.Amount = 0 - remainingBalance;
remainingBalance = 0.0m;
}
if (remainingBalance <= 0.0m)
{
break;
}
}
if (remainingBalance > 0 && refundTransaction.TransactionDetails.Any())
{
refundTransaction.TransactionDetails.Last().Amount += remainingBalance;
}
var rockContext = this.Context as Rock.Data.RockContext;
var registrationEntityType = EntityTypeCache.Get(typeof(Rock.Model.Registration));
if (registrationEntityType != null)
{
foreach (var transactionDetail in refundTransaction.TransactionDetails
.Where(d =>
d.EntityTypeId.HasValue &&
d.EntityTypeId.Value == registrationEntityType.Id &&
d.EntityId.HasValue))
{
var registrationChanges = new History.HistoryChangeList();
registrationChanges.AddChange(History.HistoryVerb.Process, History.HistoryChangeType.Record, $"{transactionDetail.Amount.FormatAsCurrency()} Refund");
HistoryService.SaveChanges(
rockContext,
typeof(Registration),
Rock.SystemGuid.Category.HISTORY_EVENT_REGISTRATION.AsGuid(),
transactionDetail.EntityId.Value,
registrationChanges
);
}
}
refundTransaction.RefundDetails = new FinancialTransactionRefund();
refundTransaction.RefundDetails.RefundReasonValueId = reasonValueId;
refundTransaction.RefundDetails.RefundReasonSummary = summary;
refundTransaction.RefundDetails.OriginalTransactionId = transaction.Id;
string batchName = transaction.Batch.Name;
if (batchNameSuffix.IsNotNullOrWhiteSpace() && !batchName.EndsWith(batchNameSuffix))
{
batchName += batchNameSuffix;
}
// Get the batch
var batchService = new FinancialBatchService(rockContext);
TimeSpan timespan = new TimeSpan();
if (transaction.FinancialGateway != null)
{
timespan = transaction.FinancialGateway.GetBatchTimeOffset();
}
var batch = batchService.GetByNameAndDate(batchName, refundTransaction.TransactionDateTime.Value, timespan);
// If this is a new Batch, SaveChanges so that we can get the Batch.Id
if (batch.Id == 0)
{
rockContext.SaveChanges();
}
refundTransaction.BatchId = batch.Id;
Add(refundTransaction);
rockContext.SaveChanges();
batchService.IncrementControlAmount(batch.Id, refundTransaction.TotalAmount, null);
rockContext.SaveChanges();
return(refundTransaction);
}