Select your user interface:

History Tab

The History tab typically contains one section referencing a custom data list that returns a status list for the business process

The data list is a custom CLR-based data list, which means the code to retrieve the data for the data list is implemented as a .NET CLR class. While the data list is a custom list, Blackbaud recommends leveraging this CLR class that comes with the product which does the dirty work of retrieving the status history.

A CLR-based spec type defines the server-side catalog implementation that will be used to fetch the data for the list. Use this spec type when complex logic is required or when you need to pull data together from atypical data sources. A CLR Data List Spec requires an XML element named CLRDataList that describes a data list based on CLR code. The CLR code is implemented within a .NET class that resides within a .NET CLR assembly hosted in the web service layer. The .NET class should inherit Blackbaud.AppFx.Server.AppCatalog.AppDataList. The CLRDataList XML element can contain a list of parameters that match a set of public variables within the .NET CLR class file. When the Infinity user interface calls upon the CLR data list to display a list of data, the GetListResults() function is called on the .NET CLR class file. GetListResults() is responsible for gathering the data for the data list and returning the data as type Blackbaud.AppFx.Server.AppCatalog.AppDataListResult.

The CLR code will pull a status history from the BusinessProcessCatalog and BusinessProcessStatus tables for a given specific BusinessProcessCatalog.ID. Below is a sample Data List Spec. The CLR class assembly and class name are highlighted in yellow. The parameters passed to the class are highlighted in blue.  

<DataListSpec 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
	ID="7ff36611-ac9d-4ae2-a3f8-900fca2f24c2" 
	Name="Inventory Process Business Process Status List" 
	Description="Returns a status list for the inventory process businessbusiness process." 
	Author="Technical Training" 
	common:SecurityUIFolder="Constituent\Food Bank\Inventory" 
	xmlns:common="bb_appfx_commontypes" 
	xmlns="bb_appfx_datalist">
	
	<CLRDataList AssemblyName="Blackbaud.AppFx.Platform.Catalog" 
               ClassName="Blackbaud.AppFx.Platform.Catalog.BusinessProcessStatusDatalist">
		<common:GrantSelectList>
			<common:GrantSelect>BUSINESSPROCESSCATALOG</common:GrantSelect>
			<common:GrantSelect>BUSINESSPROCESSSTATUS</common:GrantSelect>
			<common:GrantSelect>APPUSER</common:GrantSelect>
		</common:GrantSelectList>
		<common:StaticParameters>
			<common:ParameterList>
				<common:Param ID="BusinessProcessCatalogID">
					<common:Value>a28d4f17-53fe-48d9-85bc-37e12884dc00</common:Value>
				</common:Param>
				<common:Param ID="ApplyParametersIDFilter">
					<common:Value>True</common:Value>
				</common:Param>
			</common:ParameterList>
		</common:StaticParameters>
	</CLRDataList>
	<Parameters>
		<common:FormMetaData>
			<common:FormFields>
				<common:FormField FieldID="StatusCode" Caption="Status">
					<common:ValueList>
						<common:Items>
							<common:Item>
								<common:Value>0</common:Value>
								<common:Label>Completed</common:Label>
							</common:Item>
							<common:Item>
								<common:Value>1</common:Value>
								<common:Label>Running</common:Label>
							</common:Item>
							<common:Item>
								<common:Value>2</common:Value>
								<common:Label>Did not finish</common:Label>
							</common:Item>
						</common:Items>
					</common:ValueList>
				</common:FormField>
			</common:FormFields>
		</common:FormMetaData>
	</Parameters>
	<Output>
		<OutputFields>
			<OutputField FieldID="ID" Caption="ID" IsHidden="true" DataType="Guid" />
			<OutputField FieldID="Process name" Caption="Process name" IsHidden="true" DataType="String" />
			<OutputField FieldID="Status" Caption="Status" DataType="String" />
			<OutputField FieldID="Status message" Caption="Status message" DataType="String" />
			<OutputField FieldID="Started by" Caption="Started by" DataType="String" />
			<OutputField FieldID="Started on" Caption="Started on" DataType="Date" />
			<OutputField FieldID="Ended on" Caption="Ended on" DataType="Date" />
			<OutputField FieldID="Duration" Caption="Duration" DataType="String" />
			<OutputField FieldID="Number of records processed" Caption="Number of records processed" DataType="String" />
			<OutputField FieldID="NumberOfExceptionRecords" Caption="Number of exception records" DataType="String" />
			<OutputField FieldID="Total count" Caption="Total count" IsHidden="true" DataType="String" />
			<OutputField FieldID="Server name" Caption="Server name" DataType="String" />
			<OutputField FieldID="ImageKey" Caption="ImageKey" IsHidden="true" DataType="String" />
			<OutputField FieldID="Completed" Caption="Completed" IsHidden="true" DataType="String" />
			<OutputField FieldID="EnableDownload" Caption="EnableDownload" IsHidden="true" DataType="String" />
			<OutputField FieldID="EnableLetterMailMerge" Caption="EnableLetterMailMerge" IsHidden="true" DataType="String" />
			<OutputField FieldID="EnableLabelMailMerge" Caption="EnableLabelMailMerge" IsHidden="true" DataType="String" />
		</OutputFields>
	</Output>
