/// <summary> /// Returns breadcrumbs specific to the block that should be added to navigation /// based on the current page reference. This function is called during the page's /// oninit to load any initial breadcrumbs. /// </summary> /// <param name="pageReference">The <see cref="Rock.Web.PageReference" />.</param> /// <returns> /// A <see cref="System.Collections.Generic.List{BreadCrumb}" /> of block related <see cref="Rock.Web.UI.BreadCrumb">BreadCrumbs</see>. /// </returns> public override List<BreadCrumb> GetBreadCrumbs( PageReference pageReference ) { var breadCrumbs = new List<BreadCrumb>(); int? batchId = PageParameter( pageReference, "batchId" ).AsIntegerOrNull(); if ( batchId != null ) { string batchName = new FinancialBatchService( new RockContext() ) .Queryable().Where( b => b.Id == batchId.Value ) .Select( b => b.Name ) .FirstOrDefault(); if ( !string.IsNullOrWhiteSpace( batchName ) ) { breadCrumbs.Add( new BreadCrumb( batchName, pageReference ) ); } else { breadCrumbs.Add( new BreadCrumb( "New Batch", pageReference ) ); } } else { // don't show a breadcrumb if we don't have a pageparam to work with } return breadCrumbs; }
/// <summary> /// Handles the GridReorder event of the grdFinancialBatch control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="GridReorderEventArgs"/> instance containing the event data.</param> private void rGridBatch_GridReorder(object sender, GridReorderEventArgs e) { var batchService = new Rock.Model.FinancialBatchService(); var queryable = batchService.Queryable(); List <Rock.Model.FinancialBatch> items = queryable.ToList(); batchService.Reorder(items, e.OldIndex, e.NewIndex, CurrentPersonId); BindGrid(); }
/// <summary> /// Handles the GridReorder event of the gBatchList control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="GridReorderEventArgs"/> instance containing the event data.</param> private void gBatchList_GridReorder(object sender, GridReorderEventArgs e) { var rockContext = new RockContext(); var batchService = new Rock.Model.FinancialBatchService(rockContext); var queryable = batchService.Queryable(); List <Rock.Model.FinancialBatch> items = queryable.ToList(); batchService.Reorder(items, e.OldIndex, e.NewIndex); rockContext.SaveChanges(); BindGrid(); }
/// <summary> /// Handles the Delete event of the grdFinancialBatch control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs"/> instance containing the event data.</param> protected void rGridBatch_Delete(object sender, RowEventArgs e) { var FinancialBatchService = new Rock.Model.FinancialBatchService(); Rock.Model.FinancialBatch FinancialBatch = FinancialBatchService.Get((int)rGridBatch.DataKeys[e.RowIndex]["id"]); if (FinancialBatch != null) { FinancialBatchService.Delete(FinancialBatch, CurrentPersonId); FinancialBatchService.Save(FinancialBatch, CurrentPersonId); } BindGrid(); }
/// <summary> /// Refreshes the list. Public method...can be called from other blocks. /// </summary> public void RefreshList() { var contextEntity = this.ContextEntity(); if ( contextEntity != null ) { if ( contextEntity is FinancialBatch ) { var batchId = PageParameter( "financialBatchId" ); var batch = new FinancialBatchService( new RockContext() ).Get( int.Parse( batchId ) ); _batch = batch; BindGrid(); } } }
/// <summary> /// Handles the Click event of the btnSaveFinancialBatch 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 btnSaveFinancialBatch_Click( object sender, EventArgs e ) { using ( new Rock.Data.UnitOfWorkScope() ) { var financialBatchService = new FinancialBatchService(); FinancialBatch financialBatch = null; int financialBatchId = 0; if ( !string.IsNullOrEmpty( hfBatchId.Value ) ) { financialBatchId = int.Parse( hfBatchId.Value ); } if ( financialBatchId == 0 ) { financialBatch = new Rock.Model.FinancialBatch(); financialBatch.CreatedByPersonId = CurrentPersonId.Value; financialBatchService.Add( financialBatch, CurrentPersonId ); } else { financialBatch = financialBatchService.Get( financialBatchId ); } financialBatch.Name = tbName.Text; financialBatch.BatchStartDateTime = dtBatchDate.LowerValue; financialBatch.BatchEndDateTime = dtBatchDate.UpperValue; financialBatch.CampusId = cpCampus.SelectedCampusId; financialBatch.Status = (BatchStatus) ddlStatus.SelectedIndex; decimal fcontrolamt = 0; decimal.TryParse( tbControlAmount.Text, out fcontrolamt ); financialBatch.ControlAmount = fcontrolamt; if ( !financialBatch.IsValid ) { // Controls will render the error messages return; } RockTransactionScope.WrapTransaction( () => { financialBatchService.Save( financialBatch, CurrentPersonId ); hfBatchId.SetValue( financialBatch.Id ); } ); } var savedFinancialBatch = new FinancialBatchService().Get( hfBatchId.ValueAsInt() ); ShowReadOnly( savedFinancialBatch ); }
/// <summary> /// When implemented by a class, enables a server control to process an event raised when a form is posted to the server. /// </summary> /// <param name="eventArgument">A <see cref="T:System.String" /> that represents an optional event argument to be passed to the event handler.</param> public void RaisePostBackEvent( string eventArgument ) { if ( eventArgument == "StatusUpdate" && ddlAction != null && ddlAction.SelectedValue != null && !string.IsNullOrWhiteSpace( ddlAction.SelectedValue ) ) { var batchesSelected = new List<int>(); gBatchList.SelectedKeys.ToList().ForEach( b => batchesSelected.Add( b.ToString().AsInteger() ) ); if ( batchesSelected.Any() ) { var newStatus = ddlAction.SelectedValue == "OPEN" ? BatchStatus.Open : BatchStatus.Closed; var rockContext = new RockContext(); var batchService = new FinancialBatchService( rockContext ); var batchesToUpdate = batchService.Queryable() .Where( b => batchesSelected.Contains( b.Id ) && b.Status != newStatus ) .ToList(); foreach ( var batch in batchesToUpdate ) { batch.Status = newStatus; } rockContext.SaveChanges(); nbResult.Text = string.Format( "{0} batches were {1}.", batchesToUpdate.Count().ToString( "N0" ), newStatus == BatchStatus.Open ? "opened" : "closed" ); nbResult.NotificationBoxType = NotificationBoxType.Success; nbResult.Visible = true; } else { nbResult.Text = string.Format( "There were not any batches selected." ); nbResult.NotificationBoxType = NotificationBoxType.Warning; nbResult.Visible = true; } ddlAction.SelectedIndex = 0; BindGrid(); } }
/// <summary> /// Shows the detail. /// </summary> /// <param name="itemKey">The item key.</param> /// <param name="itemKeyValue">The item key value.</param> public void ShowDetail( string itemKey, int itemKeyValue ) { if ( !itemKey.Equals( "financialBatchId" ) ) { return; } FinancialBatch financialBatch = null; if ( !itemKeyValue.Equals( 0 ) ) { financialBatch = new FinancialBatchService( new RockContext() ).Get( itemKeyValue ); } else { financialBatch = new FinancialBatch { Id = 0 }; } bool readOnly = false; if ( !IsUserAuthorized( Authorization.EDIT ) ) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlyEditActionNotAllowed( FinancialBatch.FriendlyTypeName ); } if ( !readOnly ) { lbEdit.Visible = true; if ( financialBatch.Id > 0 ) { ShowSummary( financialBatch ); } else { ShowEdit( financialBatch ); } } else { lbEdit.Visible = false; ShowSummary( financialBatch ); } lbSave.Visible = !readOnly; }
/// <summary> /// Handles the Delete event of the gBatchList control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs"/> instance containing the event data.</param> protected void gBatchList_Delete( object sender, RowEventArgs e ) { var rockContext = new RockContext(); FinancialBatchService financialBatchService = new FinancialBatchService( rockContext ); FinancialBatch financialBatch = financialBatchService.Get( e.RowKeyId ); if ( financialBatch != null ) { string errorMessage; if ( !financialBatchService.CanDelete( financialBatch, out errorMessage ) ) { mdGridWarning.Show( errorMessage, ModalAlertType.Information ); return; } financialBatchService.Delete( financialBatch ); rockContext.SaveChanges(); } BindGrid(); }
/// <summary> /// Handles the Click event of the btnSaveFinancialBatch 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 btnSaveFinancialBatch_Click(object sender, EventArgs e) { using (new Rock.Data.UnitOfWorkScope()) { var financialBatchService = new Rock.Model.FinancialBatchService(); Rock.Model.FinancialBatch financialBatch = null; int financialBatchId = 0; if (!string.IsNullOrEmpty(hfIdValue.Value)) { financialBatchId = Int32.Parse(hfIdValue.Value); } if (financialBatchId == 0) { financialBatch = new Rock.Model.FinancialBatch(); financialBatch.CreatedByPersonId = CurrentPersonId.Value; financialBatchService.Add(financialBatch, CurrentPersonId); } else { financialBatch = financialBatchService.Get(financialBatchId); } financialBatch.Name = tbName.Text; financialBatch.BatchStartDateTime = dtBatchDate.LowerValue; financialBatch.BatchEndDateTime = dtBatchDate.UpperValue; financialBatch.CampusId = cpCampus.SelectedCampusId; financialBatch.Status = (BatchStatus)ddlStatus.SelectedIndex; decimal fcontrolamt = 0; decimal.TryParse(tbControlAmount.Text, out fcontrolamt); financialBatch.ControlAmount = fcontrolamt; financialBatchService.Save(financialBatch, CurrentPersonId); } BindGrid(); NavigateToParentPage(); }
/// <summary> /// Handles the Delete event of the gBatchList control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs"/> instance containing the event data.</param> protected void gBatchList_Delete( object sender, RowEventArgs e ) { var rockContext = new RockContext(); var batchService = new FinancialBatchService( rockContext ); var batch = batchService.Get( e.RowKeyId ); if ( batch != null ) { if ( UserCanEdit || batch.IsAuthorized( Rock.Security.Authorization.EDIT, CurrentPerson ) ) { string errorMessage; if ( !batchService.CanDelete( batch, out errorMessage ) ) { mdGridWarning.Show( errorMessage, ModalAlertType.Information ); return; } batchService.Delete( batch ); rockContext.SaveChanges(); } } BindGrid(); }
/// <summary> /// Handles adding families from the given XML element snippet /// </summary> /// <param name="elemFamilies">The xml element containing all the families.</param> /// <param name="rockContext">The rock context.</param> private void AddFamilies( XElement elemFamilies, RockContext rockContext ) { if ( elemFamilies == null ) { return; } // Persist the storage type's settings specific to the photo binary file type var settings = new Dictionary<string, string>(); if ( _personImageBinaryFileType.Attributes == null ) { _personImageBinaryFileType.LoadAttributes(); } foreach ( var attributeValue in _personImageBinaryFileType.AttributeValues ) { settings.Add( attributeValue.Key, attributeValue.Value.Value ); } _personImageBinaryFileTypeSettings = settings.ToJson(); bool fabricateAttendance = GetAttributeValue( "FabricateAttendance" ).AsBoolean(); GroupService groupService = new GroupService( rockContext ); var allFamilies = rockContext.Groups; List<Group> allGroups = new List<Group>(); var attendanceData = new Dictionary<Guid, List<Attendance>>(); // Next create the family along with its members and related data foreach ( var elemFamily in elemFamilies.Elements( "family" ) ) { Guid guid = elemFamily.Attribute( "guid" ).Value.Trim().AsGuid(); var familyMembers = BuildFamilyMembersFromXml( elemFamily.Element( "members" ), rockContext ); // Call replica of groupService's SaveNewFamily method in an attempt to speed things up Group family = CreateNewFamily( familyMembers, campusId: 1 ); family.Guid = guid; // add the family to the context's list of groups allFamilies.Add( family ); // add the families address(es) AddFamilyAddresses( groupService, family, elemFamily.Element( "addresses" ), rockContext ); // add their attendance data if ( fabricateAttendance ) { AddFamilyAttendance( family, elemFamily, rockContext, attendanceData ); } allGroups.Add( family ); _stopwatch.Stop(); AppendFormat( "{0:00}:{1:00}.{2:00} added {3}<br/>", _stopwatch.Elapsed.Minutes, _stopwatch.Elapsed.Seconds, _stopwatch.Elapsed.Milliseconds / 10, family.Name ); _stopwatch.Start(); } rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); // Now save each person's attributevalues (who had them defined in the XML) // and add each person's ID to a dictionary for use later. _stopwatch.Stop(); AppendFormat( "{0:00}:{1:00}.{2:00} saving attributes for everyone...<br/>", _stopwatch.Elapsed.Minutes, _stopwatch.Elapsed.Seconds, _stopwatch.Elapsed.Milliseconds / 10 ); _stopwatch.Start(); AttributeValueService attributeValueService = new AttributeValueService( rockContext ); foreach ( var gm in allGroups.SelectMany( g => g.Members ) ) { // Put the person's id into the people dictionary for later use. if ( !_peopleDictionary.ContainsKey( gm.Person.Guid ) ) { _peopleDictionary.Add( gm.Person.Guid, gm.Person.Id ); } // Only save if the person had attributes, otherwise it will error. if ( _personWithAttributes.ContainsKey( gm.Person.Guid ) ) { foreach ( var attributeCache in gm.Person.Attributes.Select( a => a.Value ) ) { var newValue = gm.Person.AttributeValues[attributeCache.Key]; if ( newValue != null ) { var attributeValue = new AttributeValue(); attributeValue.AttributeId = newValue.AttributeId; attributeValue.EntityId = gm.Person.Id; attributeValue.Value = newValue.Value; rockContext.AttributeValues.Add( attributeValue ); } } } } rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); _stopwatch.Stop(); AppendFormat( "{0:00}:{1:00}.{2:00} attributes saved<br/>", _stopwatch.Elapsed.Minutes, _stopwatch.Elapsed.Seconds, _stopwatch.Elapsed.Milliseconds / 10 ); _stopwatch.Start(); // Create person alias records for each person manually since we set disablePrePostProcessing=true on save PersonService personService = new PersonService( rockContext ); foreach ( var person in personService.Queryable( "Aliases", true ) .Where( p => _peopleDictionary.Keys.Contains( p.Guid ) && !p.Aliases.Any() ) ) { person.Aliases.Add( new PersonAlias { AliasPersonId = person.Id, AliasPersonGuid = person.Guid } ); } rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); _stopwatch.Stop(); AppendFormat( "{0:00}:{1:00}.{2:00} added person aliases<br/>", _stopwatch.Elapsed.Minutes, _stopwatch.Elapsed.Seconds, _stopwatch.Elapsed.Milliseconds / 10 ); _stopwatch.Start(); // Put the person alias ids into the people alias dictionary for later use. PersonAliasService personAliasService = new PersonAliasService( rockContext ); foreach ( var personAlias in personAliasService.Queryable( "Person" ) .Where( a => _peopleDictionary.Keys.Contains( a.Person.Guid ) && a.PersonId == a.AliasPersonId ) ) { _peopleAliasDictionary.Add( personAlias.Person.Guid, personAlias.Id ); } // Now that person aliases have been saved, save the attendance records var attendanceService = new AttendanceService( rockContext ); var attendanceGuids = attendanceData.Select( a => a.Key ).ToList(); foreach ( var aliasKeyValue in _peopleAliasDictionary .Where( a => attendanceGuids.Contains( a.Key )) ) { foreach ( var attendance in attendanceData[aliasKeyValue.Key] ) { attendance.PersonAliasId = aliasKeyValue.Value; attendanceService.Add( attendance ); } } rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); _stopwatch.Stop(); AppendFormat( "{0:00}:{1:00}.{2:00} added attendance records<br/>", _stopwatch.Elapsed.Minutes, _stopwatch.Elapsed.Seconds, _stopwatch.Elapsed.Milliseconds / 10 ); _stopwatch.Start(); // Now re-process the family section looking for any giving data. // We do this last because we need the personAliases that were just added. // Persist the storage type's settings specific to the contribution binary file type settings = new Dictionary<string, string>(); if ( _checkImageBinaryFileType.Attributes == null ) { _checkImageBinaryFileType.LoadAttributes(); } foreach ( var attributeValue in _checkImageBinaryFileType.AttributeValues ) { settings.Add( attributeValue.Key, attributeValue.Value.Value ); } _checkImageBinaryFileTypeSettings = settings.ToJson(); foreach ( var elemFamily in elemFamilies.Elements( "family" ) ) { // add the families giving data if ( GetAttributeValue( "EnableGiving" ).AsBoolean() ) { // Support multiple giving elements per family foreach ( var elementGiving in elemFamily.Elements( "giving" ) ) { AddFamilyGiving( elementGiving, elemFamily.Attribute( "name" ).Value, rockContext ); } } } if ( GetAttributeValue( "EnableGiving" ).AsBoolean() ) { // Now add the batches to the service to be persisted var financialBatchService = new FinancialBatchService( rockContext ); foreach ( var financialBatch in _contributionBatches ) { financialBatchService.Add( financialBatch.Value ); } } rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); }
/// <summary> /// Binds the grid. /// </summary> private void BindGrid() { var batchService = new FinancialBatchService(); var batches = batchService.Queryable(); SortProperty sortProperty = rGridBatch.SortProperty; if ( dtBatchDate.SelectedDate.HasValue ) { batches = batches.Where( batch => batch.BatchStartDateTime >= dtBatchDate.SelectedDate ); } if ( (ddlStatus.SelectedValueAsInt() ?? 0) > 0 ) { var batchStatus = ddlStatus.SelectedValueAsEnum<BatchStatus>(); batches = batches.Where( Batch => Batch.Status == batchStatus ); } if ( !string.IsNullOrEmpty( txtTitle.Text ) ) { batches = batches.Where( Batch => Batch.Name == txtTitle.Text ); } if ( ddlCampus.SelectedCampusId.HasValue ) { batches = batches.Where( Batch => Batch.CampusId == ddlCampus.SelectedCampusId.Value ); } if ( sortProperty != null ) { rGridBatch.DataSource = batches.Sort( sortProperty ).ToList(); } else { rGridBatch.DataSource = batches.OrderBy( b => b.Name ).ToList(); } rGridBatch.DataBind(); }
private void SaveTransaction( FinancialGateway financialGateway, GatewayComponent gateway, Person person, PaymentInfo paymentInfo, FinancialTransaction transaction, RockContext rockContext ) { var txnChanges = new List<string>(); txnChanges.Add( "Created Transaction" ); History.EvaluateChange( txnChanges, "Transaction Code", string.Empty, transaction.TransactionCode ); transaction.AuthorizedPersonAliasId = person.PrimaryAliasId; History.EvaluateChange( txnChanges, "Person", string.Empty, person.FullName ); transaction.TransactionDateTime = RockDateTime.Now; History.EvaluateChange( txnChanges, "Date/Time", null, transaction.TransactionDateTime ); transaction.FinancialGatewayId = financialGateway.Id; History.EvaluateChange( txnChanges, "Gateway", string.Empty, financialGateway.Name ); var txnType = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION ) ); transaction.TransactionTypeValueId = txnType.Id; History.EvaluateChange( txnChanges, "Type", string.Empty, txnType.Value ); transaction.Summary = paymentInfo.Comment1; History.EvaluateChange( txnChanges, "Summary", string.Empty, transaction.Summary ); if ( transaction.FinancialPaymentDetail == null ) { transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } transaction.FinancialPaymentDetail.SetFromPaymentInfo( paymentInfo, gateway, rockContext, txnChanges ); Guid sourceGuid = Guid.Empty; if ( Guid.TryParse( GetAttributeValue( "Source" ), out sourceGuid ) ) { var source = DefinedValueCache.Read( sourceGuid ); if ( source != null ) { transaction.SourceTypeValueId = source.Id; History.EvaluateChange( txnChanges, "Source", string.Empty, source.Value ); } } foreach ( var account in SelectedAccounts.Where( a => a.Amount > 0 ) ) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.Amount = account.Amount; transactionDetail.AccountId = account.Id; transaction.TransactionDetails.Add( transactionDetail ); History.EvaluateChange( txnChanges, account.Name, 0.0M.FormatAsCurrency(), transactionDetail.Amount.FormatAsCurrency() ); } var batchService = new FinancialBatchService( rockContext ); // Get the batch var batch = batchService.Get( GetAttributeValue( "BatchNamePrefix" ), paymentInfo.CurrencyTypeValue, paymentInfo.CreditCardTypeValue, transaction.TransactionDateTime.Value, financialGateway.GetBatchTimeOffset() ); var batchChanges = new List<string>(); if ( batch.Id == 0 ) { batchChanges.Add( "Generated the batch" ); History.EvaluateChange( batchChanges, "Batch Name", string.Empty, batch.Name ); History.EvaluateChange( batchChanges, "Status", null, batch.Status ); History.EvaluateChange( batchChanges, "Start Date/Time", null, batch.BatchStartDateTime ); History.EvaluateChange( batchChanges, "End Date/Time", null, batch.BatchEndDateTime ); } decimal newControlAmount = batch.ControlAmount + transaction.TotalAmount; History.EvaluateChange( batchChanges, "Control Amount", batch.ControlAmount.FormatAsCurrency(), newControlAmount.FormatAsCurrency() ); batch.ControlAmount = newControlAmount; transaction.BatchId = batch.Id; batch.Transactions.Add( transaction ); rockContext.SaveChanges(); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, batchChanges ); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), batch.Id, txnChanges, person.FullName, typeof( FinancialTransaction ), transaction.Id ); SendReceipt( transaction.Id ); TransactionCode = transaction.TransactionCode; }
/// <summary> /// Handles the Click event of the lbSave control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void lbSave_Click( object sender, EventArgs e ) { var rockContext = new RockContext(); var batchService = new FinancialBatchService( rockContext ); FinancialBatch batch = null; var changes = new List<string>(); int batchId = hfBatchId.Value.AsInteger(); if ( batchId == 0 ) { batch = new FinancialBatch(); batchService.Add( batch ); changes.Add( "Created the batch" ); } else { batch = batchService.Get( batchId ); } if ( batch != null ) { History.EvaluateChange( changes, "Batch Name", batch.Name, tbName.Text ); batch.Name = tbName.Text; BatchStatus batchStatus = (BatchStatus)ddlStatus.SelectedIndex; History.EvaluateChange( changes, "Status", batch.Status, batchStatus ); batch.Status = batchStatus; CampusCache oldCampus = null; if ( batch.CampusId.HasValue ) { oldCampus = CampusCache.Read( batch.CampusId.Value ); } CampusCache newCampus = null; if ( campCampus.SelectedCampusId.HasValue ) { newCampus = CampusCache.Read( campCampus.SelectedCampusId.Value ); } History.EvaluateChange( changes, "Campus", oldCampus != null ? oldCampus.Name : "None", newCampus != null ? newCampus.Name : "None" ); batch.CampusId = campCampus.SelectedCampusId; DateTime? startDateTime = dtpStart.SelectedDateTimeIsBlank ? null : dtpStart.SelectedDateTime; History.EvaluateChange( changes, "Start Date/Time", batch.BatchStartDateTime, startDateTime ); batch.BatchStartDateTime = startDateTime; DateTime? endDateTime; if ( dtpEnd.SelectedDateTimeIsBlank && batch.BatchStartDateTime.HasValue ) { endDateTime = batch.BatchStartDateTime.Value.AddDays( 1 ); } else { endDateTime = dtpEnd.SelectedDateTimeIsBlank ? null : dtpEnd.SelectedDateTime; } History.EvaluateChange( changes, "End Date/Time", batch.BatchEndDateTime, endDateTime ); batch.BatchEndDateTime = endDateTime; decimal controlAmount = tbControlAmount.Text.AsDecimal(); History.EvaluateChange( changes, "Control Amount", batch.ControlAmount.FormatAsCurrency(), controlAmount.FormatAsCurrency() ); batch.ControlAmount = controlAmount; History.EvaluateChange( changes, "Accounting System Code", batch.AccountingSystemCode, tbAccountingCode.Text ); batch.AccountingSystemCode = tbAccountingCode.Text; History.EvaluateChange( changes, "Notes", batch.Note, tbNote.Text ); batch.Note = tbNote.Text; cvBatch.IsValid = batch.IsValid; if ( !Page.IsValid || !batch.IsValid ) { cvBatch.ErrorMessage = batch.ValidationResults.Select( a => a.ErrorMessage ).ToList().AsDelimited( "<br />" ); return; } rockContext.WrapTransaction( () => { if ( rockContext.SaveChanges() > 0 ) { if ( changes.Any() ) { HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, changes ); } } } ); if ( batchId == 0 ) { // If created a new batch, navigate to same page so that transaction list displays correctly var pageReference = CurrentPageReference; pageReference.Parameters.AddOrReplace( "batchId", batch.Id.ToString() ); NavigateToPage( pageReference ); } else { hfBatchId.SetValue( batch.Id ); // Requery the batch to support EF navigation properties var savedBatch = GetBatch( batch.Id ); ShowReadonlyDetails( savedBatch ); // If there is a batch context item, update the context's properties with new values var contextObjects = new Dictionary<string, object>(); foreach ( var contextEntityType in RockPage.GetContextEntityTypes() ) { var contextEntity = RockPage.GetCurrentContext( contextEntityType ); if ( contextEntity is FinancialBatch ) { var contextBatch = contextEntity as FinancialBatch; contextBatch.CopyPropertiesFrom( batch ); } } // Then refresh transaction list RockPage.UpdateBlocks( "~/Blocks/Finance/TransactionList.ascx" ); } } }
/// <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> /// Gets the batch. /// </summary> /// <param name="batchId">The batch identifier.</param> /// <returns></returns> private FinancialBatch GetBatch( int batchId, RockContext rockContext = null ) { rockContext = rockContext ?? new RockContext(); var batch = new FinancialBatchService( rockContext ) .Queryable( "Campus,Transactions.TransactionDetails.Account" ) .Where( b => b.Id == batchId ) .FirstOrDefault(); return batch; }
/// <summary> /// Handles the GridReorder event of the gBatchList control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="GridReorderEventArgs"/> instance containing the event data.</param> private void gBatchList_GridReorder( object sender, GridReorderEventArgs e ) { var rockContext = new RockContext(); var batchService = new Rock.Model.FinancialBatchService( rockContext ); var queryable = batchService.Queryable(); List<Rock.Model.FinancialBatch> items = queryable.ToList(); batchService.Reorder( items, e.OldIndex, e.NewIndex ); rockContext.SaveChanges(); BindGrid(); }
/// <summary> /// When implemented by a class, enables a server control to process an event raised when a form is posted to the server. /// </summary> /// <param name="eventArgument">A <see cref="T:System.String" /> that represents an optional event argument to be passed to the event handler.</param> public void RaisePostBackEvent( string eventArgument ) { if ( eventArgument == "StatusUpdate" && ddlAction != null && ddlAction.SelectedValue != null && !string.IsNullOrWhiteSpace( ddlAction.SelectedValue ) ) { var batchesSelected = new List<int>(); gBatchList.SelectedKeys.ToList().ForEach( b => batchesSelected.Add( b.ToString().AsInteger() ) ); if ( batchesSelected.Any() ) { var newStatus = ddlAction.SelectedValue == "OPEN" ? BatchStatus.Open : BatchStatus.Closed; var rockContext = new RockContext(); var batchService = new FinancialBatchService( rockContext ); var batchesToUpdate = batchService.Queryable() .Where( b => batchesSelected.Contains( b.Id ) && b.Status != newStatus ) .ToList(); foreach ( var batch in batchesToUpdate ) { var changes = new List<string>(); History.EvaluateChange( changes, "Status", batch.Status, newStatus ); batch.Status = newStatus; if ( !batch.IsValid ) { string message = string.Format( "Unable to update status for the selected batches.<br/><br/>{0}", batch.ValidationResults.AsDelimited( "<br/>" ) ); maWarningDialog.Show( message, ModalAlertType.Warning ); return; } HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, changes, false ); } rockContext.SaveChanges(); nbResult.Text = string.Format( "{0} batches were {1}.", batchesToUpdate.Count().ToString( "N0" ), newStatus == BatchStatus.Open ? "opened" : "closed" ); nbResult.NotificationBoxType = NotificationBoxType.Success; nbResult.Visible = true; } else { nbResult.Text = string.Format( "There were not any batches selected." ); nbResult.NotificationBoxType = NotificationBoxType.Warning; nbResult.Visible = true; } ddlAction.SelectedIndex = 0; BindGrid(); } }
// // Swipe Panel Events // private void ProcessSwipe( string swipeData ) { try { using ( var rockContext = new RockContext() ) { // create swipe object SwipePaymentInfo swipeInfo = new SwipePaymentInfo( swipeData ); swipeInfo.Amount = this.Amounts.Sum( a => a.Value ); // if not anonymous then add contact info to the gateway transaction if ( this.AnonymousGiverPersonAliasId != this.SelectedGivingUnit.PersonAliasId ) { var giver = new PersonAliasService( rockContext ).Queryable( "Person, Person.PhoneNumbers" ).Where( p => p.Id == this.SelectedGivingUnit.PersonAliasId ).FirstOrDefault(); swipeInfo.FirstName = giver.Person.NickName; swipeInfo.LastName = giver.Person.LastName; if ( giver.Person.PhoneNumbers != null ) { Guid homePhoneValueGuid = new Guid( Rock.SystemGuid.DefinedValue.PERSON_PHONE_TYPE_HOME ); var homephone = giver.Person.PhoneNumbers.Where( p => p.NumberTypeValue.Guid == homePhoneValueGuid ).FirstOrDefault(); if ( homephone != null ) { swipeInfo.Phone = homephone.NumberFormatted; } } var homeLocation = giver.Person.GetHomeLocation(); if ( homeLocation != null ) { swipeInfo.Street1 = homeLocation.Street1; if ( !string.IsNullOrWhiteSpace( homeLocation.Street2 ) ) { swipeInfo.Street2 = homeLocation.Street2; } swipeInfo.City = homeLocation.City; swipeInfo.State = homeLocation.State; swipeInfo.PostalCode = homeLocation.PostalCode; } } // add comment to the transation swipeInfo.Comment1 = GetAttributeValue( "PaymentComment" ); // get gateway FinancialGateway financialGateway = null; GatewayComponent gateway = null; Guid? gatewayGuid = GetAttributeValue( "CreditCardGateway" ).AsGuidOrNull(); if ( gatewayGuid.HasValue ) { financialGateway = new FinancialGatewayService( rockContext ).Get( gatewayGuid.Value ); if ( financialGateway != null ) { financialGateway.LoadAttributes( rockContext ); } gateway = financialGateway.GetGatewayComponent(); } if ( gateway != null ) { string errorMessage = string.Empty; var transaction = gateway.Charge( financialGateway, swipeInfo, out errorMessage ); if ( transaction != null ) { var txnChanges = new List<string>(); txnChanges.Add( "Created Transaction (from kiosk)" ); _transactionCode = transaction.TransactionCode; History.EvaluateChange( txnChanges, "Transaction Code", string.Empty, transaction.TransactionCode ); var personName = new PersonAliasService( rockContext ) .Queryable().AsNoTracking() .Where( a => a.Id == this.SelectedGivingUnit.PersonAliasId ) .Select( a => a.Person.NickName + " " + a.Person.LastName ) .FirstOrDefault(); transaction.AuthorizedPersonAliasId = this.SelectedGivingUnit.PersonAliasId; History.EvaluateChange( txnChanges, "Person", string.Empty, personName ); transaction.TransactionDateTime = RockDateTime.Now; History.EvaluateChange( txnChanges, "Date/Time", null, transaction.TransactionDateTime ); transaction.FinancialGatewayId = financialGateway.Id; History.EvaluateChange( txnChanges, "Gateway", string.Empty, financialGateway.Name ); var txnType = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION ) ); transaction.TransactionTypeValueId = txnType.Id; History.EvaluateChange( txnChanges, "Type", string.Empty, txnType.Value ); transaction.Summary = swipeInfo.Comment1; History.EvaluateChange( txnChanges, "Transaction Code", string.Empty, transaction.Summary ); if ( transaction.FinancialPaymentDetail == null ) { transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } transaction.FinancialPaymentDetail.SetFromPaymentInfo( swipeInfo, gateway, rockContext, txnChanges ); Guid sourceGuid = Guid.Empty; if ( Guid.TryParse( GetAttributeValue( "Source" ), out sourceGuid ) ) { var source = DefinedValueCache.Read( sourceGuid ); if ( source != null ) { transaction.SourceTypeValueId = source.Id; History.EvaluateChange( txnChanges, "Source", string.Empty, source.Value ); } } foreach ( var accountAmount in this.Amounts.Where( a => a.Value > 0 ) ) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.Amount = accountAmount.Value; transactionDetail.AccountId = accountAmount.Key; transaction.TransactionDetails.Add( transactionDetail ); var account = new FinancialAccountService( rockContext ).Get( accountAmount.Key ); if ( account != null ) { History.EvaluateChange( txnChanges, account.Name, 0.0M.FormatAsCurrency(), transactionDetail.Amount.FormatAsCurrency() ); } } var batchService = new FinancialBatchService( rockContext ); // Get the batch var batch = batchService.Get( GetAttributeValue( "BatchNamePrefix" ), swipeInfo.CurrencyTypeValue, swipeInfo.CreditCardTypeValue, transaction.TransactionDateTime.Value, financialGateway.GetBatchTimeOffset() ); var batchChanges = new List<string>(); if ( batch.Id == 0 ) { batchChanges.Add( "Generated the batch" ); History.EvaluateChange( batchChanges, "Batch Name", string.Empty, batch.Name ); History.EvaluateChange( batchChanges, "Status", null, batch.Status ); History.EvaluateChange( batchChanges, "Start Date/Time", null, batch.BatchStartDateTime ); History.EvaluateChange( batchChanges, "End Date/Time", null, batch.BatchEndDateTime ); } decimal newControlAmount = batch.ControlAmount + transaction.TotalAmount; History.EvaluateChange( batchChanges, "Control Amount", batch.ControlAmount.FormatAsCurrency(), newControlAmount.FormatAsCurrency() ); batch.ControlAmount = newControlAmount; transaction.BatchId = batch.Id; batch.Transactions.Add( transaction ); rockContext.WrapTransaction( () => { rockContext.SaveChanges(); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, batchChanges ); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), batch.Id, txnChanges, personName, typeof( FinancialTransaction ), transaction.Id ); } ); // send receipt in one is configured and not giving anonymously if ( !string.IsNullOrWhiteSpace( GetAttributeValue( "ReceiptEmail" ) ) && ( this.AnonymousGiverPersonAliasId != this.SelectedGivingUnit.PersonAliasId ) ) { _receiptSent = true; SendReceipt(); } HidePanels(); ShowReceiptPanel(); } else { lSwipeErrors.Text = String.Format( "<div class='alert alert-danger'>An error occurred while process this transaction. Message: {0}</div>", errorMessage ); } } else { lSwipeErrors.Text = "<div class='alert alert-danger'>Invalid gateway provided. Please provide a gateway. Transaction not processed.</div>"; } } } catch ( Exception ex ) { lSwipeErrors.Text = String.Format( "<div class='alert alert-danger'>An error occurred while process this transaction. Message: {0}</div>", ex.Message ); } }
/// <summary> /// Binds the grid. /// </summary> private void BindGrid() { var batchService = new FinancialBatchService( new RockContext() ); var batches = batchService.Queryable(); if ( dpBatchDate.SelectedDate.HasValue ) { batches = batches.Where( batch => batch.BatchStartDateTime >= dpBatchDate.SelectedDate ); } string status = gfBatchFilter.GetUserPreference( "Status" ); if ( !string.IsNullOrWhiteSpace( status ) ) { var batchStatus = (BatchStatus)Enum.Parse( typeof( BatchStatus ), status ); batches = batches.Where( batch => batch.Status == batchStatus ); } if ( !string.IsNullOrEmpty( tbTitle.Text ) ) { batches = batches.Where( batch => batch.Name == tbTitle.Text ); } if ( campCampus.SelectedCampusId.HasValue ) { batches = batches.Where( batch => batch.CampusId == campCampus.SelectedCampusId.Value ); } SortProperty sortProperty = gBatchList.SortProperty; if ( sortProperty != null ) { gBatchList.DataSource = batches.Sort( sortProperty ).ToList(); } else { gBatchList.DataSource = batches.OrderBy( batch => batch.Name ).ToList(); } gBatchList.DataBind(); }
/// <summary> /// Processes the payments. /// </summary> /// <param name="gateway">The gateway.</param> /// <param name="batchNamePrefix">The batch name prefix.</param> /// <param name="payments">The payments.</param> /// <param name="batchUrlFormat">The batch URL format.</param> /// <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> /// Gets the batch. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="financialGateway">The financial gateway.</param> /// <param name="suffix">The suffix.</param> /// <returns></returns> private FinancialBatch GetBatch( RockContext rockContext, FinancialGateway financialGateway, string suffix ) { if ( suffix == null ) { suffix = string.Empty; } if ( suffix.Length > 1 ) { suffix = char.ToUpper( suffix[0] ) + suffix.Substring( 1 ); } var batchService = new FinancialBatchService( rockContext ); var batchName = string.Format( "{0}: {1}", BATCH_PREFIX, suffix ); return batchService.GetByNameAndDate( batchName, RockDateTime.Now, financialGateway.GetBatchTimeOffset() ); }
/// <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); }
private List<BatchRow> GetData() { var batchService = new FinancialBatchService( new RockContext() ); var qry = batchService.Queryable() .Where( b => b.BatchStartDateTime.HasValue ); // filter by date string dateRangeValue = gfBatchFilter.GetUserPreference( "Date Range" ); if ( !string.IsNullOrWhiteSpace( dateRangeValue ) ) { var drp = new DateRangePicker(); drp.DelimitedValues = dateRangeValue; if ( drp.LowerValue.HasValue ) { qry = qry.Where( b => b.BatchStartDateTime >= drp.LowerValue ); } if ( drp.UpperValue.HasValue ) { var endOfDay = drp.UpperValue.Value.AddDays( 1 ); qry = qry.Where( b => b.BatchStartDateTime < drp.UpperValue ); } } // filter by status var status = gfBatchFilter.GetUserPreference( "Status" ).ConvertToEnumOrNull<BatchStatus>(); if ( status.HasValue ) { qry = qry.Where( b => b.Status == status ); } // filter by title string title = gfBatchFilter.GetUserPreference( "Title" ); if ( !string.IsNullOrEmpty( title ) ) { qry = qry.Where( batch => batch.Name.StartsWith( title ) ); } // filter by accounting code if ( tbAccountingCode.Visible ) { string accountingCode = gfBatchFilter.GetUserPreference( "Accounting Code" ); if ( !string.IsNullOrEmpty( accountingCode ) ) { qry = qry.Where( batch => batch.AccountingSystemCode.StartsWith( accountingCode ) ); } } // filter by campus var campus = CampusCache.Read( gfBatchFilter.GetUserPreference( "Campus" ).AsInteger() ); if ( campus != null ) { qry = qry.Where( b => b.CampusId == campus.Id ); } IOrderedQueryable<FinancialBatch> sortedQry = null; SortProperty sortProperty = gBatchList.SortProperty; if ( sortProperty != null ) { switch ( sortProperty.Property ) { case "TransactionCount": { if ( sortProperty.Direction == SortDirection.Ascending ) { sortedQry = qry.OrderBy( b => b.Transactions.Count() ); } else { sortedQry = qry.OrderByDescending( b => b.Transactions.Count() ); } break; } case "TransactionAmount": { if ( sortProperty.Direction == SortDirection.Ascending ) { sortedQry = qry.OrderBy( b => b.Transactions.Sum( t => (decimal?)( t.TransactionDetails.Sum( d => (decimal?)d.Amount ) ?? 0.0M ) ) ?? 0.0M ); } else { sortedQry = qry.OrderByDescending( b => b.Transactions.Sum( t => (decimal?)( t.TransactionDetails.Sum( d => (decimal?)d.Amount ) ?? 0.0M ) ) ?? 0.0M ); } break; } default: { sortedQry = qry.Sort( sortProperty ); break; } } } else { sortedQry = qry .OrderByDescending( b => b.BatchStartDateTime ) .ThenBy( b => b.Name ); } return sortedQry .Select( b => new BatchRow { Id = b.Id, BatchStartDateTime = b.BatchStartDateTime.Value, Name = b.Name, AccountingSystemCode = b.AccountingSystemCode, TransactionCount = b.Transactions.Count(), TransactionAmount = b.Transactions.Sum( t => (decimal?)( t.TransactionDetails.Sum( d => (decimal?)d.Amount ) ?? 0.0M ) ) ?? 0.0M, ControlAmount = b.ControlAmount, CampusName = b.Campus != null ? b.Campus.Name : "", Status = b.Status, UnMatchedTxns = b.Transactions.Any( t => !t.AuthorizedPersonAliasId.HasValue ) } ) .ToList(); }
/// <summary> /// Handles the Delete event of the gBatchList control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs"/> instance containing the event data.</param> protected void gBatchList_Delete( object sender, RowEventArgs e ) { var rockContext = new RockContext(); var batchService = new FinancialBatchService( rockContext ); var batch = batchService.Get( e.RowKeyId ); if ( batch != null ) { if ( UserCanEdit || batch.IsAuthorized( Rock.Security.Authorization.EDIT, CurrentPerson ) ) { string errorMessage; if ( !batchService.CanDelete( batch, out errorMessage ) ) { mdGridWarning.Show( errorMessage, ModalAlertType.Information ); return; } rockContext.WrapTransaction( () => { HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, new List<string> { "Deleted the batch" } ); batchService.Delete( batch ); rockContext.SaveChanges(); } ); } } BindGrid(); }
private bool SaveTransaction( GatewayComponent gateway, Registration registration, FinancialTransaction transaction, PaymentInfo paymentInfo, RockContext rockContext ) { if ( transaction != null ) { var txnChanges = new List<string>(); txnChanges.Add( "Created Transaction" ); History.EvaluateChange( txnChanges, "Transaction Code", string.Empty, transaction.TransactionCode ); transaction.AuthorizedPersonAliasId = registration.PersonAliasId; transaction.TransactionDateTime = RockDateTime.Now; History.EvaluateChange( txnChanges, "Date/Time", null, transaction.TransactionDateTime ); transaction.FinancialGatewayId = RegistrationTemplate.FinancialGatewayId; History.EvaluateChange( txnChanges, "Gateway", string.Empty, RegistrationTemplate.FinancialGateway.Name ); var txnType = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_EVENT_REGISTRATION ) ); transaction.TransactionTypeValueId = txnType.Id; History.EvaluateChange( txnChanges, "Type", string.Empty, txnType.Value ); if ( transaction.FinancialPaymentDetail == null ) { transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } DefinedValueCache currencyType = null; DefinedValueCache creditCardType = null; if ( paymentInfo != null ) { transaction.FinancialPaymentDetail.SetFromPaymentInfo( paymentInfo, gateway, rockContext, txnChanges ); currencyType = paymentInfo.CurrencyTypeValue; creditCardType = paymentInfo.CreditCardTypeValue; } Guid sourceGuid = Guid.Empty; if ( Guid.TryParse( GetAttributeValue( "Source" ), out sourceGuid ) ) { var source = DefinedValueCache.Read( sourceGuid ); if ( source != null ) { transaction.SourceTypeValueId = source.Id; History.EvaluateChange( txnChanges, "Source", string.Empty, source.Value ); } } transaction.Summary = registration.GetSummary( RegistrationInstanceState ); var transactionDetail = new FinancialTransactionDetail(); transactionDetail.Amount = RegistrationState.PaymentAmount ?? 0.0m; transactionDetail.AccountId = RegistrationInstanceState.AccountId.Value; transactionDetail.EntityTypeId = EntityTypeCache.Read( typeof( Rock.Model.Registration ) ).Id; transactionDetail.EntityId = registration.Id; transaction.TransactionDetails.Add( transactionDetail ); History.EvaluateChange( txnChanges, RegistrationInstanceState.Account.Name, 0.0M.FormatAsCurrency(), transactionDetail.Amount.FormatAsCurrency() ); var batchChanges = new List<string>(); rockContext.WrapTransaction( () => { var batchService = new FinancialBatchService( rockContext ); // determine batch prefix string batchPrefix = string.Empty; if ( !string.IsNullOrWhiteSpace( RegistrationTemplate.BatchNamePrefix ) ) { batchPrefix = RegistrationTemplate.BatchNamePrefix; } else { batchPrefix = GetAttributeValue( "BatchNamePrefix" ); } // Get the batch var batch = batchService.Get( batchPrefix, currencyType, creditCardType, transaction.TransactionDateTime.Value, RegistrationTemplate.FinancialGateway.GetBatchTimeOffset() ); if ( batch.Id == 0 ) { batchChanges.Add( "Generated the batch" ); History.EvaluateChange( batchChanges, "Batch Name", string.Empty, batch.Name ); History.EvaluateChange( batchChanges, "Status", null, batch.Status ); History.EvaluateChange( batchChanges, "Start Date/Time", null, batch.BatchStartDateTime ); History.EvaluateChange( batchChanges, "End Date/Time", null, batch.BatchEndDateTime ); } decimal newControlAmount = batch.ControlAmount + transaction.TotalAmount; History.EvaluateChange( batchChanges, "Control Amount", batch.ControlAmount.FormatAsCurrency(), newControlAmount.FormatAsCurrency() ); batch.ControlAmount = newControlAmount; transaction.BatchId = batch.Id; batch.Transactions.Add( transaction ); rockContext.SaveChanges(); } ); if ( transaction.BatchId.HasValue ) { Task.Run( () => HistoryService.SaveChanges( new RockContext(), typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), transaction.BatchId.Value, batchChanges, true, CurrentPersonAliasId ) ); Task.Run( () => HistoryService.SaveChanges( new RockContext(), typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), transaction.BatchId.Value, txnChanges, CurrentPerson != null ? CurrentPerson.FullName : string.Empty, typeof( FinancialTransaction ), transaction.Id, true, CurrentPersonAliasId ) ); } List<string> registrationChanges = new List<string>(); registrationChanges.Add( string.Format( "Made {0} payment", transaction.TotalAmount.FormatAsCurrency() ) ); Task.Run( () => HistoryService.SaveChanges( new RockContext(), typeof( Registration ), Rock.SystemGuid.Category.HISTORY_EVENT_REGISTRATION.AsGuid(), registration.Id, registrationChanges, true, CurrentPersonAliasId ) ); TransactionCode = transaction.TransactionCode; return true; } else { return false; } }
/// <summary> /// Processes the payments. /// </summary> /// <param name="gateway">The gateway.</param> /// <param name="batchNamePrefix">The batch name prefix.</param> /// <param name="payments">The payments.</param> /// <param name="batchUrlFormat">The batch URL format.</param> /// <returns></returns> public static string ProcessPayments( FinancialGateway gateway, string batchNamePrefix, List<Payment> payments, string batchUrlFormat = "" ) { int totalPayments = 0; int totalAlreadyDownloaded = 0; int totalNoScheduledTransaction = 0; int totalAdded = 0; var batches = new List<FinancialBatch>(); var batchSummary = new Dictionary<Guid, List<Payment>>(); var initialControlAmounts = new Dictionary<Guid, decimal>(); var allBatchChanges = new Dictionary<Guid, List<string>>(); var allTxnChanges = new Dictionary<Guid, List<string>>(); var txnPersonNames = new Dictionary<Guid, string>(); using ( var rockContext = new RockContext() ) { var accountService = new FinancialAccountService( rockContext ); var txnService = new FinancialTransactionService( rockContext ); var batchService = new FinancialBatchService( rockContext ); var scheduledTxnService = new FinancialScheduledTransactionService( rockContext ); var contributionTxnType = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid() ); var defaultAccount = accountService.Queryable() .Where( a => a.IsActive && !a.ParentAccountId.HasValue && ( !a.StartDate.HasValue || a.StartDate.Value <= RockDateTime.Now ) && ( !a.EndDate.HasValue || a.EndDate.Value >= RockDateTime.Now ) ) .OrderBy( a => a.Order ) .FirstOrDefault(); var batchTxnChanges = new Dictionary<Guid, List<string>>(); var batchBatchChanges = new Dictionary<Guid, List<string>>(); foreach ( var payment in payments.Where( p => p.Amount > 0.0M ) ) { totalPayments++; // Only consider transactions that have not already been added if ( txnService.GetByTransactionCode( payment.TransactionCode ) == null ) { var scheduledTransaction = scheduledTxnService.GetByScheduleId( payment.GatewayScheduleId ); if ( scheduledTransaction != null ) { scheduledTransaction.IsActive = payment.ScheduleActive; var txnChanges = new List<string>(); var transaction = new FinancialTransaction(); transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); transaction.Guid = Guid.NewGuid(); allTxnChanges.Add( transaction.Guid, txnChanges ); txnChanges.Add( "Created Transaction (Downloaded from Gateway)" ); transaction.TransactionCode = payment.TransactionCode; History.EvaluateChange( txnChanges, "Transaction Code", string.Empty, transaction.TransactionCode ); transaction.TransactionDateTime = payment.TransactionDateTime; History.EvaluateChange( txnChanges, "Date/Time", null, transaction.TransactionDateTime ); transaction.ScheduledTransactionId = scheduledTransaction.Id; transaction.AuthorizedPersonAliasId = scheduledTransaction.AuthorizedPersonAliasId; History.EvaluateChange( txnChanges, "Person", string.Empty, scheduledTransaction.AuthorizedPersonAlias.Person.FullName ); txnPersonNames.Add( transaction.Guid, scheduledTransaction.AuthorizedPersonAlias.Person.FullName ); transaction.FinancialGatewayId = gateway.Id; History.EvaluateChange( txnChanges, "Gateway", string.Empty, gateway.Name ); transaction.TransactionTypeValueId = contributionTxnType.Id; History.EvaluateChange( txnChanges, "Type", string.Empty, contributionTxnType.Value ); var currencyTypeValue = payment.CurrencyTypeValue; var creditCardTypevalue = payment.CreditCardTypeValue; if ( scheduledTransaction.FinancialPaymentDetail != null ) { if ( currencyTypeValue == null && scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.HasValue ) { currencyTypeValue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.Value ); } if ( creditCardTypevalue == null && scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.HasValue ) { creditCardTypevalue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.Value ); } transaction.FinancialPaymentDetail.AccountNumberMasked = scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked; transaction.FinancialPaymentDetail.NameOnCardEncrypted = scheduledTransaction.FinancialPaymentDetail.NameOnCardEncrypted; transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationMonthEncrypted; transaction.FinancialPaymentDetail.ExpirationYearEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationYearEncrypted; } if ( currencyTypeValue != null ) { transaction.FinancialPaymentDetail.CurrencyTypeValueId = currencyTypeValue.Id; History.EvaluateChange( txnChanges, "Currency Type", string.Empty, currencyTypeValue.Value ); } if ( creditCardTypevalue != null ) { transaction.FinancialPaymentDetail.CreditCardTypeValueId = creditCardTypevalue.Id; History.EvaluateChange( txnChanges, "Credit Card Type", string.Empty, creditCardTypevalue.Value ); } //transaction.SourceTypeValueId = DefinedValueCache.Read( sourceGuid ).Id; // Try to allocate the amount of the transaction based on the current scheduled transaction accounts decimal remainingAmount = payment.Amount; foreach ( var detail in scheduledTransaction.ScheduledTransactionDetails.Where( d => d.Amount != 0.0M ) ) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.AccountId = detail.AccountId; if ( detail.Amount <= remainingAmount ) { // If the configured amount for this account is less than or equal to the remaining // amount, allocate the configured amount transactionDetail.Amount = detail.Amount; remainingAmount -= detail.Amount; } else { // If the configured amount is greater than the remaining amount, only allocate // the remaining amount transaction.Summary = "Note: Downloaded transaction amount was less than the configured allocation amounts for the Scheduled Transaction."; detail.Amount = remainingAmount; detail.Summary = "Note: The downloaded amount was not enough to apply the configured amount to this account."; remainingAmount = 0.0M; } transaction.TransactionDetails.Add( transactionDetail ); History.EvaluateChange( txnChanges, detail.Account.Name, 0.0M.ToString( "C2" ), transactionDetail.Amount.ToString( "C2" ) ); History.EvaluateChange( txnChanges, "Summary", string.Empty, transactionDetail.Summary ); if ( remainingAmount <= 0.0M ) { // If there's no amount left, break out of details break; } } // If there's still amount left after allocating based on current config, add the remainder // to the account that was configured for the most amount if ( remainingAmount > 0.0M ) { transaction.Summary = "Note: Downloaded transaction amount was greater than the configured allocation amounts for the Scheduled Transaction."; var transactionDetail = transaction.TransactionDetails .OrderByDescending( d => d.Amount ) .First(); if ( transactionDetail == null && defaultAccount != null ) { transactionDetail = new FinancialTransactionDetail(); transactionDetail.AccountId = defaultAccount.Id; } if ( transactionDetail != null ) { transactionDetail.Amount += remainingAmount; transactionDetail.Summary = "Note: Extra amount was applied to this account."; } History.EvaluateChange( txnChanges, defaultAccount.Name, 0.0M.ToString( "C2" ), transactionDetail.Amount.ToString( "C2" ) ); History.EvaluateChange( txnChanges, "Summary", string.Empty, transactionDetail.Summary ); } // Get the batch var batch = batchService.Get( batchNamePrefix, currencyTypeValue, creditCardTypevalue, transaction.TransactionDateTime.Value, gateway.GetBatchTimeOffset(), batches ); var batchChanges = new List<string>(); if ( batch.Id != 0 ) { initialControlAmounts.AddOrIgnore( batch.Guid, batch.ControlAmount ); } batch.ControlAmount += transaction.TotalAmount; batch.Transactions.Add( transaction ); // Add summary if ( !batchSummary.ContainsKey( batch.Guid ) ) { batchSummary.Add( batch.Guid, new List<Payment>() ); } batchSummary[batch.Guid].Add( payment ); totalAdded++; } else { totalNoScheduledTransaction++; } } else { totalAlreadyDownloaded++; } } foreach ( var batch in batches ) { var batchChanges = new List<string>(); allBatchChanges.Add( batch.Guid, batchChanges ); if ( batch.Id == 0 ) { batchChanges.Add( "Generated the batch" ); History.EvaluateChange( batchChanges, "Batch Name", string.Empty, batch.Name ); History.EvaluateChange( batchChanges, "Status", null, batch.Status ); History.EvaluateChange( batchChanges, "Start Date/Time", null, batch.BatchStartDateTime ); History.EvaluateChange( batchChanges, "End Date/Time", null, batch.BatchEndDateTime ); } if ( initialControlAmounts.ContainsKey( batch.Guid ) ) { History.EvaluateChange( batchChanges, "Control Amount", initialControlAmounts[batch.Guid].ToString( "C2" ), batch.ControlAmount.ToString( "C2" ) ); } } rockContext.WrapTransaction( () => { rockContext.SaveChanges(); foreach ( var batch in batches ) { HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, allBatchChanges[batch.Guid] ); foreach ( var transaction in batch.Transactions ) { if ( allTxnChanges.ContainsKey( transaction.Guid ) ) { HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), batch.Id, allTxnChanges[transaction.Guid], txnPersonNames[transaction.Guid], typeof( FinancialTransaction ), transaction.Id ); } } } rockContext.SaveChanges(); } ); } StringBuilder sb = new StringBuilder(); sb.AppendFormat( "<li>{0} {1} downloaded.</li>", totalPayments.ToString( "N0" ), ( totalPayments == 1 ? "payment" : "payments" ) ); if ( totalAlreadyDownloaded > 0 ) { sb.AppendFormat( "<li>{0} {1} previously downloaded and {2} already been added.</li>", totalAlreadyDownloaded.ToString( "N0" ), ( totalAlreadyDownloaded == 1 ? "payment was" : "payments were" ), ( totalAlreadyDownloaded == 1 ? "has" : "have" ) ); } if ( totalNoScheduledTransaction > 0 ) { sb.AppendFormat( "<li>{0} {1} could not be matched to an existing scheduled payment profile.</li>", totalNoScheduledTransaction.ToString( "N0" ), ( totalNoScheduledTransaction == 1 ? "payment" : "payments" ) ); } sb.AppendFormat( "<li>{0} {1} successfully added.</li>", totalAdded.ToString( "N0" ), ( totalAdded == 1 ? "payment was" : "payments were" ) ); foreach ( var batchItem in batchSummary ) { int items = batchItem.Value.Count; if (items > 0) { var batch = batches .Where( b => b.Guid.Equals( batchItem.Key ) ) .FirstOrDefault(); string batchName = string.Format("'{0} ({1})'", batch.Name, batch.BatchStartDateTime.Value.ToString("d")); if ( !string.IsNullOrWhiteSpace( batchUrlFormat ) ) { batchName = string.Format( "<a href='{0}'>{1}</a>", string.Format( batchUrlFormat, batch.Id ), batchName ); } decimal sum = batchItem.Value.Select( p => p.Amount ).Sum(); string summaryformat = items == 1 ? "<li>{0} transaction of {1} was added to the {2} batch.</li>" : "<li>{0} transactions totaling {1} were added to the {2} batch</li>"; sb.AppendFormat( summaryformat, items.ToString( "N0" ), sum.ToString( "C2" ), batchName ); } } return sb.ToString(); }
/// <summary> /// 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 confirmation. /// </summary> /// <param name="errorMessage">The error message.</param> /// <returns></returns> private bool ProcessConfirmation( out string errorMessage ) { var rockContext = new RockContext(); if ( string.IsNullOrWhiteSpace( TransactionCode ) ) { GatewayComponent gateway = hfPaymentTab.Value == "ACH" ? _achGateway : _ccGateway; if ( gateway == null ) { errorMessage = "There was a problem creating the payment gateway information"; return false; } Person person = GetPerson( true ); if ( person == null ) { errorMessage = "There was a problem creating the person information"; return false; } if ( !person.PrimaryAliasId.HasValue) { errorMessage = "There was a problem creating the person's primary alias"; return false; } PaymentInfo paymentInfo = GetPaymentInfo(); if ( paymentInfo == null ) { errorMessage = "There was a problem creating the payment information"; return false; } else { paymentInfo.FirstName = person.FirstName; paymentInfo.LastName = person.LastName; } if ( paymentInfo.CreditCardTypeValue != null ) { CreditCardTypeValueId = paymentInfo.CreditCardTypeValue.Id; } PaymentSchedule schedule = GetSchedule(); if ( schedule != null ) { schedule.PersonId = person.Id; var scheduledTransaction = gateway.AddScheduledPayment( schedule, paymentInfo, out errorMessage ); if ( scheduledTransaction != null ) { scheduledTransaction.TransactionFrequencyValueId = schedule.TransactionFrequencyValue.Id; scheduledTransaction.AuthorizedPersonAliasId = person.PrimaryAliasId.Value; scheduledTransaction.GatewayEntityTypeId = EntityTypeCache.Read( gateway.TypeGuid ).Id; scheduledTransaction.CurrencyTypeValueId = paymentInfo.CurrencyTypeValue.Id; scheduledTransaction.CreditCardTypeValueId = CreditCardTypeValueId; var changeSummary = new StringBuilder(); changeSummary.AppendFormat( "{0} starting {1}", schedule.TransactionFrequencyValue.Value, schedule.StartDate.ToShortDateString() ); changeSummary.AppendLine(); changeSummary.Append( paymentInfo.CurrencyTypeValue.Value ); if (paymentInfo.CreditCardTypeValue != null) { changeSummary.AppendFormat( " - {0}", paymentInfo.CreditCardTypeValue.Value ); } changeSummary.AppendFormat( " {0}", paymentInfo.MaskedNumber ); changeSummary.AppendLine(); foreach ( var account in SelectedAccounts.Where( a => a.Amount > 0 ) ) { var transactionDetail = new FinancialScheduledTransactionDetail(); transactionDetail.Amount = account.Amount; transactionDetail.AccountId = account.Id; scheduledTransaction.ScheduledTransactionDetails.Add( transactionDetail ); changeSummary.AppendFormat( "{0}: {1:C2}", account.Name, account.Amount ); changeSummary.AppendLine(); } var transactionService = new FinancialScheduledTransactionService( rockContext ); transactionService.Add( scheduledTransaction ); rockContext.SaveChanges(); // Add a note about the change var noteTypeService = new NoteTypeService( rockContext ); var noteType = noteTypeService.Get( scheduledTransaction.TypeId, "Note" ); var noteService = new NoteService( rockContext ); var note = new Note(); note.NoteTypeId = noteType.Id; note.EntityId = scheduledTransaction.Id; note.Caption = "Created Transaction"; note.Text = changeSummary.ToString(); noteService.Add( note ); rockContext.SaveChanges(); ScheduleId = scheduledTransaction.GatewayScheduleId; TransactionCode = scheduledTransaction.TransactionCode; } else { return false; } } else { var transaction = gateway.Charge( paymentInfo, out errorMessage ); if ( transaction != null ) { transaction.TransactionDateTime = RockDateTime.Now; transaction.AuthorizedPersonAliasId = person.PrimaryAliasId; transaction.GatewayEntityTypeId = gateway.TypeId; transaction.TransactionTypeValueId = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION ) ).Id; transaction.CurrencyTypeValueId = paymentInfo.CurrencyTypeValue.Id; transaction.CreditCardTypeValueId = CreditCardTypeValueId; Guid sourceGuid = Guid.Empty; if ( Guid.TryParse( GetAttributeValue( "Source" ), out sourceGuid ) ) { transaction.SourceTypeValueId = DefinedValueCache.Read( sourceGuid ).Id; } foreach ( var account in SelectedAccounts.Where( a => a.Amount > 0 ) ) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.Amount = account.Amount; transactionDetail.AccountId = account.Id; transaction.TransactionDetails.Add( transactionDetail ); } var batchService = new FinancialBatchService( rockContext ); // Get the batch var batch = batchService.Get( GetAttributeValue( "BatchNamePrefix" ), paymentInfo.CurrencyTypeValue, paymentInfo.CreditCardTypeValue, transaction.TransactionDateTime.Value, gateway.BatchTimeOffset ); batch.ControlAmount += transaction.TotalAmount; transaction.BatchId = batch.Id; batch.Transactions.Add( transaction ); rockContext.SaveChanges(); TransactionCode = transaction.TransactionCode; } else { return false; } } tdTransactionCodeReceipt.Description = TransactionCode; tdTransactionCodeReceipt.Visible = !string.IsNullOrWhiteSpace( TransactionCode ); tdScheduleId.Description = ScheduleId; tdScheduleId.Visible = !string.IsNullOrWhiteSpace( ScheduleId ); tdNameReceipt.Description = paymentInfo.FullName; tdPhoneReceipt.Description = paymentInfo.Phone; tdEmailReceipt.Description = paymentInfo.Email; tdAddressReceipt.Description = string.Format( "{0} {1}, {2} {3}", paymentInfo.Street1, paymentInfo.City, paymentInfo.State, paymentInfo.PostalCode ); rptAccountListReceipt.DataSource = SelectedAccounts.Where( a => a.Amount != 0 ); rptAccountListReceipt.DataBind(); tdTotalReceipt.Description = paymentInfo.Amount.ToString( "C" ); tdPaymentMethodReceipt.Description = paymentInfo.CurrencyTypeValue.Description; tdAccountNumberReceipt.Description = paymentInfo.MaskedNumber; tdWhenReceipt.Description = schedule != null ? schedule.ToString() : "Today"; // If there was a transaction code returned and this was not already created from a previous saved account, // show the option to save the account. if ( !( paymentInfo is ReferencePaymentInfo ) && !string.IsNullOrWhiteSpace( TransactionCode ) ) { cbSaveAccount.Visible = true; pnlSaveAccount.Visible = true; txtSaveAccount.Visible = true; // If current person does not have a login, have them create a username and password phCreateLogin.Visible = !new UserLoginService( rockContext ).GetByPersonId( person.Id ).Any(); } else { pnlSaveAccount.Visible = false; } return true; } else { pnlDupWarning.Visible = true; divActions.Visible = false; errorMessage = string.Empty; return false; } }
/// <summary> /// Processes the payment. /// </summary> /// <param name="rockContext">The rock context.</param> /// <param name="registration">The registration.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> private bool ProcessPayment( RockContext rockContext, Registration registration, out string errorMessage ) { GatewayComponent gateway = null; if ( RegistrationTemplate != null && RegistrationTemplate.FinancialGateway != null ) { gateway = RegistrationTemplate.FinancialGateway.GetGatewayComponent(); } if ( gateway == null ) { errorMessage = "There was a problem creating the payment gateway information"; return false; } if ( !RegistrationInstanceState.AccountId.HasValue || RegistrationInstanceState.Account == null ) { errorMessage = "There was a problem with the account configuration for this " + RegistrationTerm.ToLower(); return false; } var paymentInfo = new CreditCardPaymentInfo( txtCreditCard.Text, txtCVV.Text, mypExpiration.SelectedDate.Value ); paymentInfo.NameOnCard = gateway != null && gateway.SplitNameOnCard ? txtCardFirstName.Text : txtCardName.Text; paymentInfo.LastNameOnCard = txtCardLastName.Text; paymentInfo.BillingStreet1 = acBillingAddress.Street1; paymentInfo.BillingStreet2 = acBillingAddress.Street2; paymentInfo.BillingCity = acBillingAddress.City; paymentInfo.BillingState = acBillingAddress.State; paymentInfo.BillingPostalCode = acBillingAddress.PostalCode; paymentInfo.BillingCountry = acBillingAddress.Country; paymentInfo.Amount = RegistrationState.PaymentAmount ?? 0.0m; paymentInfo.Email = RegistrationState.ConfirmationEmail; paymentInfo.FirstName = RegistrationState.FirstName; paymentInfo.LastName = RegistrationState.LastName; var transaction = gateway.Charge( RegistrationTemplate.FinancialGateway, paymentInfo, out errorMessage ); if ( transaction != null ) { var txnChanges = new List<string>(); txnChanges.Add( "Created Transaction" ); History.EvaluateChange( txnChanges, "Transaction Code", string.Empty, transaction.TransactionCode ); transaction.AuthorizedPersonAliasId = registration.PersonAliasId; transaction.TransactionDateTime = RockDateTime.Now; History.EvaluateChange( txnChanges, "Date/Time", null, transaction.TransactionDateTime ); transaction.FinancialGatewayId = RegistrationTemplate.FinancialGatewayId; History.EvaluateChange( txnChanges, "Gateway", string.Empty, RegistrationTemplate.FinancialGateway.Name ); var txnType = DefinedValueCache.Read( new Guid( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_EVENT_REGISTRATION ) ); transaction.TransactionTypeValueId = txnType.Id; History.EvaluateChange( txnChanges, "Type", string.Empty, txnType.Value ); if ( transaction.FinancialPaymentDetail == null ) { transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); } transaction.FinancialPaymentDetail.NameOnCardEncrypted = Rock.Security.Encryption.EncryptString( paymentInfo.NameOnCard ); transaction.FinancialPaymentDetail.AccountNumberMasked = paymentInfo.MaskedNumber; transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = Rock.Security.Encryption.EncryptString( paymentInfo.ExpirationDate.Month.ToString() ); transaction.FinancialPaymentDetail.ExpirationYearEncrypted = Rock.Security.Encryption.EncryptString( paymentInfo.ExpirationDate.Year.ToString() ); transaction.FinancialPaymentDetail.CurrencyTypeValueId = paymentInfo.CurrencyTypeValue.Id; History.EvaluateChange( txnChanges, "Currency Type", string.Empty, paymentInfo.CurrencyTypeValue.Value ); transaction.FinancialPaymentDetail.CreditCardTypeValueId = paymentInfo.CreditCardTypeValue != null ? paymentInfo.CreditCardTypeValue.Id : (int?)null; if ( transaction.FinancialPaymentDetail.CreditCardTypeValueId.HasValue ) { var ccType = DefinedValueCache.Read( transaction.FinancialPaymentDetail.CreditCardTypeValueId.Value ); History.EvaluateChange( txnChanges, "Credit Card Type", string.Empty, ccType.Value ); } Guid sourceGuid = Guid.Empty; if ( Guid.TryParse( GetAttributeValue( "Source" ), out sourceGuid ) ) { var source = DefinedValueCache.Read( sourceGuid ); if ( source != null ) { transaction.SourceTypeValueId = source.Id; History.EvaluateChange( txnChanges, "Source", string.Empty, source.Value ); } } transaction.Summary = registration.GetSummary( RegistrationInstanceState ); var transactionDetail = new FinancialTransactionDetail(); transactionDetail.Amount = RegistrationState.PaymentAmount ?? 0.0m; transactionDetail.AccountId = RegistrationInstanceState.AccountId.Value; transactionDetail.EntityTypeId = EntityTypeCache.Read( typeof( Rock.Model.Registration ) ).Id; transactionDetail.EntityId = registration.Id; transaction.TransactionDetails.Add( transactionDetail ); History.EvaluateChange( txnChanges, RegistrationInstanceState.Account.Name, 0.0M.ToString( "C2" ), transactionDetail.Amount.ToString( "C2" ) ); var batchService = new FinancialBatchService( rockContext ); // Get the batch var batch = batchService.Get( GetAttributeValue( "BatchNamePrefix" ), paymentInfo.CurrencyTypeValue, paymentInfo.CreditCardTypeValue, transaction.TransactionDateTime.Value, RegistrationTemplate.FinancialGateway.GetBatchTimeOffset() ); var batchChanges = new List<string>(); if ( batch.Id == 0 ) { batchChanges.Add( "Generated the batch" ); History.EvaluateChange( batchChanges, "Batch Name", string.Empty, batch.Name ); History.EvaluateChange( batchChanges, "Status", null, batch.Status ); History.EvaluateChange( batchChanges, "Start Date/Time", null, batch.BatchStartDateTime ); History.EvaluateChange( batchChanges, "End Date/Time", null, batch.BatchEndDateTime ); } decimal newControlAmount = batch.ControlAmount + transaction.TotalAmount; History.EvaluateChange( batchChanges, "Control Amount", batch.ControlAmount.ToString( "C2" ), newControlAmount.ToString( "C2" ) ); batch.ControlAmount = newControlAmount; transaction.BatchId = batch.Id; batch.Transactions.Add( transaction ); rockContext.SaveChanges(); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), batch.Id, batchChanges ); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), batch.Id, txnChanges, CurrentPerson != null ? CurrentPerson.FullName : string.Empty, typeof( FinancialTransaction ), transaction.Id ); TransactionCode = transaction.TransactionCode; return true; } else { return false; } }
/// <summary> /// Handles the Delete event of the grdFinancialBatch control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs"/> instance containing the event data.</param> protected void rGridBatch_Delete( object sender, RowEventArgs e ) { var FinancialBatchService = new Rock.Model.FinancialBatchService(); Rock.Model.FinancialBatch FinancialBatch = FinancialBatchService.Get( (int)rGridBatch.DataKeys[e.RowIndex]["id"] ); if ( FinancialBatch != null ) { FinancialBatchService.Delete( FinancialBatch, CurrentPersonId ); FinancialBatchService.Save( FinancialBatch, CurrentPersonId ); } BindGrid(); }
/// <summary> /// When implemented by a class, enables a server control to process an event raised when a form is posted to the server. /// </summary> /// <param name="eventArgument">A <see cref="T:System.String" /> that represents an optional event argument to be passed to the event handler.</param> public void RaisePostBackEvent( string eventArgument ) { if ( _canEdit && _batch != null ) { if ( eventArgument == "MoveTransactions" && _ddlMove != null && _ddlMove.SelectedValue != null && !String.IsNullOrWhiteSpace( _ddlMove.SelectedValue ) ) { var txnsSelected = new List<int>(); gTransactions.SelectedKeys.ToList().ForEach( b => txnsSelected.Add( b.ToString().AsInteger() ) ); if ( txnsSelected.Any() ) { var rockContext = new RockContext(); var batchService = new FinancialBatchService( rockContext ); var newBatch = batchService.Get( _ddlMove.SelectedValue.AsInteger() ); var oldBatch = batchService.Get( _batch.Id ); if ( oldBatch != null && newBatch != null && newBatch.Status == BatchStatus.Open ) { var txnService = new FinancialTransactionService( rockContext ); var txnsToUpdate = txnService.Queryable( "AuthorizedPersonAlias.Person" ) .Where( t => txnsSelected.Contains( t.Id ) ) .ToList(); decimal oldBatchControlAmount = oldBatch.ControlAmount; decimal newBatchControlAmount = newBatch.ControlAmount; foreach ( var txn in txnsToUpdate ) { string caption = ( txn.AuthorizedPersonAlias != null && txn.AuthorizedPersonAlias.Person != null ) ? txn.AuthorizedPersonAlias.Person.FullName : string.Format( "Transaction: {0}", txn.Id ); var changes = new List<string>(); History.EvaluateChange( changes, "Batch", string.Format( "{0} (Id:{1})", oldBatch.Name, oldBatch.Id ), string.Format( "{0} (Id:{1})", newBatch.Name, newBatch.Id ) ); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), oldBatch.Id, changes, caption, typeof( FinancialTransaction ), txn.Id, false ); HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_TRANSACTION.AsGuid(), newBatch.Id, changes, caption, typeof( FinancialTransaction ), txn.Id, false ); txn.BatchId = newBatch.Id; oldBatchControlAmount -= txn.TotalAmount; newBatchControlAmount += txn.TotalAmount; } var oldBatchChanges = new List<string>(); History.EvaluateChange( oldBatchChanges, "Control Amount", oldBatch.ControlAmount.FormatAsCurrency(), oldBatchControlAmount.FormatAsCurrency() ); oldBatch.ControlAmount = oldBatchControlAmount; HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), oldBatch.Id, oldBatchChanges, false ); var newBatchChanges = new List<string>(); History.EvaluateChange( newBatchChanges, "Control Amount", newBatch.ControlAmount.FormatAsCurrency(), newBatchControlAmount.FormatAsCurrency() ); newBatch.ControlAmount = newBatchControlAmount; HistoryService.SaveChanges( rockContext, typeof( FinancialBatch ), Rock.SystemGuid.Category.HISTORY_FINANCIAL_BATCH.AsGuid(), newBatch.Id, newBatchChanges, false ); rockContext.SaveChanges(); var pageRef = new Rock.Web.PageReference( RockPage.PageId ); pageRef.Parameters = new Dictionary<string, string>(); pageRef.Parameters.Add( "batchid", newBatch.Id.ToString() ); string newBatchLink = string.Format( "<a href='{0}'>{1}</a>", pageRef.BuildUrl(), newBatch.Name ); RockPage.UpdateBlocks( "~/Blocks/Finance/BatchDetail.ascx" ); nbResult.Text = string.Format( "{0} transactions were moved to the '{1}' batch.", txnsToUpdate.Count().ToString( "N0" ), newBatchLink ); nbResult.NotificationBoxType = NotificationBoxType.Success; nbResult.Visible = true; } else { nbResult.Text = string.Format( "The selected batch does not exist, or is no longer open." ); nbResult.NotificationBoxType = NotificationBoxType.Danger; nbResult.Visible = true; } } else { nbResult.Text = string.Format( "There were not any transactions selected." ); nbResult.NotificationBoxType = NotificationBoxType.Warning; nbResult.Visible = true; } } _ddlMove.SelectedIndex = 0; } BindGrid(); }
/// <summary> /// Handles the GridReorder event of the grdFinancialBatch control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="GridReorderEventArgs"/> instance containing the event data.</param> private void rGridBatch_GridReorder( object sender, GridReorderEventArgs e ) { var batchService = new Rock.Model.FinancialBatchService(); var queryable = batchService.Queryable(); List<Rock.Model.FinancialBatch> items = queryable.ToList(); batchService.Reorder( items, e.OldIndex, e.NewIndex, CurrentPersonId ); BindGrid(); }
/// <summary> /// Gets the batch. /// </summary> /// <param name="batchId">The batch identifier.</param> /// <returns></returns> private FinancialBatch GetBatch( int batchId, RockContext rockContext = null ) { rockContext = rockContext ?? new RockContext(); var batch = new FinancialBatchService( rockContext ).Get( batchId ); return batch; }
/// <summary> /// Deletes the family's addresses, phone numbers, photos, viewed records, and people. /// TODO: delete attendance codes for attendance data that's about to be deleted when /// we delete the person record. /// </summary> /// <param name="families">The families.</param> /// <param name="rockContext">The rock context.</param> private void DeleteExistingFamilyData( XElement families, RockContext rockContext ) { PersonService personService = new PersonService( rockContext ); PhoneNumberService phoneNumberService = new PhoneNumberService( rockContext ); PersonViewedService personViewedService = new PersonViewedService( rockContext ); PageViewService pageViewService = new PageViewService( rockContext ); BinaryFileService binaryFileService = new BinaryFileService( rockContext ); PersonAliasService personAliasService = new PersonAliasService( rockContext ); PersonDuplicateService personDuplicateService = new PersonDuplicateService( rockContext ); NoteService noteService = new NoteService( rockContext ); AuthService authService = new AuthService( rockContext ); CommunicationService communicationService = new CommunicationService( rockContext ); CommunicationRecipientService communicationRecipientService = new CommunicationRecipientService( rockContext ); FinancialBatchService financialBatchService = new FinancialBatchService( rockContext ); FinancialTransactionService financialTransactionService = new FinancialTransactionService( rockContext ); PersonPreviousNameService personPreviousNameService = new PersonPreviousNameService( rockContext ); ConnectionRequestService connectionRequestService = new ConnectionRequestService( rockContext ); ConnectionRequestActivityService connectionRequestActivityService = new ConnectionRequestActivityService( rockContext ); // delete the batch data List<int> imageIds = new List<int>(); foreach ( var batch in financialBatchService.Queryable().Where( b => b.Name.StartsWith( "SampleData" ) ) ) { imageIds.AddRange( batch.Transactions.SelectMany( t => t.Images ).Select( i => i.BinaryFileId ).ToList() ); financialTransactionService.DeleteRange( batch.Transactions ); financialBatchService.Delete( batch ); } // delete all transaction images foreach ( var image in binaryFileService.GetByIds( imageIds ) ) { binaryFileService.Delete( image ); } foreach ( var elemFamily in families.Elements( "family" ) ) { Guid guid = elemFamily.Attribute( "guid" ).Value.Trim().AsGuid(); GroupService groupService = new GroupService( rockContext ); Group family = groupService.Get( guid ); if ( family != null ) { var groupMemberService = new GroupMemberService( rockContext ); var members = groupMemberService.GetByGroupId( family.Id, true ); // delete the people records string errorMessage; List<int> photoIds = members.Select( m => m.Person ).Where( p => p.PhotoId != null ).Select( a => (int)a.PhotoId ).ToList(); foreach ( var person in members.Select( m => m.Person ) ) { person.GivingGroup = null; person.GivingGroupId = null; person.PhotoId = null; // delete phone numbers foreach ( var phone in phoneNumberService.GetByPersonId( person.Id ) ) { if ( phone != null ) { phoneNumberService.Delete( phone ); } } // delete communication recipient foreach ( var recipient in communicationRecipientService.Queryable().Where( r => r.PersonAlias.PersonId == person.Id ) ) { communicationRecipientService.Delete( recipient ); } // delete communication foreach ( var communication in communicationService.Queryable().Where( c => c.SenderPersonAliasId == person.PrimaryAlias.Id ) ) { communicationService.Delete( communication ); } // delete person viewed records foreach ( var view in personViewedService.GetByTargetPersonId( person.Id ) ) { personViewedService.Delete( view ); } // delete page viewed records foreach ( var view in pageViewService.GetByPersonId( person.Id ) ) { pageViewService.Delete( view ); } // delete notes created by them or on their record. foreach ( var note in noteService.Queryable().Where ( n => n.CreatedByPersonAlias.PersonId == person.Id || (n.NoteType.EntityTypeId == _personEntityTypeId && n.EntityId == person.Id ) ) ) { noteService.Delete( note ); } // delete previous names on their records foreach ( var previousName in personPreviousNameService.Queryable().Where( r => r.PersonAlias.PersonId == person.Id ) ) { personPreviousNameService.Delete( previousName ); } // delete any GroupMember records they have foreach ( var groupMember in groupMemberService.Queryable().Where( gm => gm.PersonId == person.Id ) ) { groupMemberService.Delete( groupMember ); } //// delete any Authorization data //foreach ( var auth in authService.Queryable().Where( a => a.PersonId == person.Id ) ) //{ // authService.Delete( auth ); //} // delete their aliases foreach ( var alias in personAliasService.Queryable().Where( a => a.PersonId == person.Id ) ) { foreach ( var duplicate in personDuplicateService.Queryable().Where( d => d.DuplicatePersonAliasId == alias.Id ) ) { personDuplicateService.Delete( duplicate ); } personAliasService.Delete( alias ); } // delete any connection requests tied to them foreach ( var request in connectionRequestService.Queryable().Where( r => r.PersonAlias.PersonId == person.Id || r.ConnectorPersonAlias.PersonId == person.Id ) ) { connectionRequestActivityService.DeleteRange( request.ConnectionRequestActivities ); connectionRequestService.Delete( request ); } // Save these changes so the CanDelete passes the check... //rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); if ( personService.CanDelete( person, out errorMessage ) ) { personService.Delete( person ); //rockContext.ChangeTracker.DetectChanges(); //rockContext.SaveChanges( disablePrePostProcessing: true ); } else { throw new Exception( string.Format( "Trying to delete {0}, but: {1}", person.FullName, errorMessage ) ); } } //rockContext.ChangeTracker.DetectChanges(); rockContext.SaveChanges( disablePrePostProcessing: true ); // delete all member photos foreach ( var photo in binaryFileService.GetByIds( photoIds ) ) { binaryFileService.Delete( photo ); } DeleteGroupAndMemberData( family, rockContext ); } } }
/// <summary> /// Processes the payments. /// </summary> /// <param name="gateway">The gateway.</param> /// <param name="batchNamePrefix">The batch name prefix.</param> /// <param name="payments">The payments.</param> /// <param name="batchUrlFormat">The batch URL format.</param> /// <param name="receiptEmail">The receipt email.</param> /// <returns></returns> public static string ProcessPayments( FinancialGateway gateway, string batchNamePrefix, List<Payment> payments, string batchUrlFormat = "", Guid? receiptEmail = null ) { int totalPayments = 0; int totalAlreadyDownloaded = 0; int totalNoScheduledTransaction = 0; int totalAdded = 0; int totalReversals = 0; int totalStatusChanges = 0; var batches = new List<FinancialBatch>(); var batchSummary = new Dictionary<Guid, List<Decimal>>(); var initialControlAmounts = new Dictionary<Guid, decimal>(); var txnPersonNames = new Dictionary<Guid, string>(); var gatewayComponent = gateway.GetGatewayComponent(); var newTransactions = new List<FinancialTransaction>(); using ( var rockContext = new RockContext() ) { var accountService = new FinancialAccountService( rockContext ); var txnService = new FinancialTransactionService( rockContext ); var batchService = new FinancialBatchService( rockContext ); var scheduledTxnService = new FinancialScheduledTransactionService( rockContext ); var contributionTxnType = DefinedValueCache.Read( Rock.SystemGuid.DefinedValue.TRANSACTION_TYPE_CONTRIBUTION.AsGuid() ); var defaultAccount = accountService.Queryable() .Where( a => a.IsActive && !a.ParentAccountId.HasValue && ( !a.StartDate.HasValue || a.StartDate.Value <= RockDateTime.Now ) && ( !a.EndDate.HasValue || a.EndDate.Value >= RockDateTime.Now ) ) .OrderBy( a => a.Order ) .FirstOrDefault(); var batchTxnChanges = new Dictionary<Guid, List<string>>(); var batchBatchChanges = new Dictionary<Guid, List<string>>(); var scheduledTransactionIds = new List<int>(); foreach ( var payment in payments.Where( p => p.Amount > 0.0M ) ) { totalPayments++; var scheduledTransaction = scheduledTxnService.GetByScheduleId( payment.GatewayScheduleId ); if ( scheduledTransaction != null ) { // Find existing payments with same transaction code var txns = txnService .Queryable( "TransactionDetails" ) .Where( t => t.TransactionCode == payment.TransactionCode ) .ToList(); // Calculate whether a transaction needs to be added var txnAmount = CalculateTransactionAmount( payment, txns ); if ( txnAmount != 0.0M ) { scheduledTransactionIds.Add( scheduledTransaction.Id ); if ( payment.ScheduleActive.HasValue ) { scheduledTransaction.IsActive = payment.ScheduleActive.Value; } var transaction = new FinancialTransaction(); transaction.FinancialPaymentDetail = new FinancialPaymentDetail(); transaction.Guid = Guid.NewGuid(); transaction.TransactionCode = payment.TransactionCode; transaction.TransactionDateTime = payment.TransactionDateTime; transaction.Status = payment.Status; transaction.StatusMessage = payment.StatusMessage; transaction.ScheduledTransactionId = scheduledTransaction.Id; transaction.AuthorizedPersonAliasId = scheduledTransaction.AuthorizedPersonAliasId; transaction.SourceTypeValueId = scheduledTransaction.SourceTypeValueId; txnPersonNames.Add( transaction.Guid, scheduledTransaction.AuthorizedPersonAlias.Person.FullName ); transaction.FinancialGatewayId = gateway.Id; transaction.TransactionTypeValueId = contributionTxnType.Id; if ( txnAmount < 0.0M ) { transaction.Summary = "Reversal for previous transaction that failed during processing." + Environment.NewLine; } var currencyTypeValue = payment.CurrencyTypeValue; var creditCardTypevalue = payment.CreditCardTypeValue; if ( scheduledTransaction.FinancialPaymentDetail != null ) { if ( currencyTypeValue == null && scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.HasValue ) { currencyTypeValue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CurrencyTypeValueId.Value ); } if ( creditCardTypevalue == null && scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.HasValue ) { creditCardTypevalue = DefinedValueCache.Read( scheduledTransaction.FinancialPaymentDetail.CreditCardTypeValueId.Value ); } transaction.FinancialPaymentDetail.AccountNumberMasked = scheduledTransaction.FinancialPaymentDetail.AccountNumberMasked; transaction.FinancialPaymentDetail.NameOnCardEncrypted = scheduledTransaction.FinancialPaymentDetail.NameOnCardEncrypted; transaction.FinancialPaymentDetail.ExpirationMonthEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationMonthEncrypted; transaction.FinancialPaymentDetail.ExpirationYearEncrypted = scheduledTransaction.FinancialPaymentDetail.ExpirationYearEncrypted; transaction.FinancialPaymentDetail.BillingLocationId = scheduledTransaction.FinancialPaymentDetail.BillingLocationId; } if ( currencyTypeValue != null ) { transaction.FinancialPaymentDetail.CurrencyTypeValueId = currencyTypeValue.Id; } if ( creditCardTypevalue != null ) { transaction.FinancialPaymentDetail.CreditCardTypeValueId = creditCardTypevalue.Id; } // Try to allocate the amount of the transaction based on the current scheduled transaction accounts decimal remainingAmount = Math.Abs( txnAmount ); foreach ( var detail in scheduledTransaction.ScheduledTransactionDetails.Where( d => d.Amount != 0.0M ) ) { var transactionDetail = new FinancialTransactionDetail(); transactionDetail.AccountId = detail.AccountId; if ( detail.Amount <= remainingAmount ) { // If the configured amount for this account is less than or equal to the remaining // amount, allocate the configured amount transactionDetail.Amount = detail.Amount; remainingAmount -= detail.Amount; } else { // If the configured amount is greater than the remaining amount, only allocate // the remaining amount transaction.Summary += "Note: Downloaded transaction amount was less than the configured allocation amounts for the Scheduled Transaction."; transactionDetail.Amount = remainingAmount; transactionDetail.Summary = "Note: The downloaded amount was not enough to apply the configured amount to this account."; remainingAmount = 0.0M; } transaction.TransactionDetails.Add( transactionDetail ); if ( remainingAmount <= 0.0M ) { // If there's no amount left, break out of details break; } } // If there's still amount left after allocating based on current config, add the remainder // to the account that was configured for the most amount if ( remainingAmount > 0.0M ) { transaction.Summary += "Note: Downloaded transaction amount was greater than the configured allocation amounts for the Scheduled Transaction."; var transactionDetail = transaction.TransactionDetails .OrderByDescending( d => d.Amount ) .FirstOrDefault(); if ( transactionDetail == null && defaultAccount != null ) { transactionDetail = new FinancialTransactionDetail(); transactionDetail.AccountId = defaultAccount.Id; transaction.TransactionDetails.Add( transactionDetail ); } if ( transactionDetail != null ) { transactionDetail.Amount += remainingAmount; transactionDetail.Summary = "Note: Extra amount was applied to this account."; } } // If the amount to apply was negative, update all details to be negative (absolute value was used when allocating to accounts) if ( txnAmount < 0.0M ) { foreach ( var txnDetail in transaction.TransactionDetails ) { txnDetail.Amount = 0 - txnDetail.Amount; } } // Get the batch var batch = batchService.Get( batchNamePrefix, currencyTypeValue, creditCardTypevalue, transaction.TransactionDateTime.Value, gateway.GetBatchTimeOffset(), batches ); var batchChanges = new List<string>(); if ( batch.Id != 0 ) { initialControlAmounts.AddOrIgnore( batch.Guid, batch.ControlAmount ); } batch.ControlAmount += transaction.TotalAmount; batch.Transactions.Add( transaction ); if ( txnAmount > 0.0M && receiptEmail.HasValue ) { newTransactions.Add( transaction ); } // Add summary if ( !batchSummary.ContainsKey( batch.Guid ) ) { batchSummary.Add( batch.Guid, new List<Decimal>() ); } batchSummary[batch.Guid].Add( txnAmount ); if ( txnAmount > 0.0M ) { totalAdded++; } else { totalReversals++; } } else { totalAlreadyDownloaded++; foreach ( var txn in txns.Where( t => t.Status != payment.Status || t.StatusMessage != payment.StatusMessage ) ) { txn.Status = payment.Status; txn.StatusMessage = payment.StatusMessage; totalStatusChanges++; } } } else { totalNoScheduledTransaction++; } } rockContext.SaveChanges(); // Queue a transaction to update the status of all affected scheduled transactions var updatePaymentStatusTxn = new Rock.Transactions.UpdatePaymentStatusTransaction( gateway.Id, scheduledTransactionIds ); Rock.Transactions.RockQueue.TransactionQueue.Enqueue( updatePaymentStatusTxn ); if ( receiptEmail.HasValue ) { // Queue a transaction to send receipts var newTransactionIds = newTransactions.Select( t => t.Id ).ToList(); var sendPaymentReceiptsTxn = new Rock.Transactions.SendPaymentReceipts( receiptEmail.Value, newTransactionIds ); Rock.Transactions.RockQueue.TransactionQueue.Enqueue( sendPaymentReceiptsTxn ); } } StringBuilder sb = new StringBuilder(); sb.AppendFormat( "<li>{0} {1} downloaded.</li>", totalPayments.ToString( "N0" ), ( totalPayments == 1 ? "payment" : "payments" ) ); if ( totalAlreadyDownloaded > 0 ) { sb.AppendFormat( "<li>{0} {1} previously downloaded and {2} already been added.</li>", totalAlreadyDownloaded.ToString( "N0" ), ( totalAlreadyDownloaded == 1 ? "payment was" : "payments were" ), ( totalAlreadyDownloaded == 1 ? "has" : "have" ) ); } if ( totalStatusChanges > 0 ) { sb.AppendFormat( "<li>{0} {1} previously downloaded but had a change of status.</li>", totalStatusChanges.ToString( "N0" ), ( totalStatusChanges == 1 ? "payment was" : "payments were" ) ); } if ( totalNoScheduledTransaction > 0 ) { sb.AppendFormat( "<li>{0} {1} could not be matched to an existing scheduled payment profile.</li>", totalNoScheduledTransaction.ToString( "N0" ), ( totalNoScheduledTransaction == 1 ? "payment" : "payments" ) ); } sb.AppendFormat( "<li>{0} {1} successfully added.</li>", totalAdded.ToString( "N0" ), ( totalAdded == 1 ? "payment was" : "payments were" ) ); if ( totalReversals > 0 ) { sb.AppendFormat( "<li>{0} {1} added as a reversal to a previous transaction.</li>", totalReversals.ToString( "N0" ), ( totalReversals == 1 ? "payment was" : "payments were" ) ); } if ( totalStatusChanges > 0 ) { sb.AppendFormat( "<li>{0} {1} previously downloaded but had a change of status.</li>", totalStatusChanges.ToString( "N0" ), ( totalStatusChanges == 1 ? "payment was" : "payments were" ) ); } foreach ( var batchItem in batchSummary ) { int items = batchItem.Value.Count; if ( items > 0 ) { var batch = batches .Where( b => b.Guid.Equals( batchItem.Key ) ) .FirstOrDefault(); string batchName = string.Format( "'{0} ({1})'", batch.Name, batch.BatchStartDateTime.Value.ToString( "d" ) ); if ( !string.IsNullOrWhiteSpace( batchUrlFormat ) ) { batchName = string.Format( "<a href='{0}'>{1}</a>", string.Format( batchUrlFormat, batch.Id ), batchName ); } decimal sum = batchItem.Value.Sum(); string summaryformat = items == 1 ? "<li>{0} transaction of {1} was added to the {2} batch.</li>" : "<li>{0} transactions totaling {1} were added to the {2} batch</li>"; sb.AppendFormat( summaryformat, items.ToString( "N0" ), sum.FormatAsCurrency(), batchName ); } } return sb.ToString(); }
/// <summary> /// Handles the Click event of the lbSave control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param> protected void lbSave_Click( object sender, EventArgs e ) { var rockContext = new RockContext(); var batchService = new FinancialBatchService( rockContext ); FinancialBatch batch = null; int batchId = hfBatchId.Value.AsInteger(); if ( batchId == 0 ) { batch = new FinancialBatch(); batchService.Add( batch ); } else { batch = batchService.Get( batchId ); } if ( batch != null ) { batch.Name = tbName.Text; batch.Status = (BatchStatus)ddlStatus.SelectedIndex; batch.CampusId = campCampus.SelectedCampusId; batch.BatchStartDateTime = dtpStart.SelectedDateTimeIsBlank ? null : dtpStart.SelectedDateTime; if ( dtpEnd.SelectedDateTimeIsBlank && batch.BatchStartDateTime.HasValue ) { batch.BatchEndDateTime = batch.BatchStartDateTime.Value.AddDays( 1 ); } else { batch.BatchEndDateTime = dtpEnd.SelectedDateTimeIsBlank ? null : dtpEnd.SelectedDateTime; } batch.ControlAmount = tbControlAmount.Text.AsDecimal(); batch.AccountingSystemCode = tbAccountingCode.Text; if ( !Page.IsValid || !batch.IsValid ) { // Controls will render the error messages return; } rockContext.SaveChanges(); hfBatchId.SetValue( batch.Id ); // Requery the batch to support EF navigation properties var savedBatch = GetBatch( batch.Id ); ShowReadonlyDetails( savedBatch ); } }