Exemplo n.º 1
0
        /// <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();
        }
Exemplo n.º 2
0
        /// <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();
        }
Exemplo n.º 3
0
        /// <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;
        }
Exemplo n.º 4
0
        /// <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();
        }
Exemplo n.º 5
0
        /// <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();
        }
Exemplo n.º 9
0
        /// <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);
        }
Exemplo n.º 10
0
        /// <summary>
        /// Processes the payments.
        /// </summary>
        /// <param name="gateway">The gateway.</param>
        /// <param name="batchNamePrefix">The batch name prefix.</param>
        /// <param name="payments">The payments.</param>
        /// <param name="batchUrlFormat">The batch URL format.</param>
        /// <returns></returns>
        public static string ProcessPayments(GatewayComponent gateway, string batchNamePrefix, List <Payment> payments, string batchUrlFormat = "")
        {
            int totalPayments               = 0;
            int totalAlreadyDownloaded      = 0;
            int totalNoScheduledTransaction = 0;
            int totalAdded = 0;

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

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

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

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

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

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

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

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

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

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

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

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

                            transaction.TransactionDetails.Add(transactionDetail);

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

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

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

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

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

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

            rockContext.SaveChanges();

            StringBuilder sb = new StringBuilder();

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

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

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

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

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

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

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


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

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

            return(sb.ToString());
        }
Exemplo n.º 11
0
        /// <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();
        }
Exemplo n.º 12
0
        /// <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);
        }
Exemplo n.º 13
0
        /// <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);
        }