</DataListSpec>

Below is the code for the Blackbaud.AppFx.Platform.Catalog.BusinessProcessStatusDatalist class that does the heavy lifting of retrieving the status history data for the data list. Within the class, GetListResults() is responsible for gathering the data for the data list and returning the data as type Blackbaud.AppFx.Server.AppCatalog.AppDataListResult.

Imports Blackbaud.AppFx.Server
Imports System.Data.SqlClient

Public NotInheritable Class BusinessProcessStatusDatalist
    Inherits AppCatalog.AppDataList

    Public FilterBusinessProcessInstanceID As Guid = Guid.Empty 'this is passed from the filter panel
    Public BusinessProcessCatalogID As String = String.Empty
    Public StatusCode As Nullable(Of Integer)
    Public ApplyParametersIDFilter As Boolean = False
    Public RowsToReturn As Nullable(Of Integer)
    Public StartedDateRangeType As Nullable(Of Integer)
    Public StartedDateSpecificDate As Nullable(Of Date)

    Public Enum ProcessStatusImage
        check = 0
        businessprocessspec
        warning
        x_16
    End Enum

    'Private Const STATUSTABLESUFFIX As String = "STATUS"

    Private Enum FieldsList
        ID
        ProcessName
        Status
        StatusMessage
        StartedBy
        StartedOn
        EndedOn
        Duration
        NumberOfRecordsProcessed
        NumberOfExceptionRecords
        TotalCount
        ServerName
        ImageKey
        Completed
        EnableDownload
        EnableLetterMailMerge
        EnableLabelMailMerge
        'EnableMultipleLetterMailMerge deprecated
        ParameterSetName
        BusinessProcessCatalogID
        ParameterSetID
    End Enum

    Public Overrides Function GetListResults() As AppFx.Server.AppCatalog.AppDataListResult

        If BusinessProcessCatalogID = "=ContextID" Then
            BusinessProcessCatalogID = ProcessContext.ContextRecordID
        Else
            If FilterBusinessProcessInstanceID <> Guid.Empty Then
                BusinessProcessCatalogID = FilterBusinessProcessInstanceID.ToString()
            End If
        End If

        Dim builder As New Text.StringBuilder
        With builder
            .Append("select ID, ")
            .Append("ProcessName, ")
            .Append("Status, ")
            .Append("StatusMessage, ")
            .Append("StartedBy, ")
            .Append("StartedOn, ")
            .Append("EndedOn, ")
            .Append("Duration, ")
            .Append("NumberOfRecordsProcessed, ")
            .Append("NumberOfExceptionRecords, ")
            .Append("TotalCount, ")
            .Append("ServerName, ")
            .Append("ImageKey, ")
            .Append("Completed, ")
            .Append("EnableDownload, ")
            .Append("EnableLetterMailMerge, ")
            .Append("EnableLabelMailMerge, ")
            '.Append("EnableMultipleLetterMailMerge, ") deprecated
            .Append("coalesce(ParameterSetName,'') ")
            .Append("from dbo.UFN_BUSINESSPROCESSSTATUS_STANDARDDATALISTVALUES_ROWSTORETURNSTARTEDDATE(@BUSINESSPROCESSCATALOGID, @PARAMETERSETID, @STATUSCODE, @ROWSTORETURN, @STARTEDDATERANGETYPE, @STARTEDDATESPECIFICDATE) ")
            .Append("order by StartedOn desc")
        End With

        Dim command As New SqlCommand(builder.ToString)
        If ApplyParametersIDFilter Then

            Dim ctxID As String = Me.ProcessContext.ContextRecordID
            If String.IsNullOrEmpty(ctxID) Then
                Throw New ServiceException("The ContextRecordID must be specified for this BusinessProcessStatusDataList", ServiceErrorCode.InvalidAPIUse)
            End If

            command.Parameters.AddWithValue("@PARAMETERSETID", New Guid(ctxID))
        Else
            command.Parameters.AddWithValue("@PARAMETERSETID", DBNull.Value)
        End If

        If BusinessProcessCatalogID <> String.Empty Then
            command.Parameters.AddWithValue("@BUSINESSPROCESSCATALOGID", New Guid(BusinessProcessCatalogID))
        Else
            command.Parameters.AddWithValue("@BUSINESSPROCESSCATALOGID", DBNull.Value)
        End If

        AddNullableIntegerParameter(command, "@STATUSCODE", StatusCode)
        AddNullableIntegerParameter(command, "@ROWSTORETURN", RowsToReturn)
        AddNullableIntegerParameter(command, "@STARTEDDATERANGETYPE", StartedDateRangeType)

        AddNullableDateParameter(command, "@STARTEDDATESPECIFICDATE", StartedDateSpecificDate)

        Dim reader As SqlDataReader
        Dim resultList As New Generic.List(Of DataListResultRow)

        Using conn As SqlClient.SqlConnection = Me.RequestContext.OpenAppDBConnection
            SpWrap.USP_BUSINESSPROCESSSTATUS_VALIDATESTATUS.ExecuteNonQuery(conn)

            command.Connection = conn
            reader = command.ExecuteReader(CommandBehavior.CloseConnection)

            Do While reader.Read
                Dim result As New DataListResultRow

                Dim valueList As New Generic.List(Of String)
                valueList.Add(reader.GetGuid(FieldsList.ID).ToString)
                valueList.Add(reader.GetString(FieldsList.ProcessName))
                valueList.Add(reader.GetString(FieldsList.Status))
                valueList.Add(reader.GetString(FieldsList.StatusMessage))
                valueList.Add(reader.GetString(FieldsList.StartedBy))
                valueList.Add(FormatFieldForList(reader.GetDateTime(FieldsList.StartedOn)))

                If reader.IsDBNull(FieldsList.EndedOn) Then
                    valueList.Add(String.Empty)
                Else
                    valueList.Add(FormatFieldForList(reader.GetDateTime(FieldsList.EndedOn)))
                End If

                valueList.Add(reader.GetString(FieldsList.Duration))
                valueList.Add(reader.GetInt32(FieldsList.NumberOfRecordsProcessed).ToString)
                valueList.Add(reader.GetInt32(FieldsList.NumberOfExceptionRecords).ToString)
                valueList.Add(reader.GetInt32(FieldsList.TotalCount).ToString)
                valueList.Add(reader.GetString(FieldsList.ServerName))
                valueList.Add(reader.GetString(FieldsList.ImageKey))
                valueList.Add(reader.GetBoolean(FieldsList.Completed).ToString)
                valueList.Add(reader.GetBoolean(FieldsList.EnableDownload).ToString)
                valueList.Add(reader.GetBoolean(FieldsList.EnableLetterMailMerge).ToString)
                valueList.Add(reader.GetBoolean(FieldsList.EnableLabelMailMerge).ToString)
                valueList.Add(Boolean.FalseString) ' deprecated -- left in so as not to break dependent specs
                valueList.Add(reader.GetString(FieldsList.ParameterSetName).ToString)
                valueList.Add(BusinessProcessCatalogID)

                If ApplyParametersIDFilter Then
                    valueList.Add(Me.ProcessContext.ContextRecordID)
                End If

                result.Values = valueList.ToArray

                resultList.Add(result)

            Loop
        End Using

        Return New AppCatalog.AppDataListResult(resultList)

    End Function

    Private Sub AddNullableIntegerParameter(ByVal command As SqlClient.SqlCommand, ByVal parameterName As String, ByVal integerValue As Nullable(Of Integer))
        If Not integerValue.HasValue Then
            integerValue = -1
        End If

        command.Parameters.AddWithValue(parameterName, integerValue)
    End Sub

    Private Sub AddNullableDateParameter(ByVal command As SqlClient.SqlCommand, ByVal parameterName As String, ByVal dateValue As Nullable(Of Date))
        If dateValue.HasValue Then
            command.Parameters.AddWithValue(parameterName, dateValue.Value)
        Else
            command.Parameters.AddWithValue(parameterName, DBNull.Value)
        End If
    End Sub
End Class