Select your user interface:

Use AppFxWebService to Create a Batch from an Existing Batch Template

To create a batch, I clicked the Add action on the Uncommitted Batches tab of the Batch Entry page. This opens a data form. I selected the Sample Constituent Batch Template, which was created from the Constituent Batch Type. Finally, I entered a description for the new batch.

Figure: Add a new batch and prepare to spy

The next thing we want to do is to spy on the AppFxWebService. We want to capture the web service call to save the data for the data form. Before saving the data form, I enabled the web service logging utility by un-commenting the appropriate tag within the Blackbaud CRM web application's Web.config file. This file can be found within the Infinity web application's bbappfx\vroot folder.

Figure: The Web.config file.

I altered the value for the "logrequests" appsettings key. The "logrequests" appsettings key can be used to log requests to the dbo.WSREQUESTLOG table within SQL Server. If the value attribute equals "DataFormSaveRequest," then only these types of requests are logged into the table.

Figure: Uncomment the logrequests appsetting key

I save the data form to create the batch from the batch template. When I query the WSREQUESTLOG table, I see that my DataFormSaveRequest was logged to the table.

Figure: Log the activity within the WSREQUESTLOG table

A DataFormSaveRequest is a type of request made to the AppFxWebService to save the data from the data form into the database. The Infinity user interface uses the AppFxWebService for virtually all types of web service calls. AppFxWebService is web service facade for the Blackbaud Application Framework operations including data forms, batches, data lists, ad-hoc query, global changes, KPIs, record operations, etc.

For a complete list of the different types of calls and the different ways you can filter the results saved to the WSREQUESTLOG table, click the Endpoints link on the main Blackbaud CRM splash page.

Figure: Endpoints

Click the Blackbaud AppFx Web Service link to see all the different calls that can be made to this service.

Figure: Browse the appfxwebservice.asmx

As you can see, many different types of calls can be made to this web service. Not only data form calls …

… but also calls to manage batches

Figure: Various types of calls to manage batches via the AppFxWebService

In the columns of the WSREQUESTLOG table, we can see that the REQUESTXML and REPLYXML columns are of data type XML.

Figure: The WSREQUESTLOG table columns

So back to our WSREQUESTLOG query results, we can see that a call to DataFormSave was made.

Figure: Query the WSREQUESTLOG table

By clicking on the XML within the REQUESTXML column of the results, we can see the XML request made:

<DataFormSaveRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="Blackbaud.AppFx.WebService.API.1">
  <ClientAppInfo REDatabaseToUse="BBInfinityPROD2.7.1633.0" ClientAppName="WebShell" SessionKey="0a340c5e-55a7-4973-b5fa-f602782fad51" TimeOutSeconds="1200" ClientUICulture="en-us" ClientCulture="en-us" />
  <FormID>0197ed14-16bc-419b-8ba3-b81beb64b8f2</FormID>
  <FileUploadKey>463a15ba-e949-4a85-bf3e-49c5abe621f0</FileUploadKey>
  <DataFormItem>
    <Values xmlns="bb_appfx_dataforms">
      <fv ID="BATCHNUMBER">
        <Value xsi:type="xsd:string" />
      </fv>
      <fv ID="DESCRIPTION">
        <Value xsi:type="xsd:string">Sample Constituent Batch</Value>
      </fv>
      <fv ID="OWNERID" />
      <fv ID="BATCHTEMPLATEID">
        <Value xmlns:q1="http://microsoft.com/wsdl/types/" xsi:type="q1:guid">00a0e05b-2c04-4c33-be23-1e79bb21d789</Value>
      </fv>
      <fv ID="ORIGINATINGBATCHID" />
      <fv ID="PROJECTEDNUMBEROFRECORDS">
        <Value xsi:type="xsd:int">0</Value>
      </fv>
      <fv ID="PROJECTEDTOTALAMOUNT">
        <Value xsi:type="xsd:decimal">0</Value>
      </fv>
      <fv ID="CREATECUSTOMBATCH">
        <Value xsi:type="xsd:boolean">false</Value>
      </fv>
      <fv ID="FORMDEFINITIONXML">
        <Value xsi:type="xsd:string</Value>
      </fv>
      <fv ID="AUTOSAVEONROWCHANGE">
        <Value xsi:type="xsd:boolean">true</Value>
      </fv>
      <fv ID="OVERRIDEBATCHNUMBER">
        <Value xsi:type="xsd:boolean">false</Value>
      </fv>
    </Values>
  </DataFormItem>
  <SecurityContext>
    <SecurityFeatureID>0197ed14-16bc-419b-8ba3-b81beb64b8f2</SecurityFeatureID>
    <SecurityFeatureType>Form</SecurityFeatureType>
    <RecordContext />
  </SecurityContext>
</DataFormSaveRequest>

We can use the XML above to gain some insight into the code we must right to automate the data form save that will create a batch from a batch template. In a way, thanks to the XML, the web service call is almost self-documenting. We know that we must submit some sort of DataFormSaveRequest from the AppFxWebService. We know that we must accurately pinpoint the data form that is used to save the data. Luckily, the yellow highlight shows us the appropriate data form. The GUID in between the FormID tags represents the data form instance ID of the data form.

<FormID>0197ed14-16bc-419b-8ba3-b81beb64b8f2</FormID>

Next, by looking that the <DataFormItem>, we can deduce that we need to construct a payload that contains the form field values used to create the batch. Looking within DataFormItem payload, we see the <Values xmlns="bb_appfx_dataforms"> that contains a collection of <fv> tags.

This <fv> tag contains the description of the new batch:

      <fv ID="DESCRIPTION">
        <Value xsi:type="xsd:string">Sample Constituent Batch</Value>
      </fv>

This <fv> tag contains the identifier of the batch template used to create the batch:

<fv ID="BATCHTEMPLATEID">
        <Value xmlns:q1="http://microsoft.com/wsdl/types/"   xsi:type="q1:guid">00a0e05b-2c04-4c33-be23-1e79bb21d789</Value>
</fv>

Let's move back to our WinForm code that we started in our previous section. We can begin to construct the appropriate request from the AppFxWebService.

First, let's add a reference to System.Web.Services to support calls that will be made directory to the AppFxWebService. Make sure your project has the following references:

Figure: Add a reference to System.Web.Services.dll

Next, we will create a new VB class file named BatchHelper. This class is used as a façade to insulate the WinForm code from the complexities of interacting with the Infinity web server.

Figure: Create a BatchHelper facade class

We will make calls to BatchHelper from our WinForm user interface (Form1). BatchHelper will contain a CreateBatchFromTemplate function to add a new batch for a given batch template. We will simply call the function and pass the GUID of the batch template, the batch description, and other relevant data needed to create a batch. Here is the call from the user interface. When we instantiate a new BatchHelper object we send the URL base path, database name, and application title as the arguments for the constructor (green highlights below). Next we call the batch helper's CreateBatchFromTemplate procedure passing the GUID of the batch template ID and the batch description (yellow highlight).

Public Class Form1
    'update the values for your system settings
    Const serviceUrlBasePath As String = "http://localhost/BBInfinity/"
    Const databaseName As String = "BBInfinity"
    Const applicationTitle As String = "BatchWebAPISample"

    Private _helper As BatchHelper
    Private _currentBatchID As System.Guid
    Private _batchRowSequence As Integer = 0

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Initialize()
    End Sub

    Private Sub cmdAddConstitBatch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdAddConstitBatch.Click
      'Pass the identifier for the appropriate batch template.
'Note:  this code assumes the batch template has already been    created.
        'The batch template is based upon the constituent batch type.
       'Create your own batch template from the constituent batch type and 'lookup the batch template id within the BATCHTEMPLATE table.  Or you can use 'WSREQUESTLOG to sniff out the batch template id by logging the call to the 'BatchSaveRequest from the AppFxWebService.  

        _currentBatchID = _helper.CreateBatchFromTemplate(New System.Guid("00a0e05b-2c04-4c33-be23-1e79bb21d789"), Me.txtBatchDescription.Text)

        Me.grpAddress.Enabled = True
        Me.cmdNewBatchRow.Enabled = True
    End Sub

    Private Sub Initialize()
        _helper = New BatchHelper(serviceUrlBasePath, databaseName, applicationTitle) 

As we look within the BatchHelper class, we see the procedures that create a batch: CreateBatchFromTemplate and DataFormSave. CreateBatchFromTemplate wraps a call to DataFormSave, which creates the batch from the batch template. CreateBatchFromTemplate provides the data form instance ID of the data form that saves the data and a generic.dictionary of values that represent the form field payload that this particular data form requires to create a batch.

Public Function CreateBatchFromTemplate(ByVal BatchTemplateID As System.Guid, ByVal BatchDescription As String) As System.Guid

        Dim DataFormValues As New Generic.Dictionary(Of String, String)

        'add the batch template id and description into the values in the generic.dictionary for the payload
        DataFormValues.Add("BATCHTEMPLATEID", BatchTemplateID.ToString)
        DataFormValues.Add("DESCRIPTION", BatchDescription)

        'The first parameter passed to DataFormSave is the data form instance Id of 
        'the data form that creates a new batch from a specified batch template.  
        'The second parameter is a generic.dictionary of form field values used as the payload
        'included in the payload is the batch template id.
        Return DataFormSave(New System.Guid("0197ed14-16bc-419b-8ba3-b81beb64b8f2"), DataFormValues)

End Function

If we save data, then the request contains the data to save (yellow highlight below). We create a save request object (green highlight), provide the payload containing the data to the request object and then attempt to save the data. In the data form below, we can see the creation of the DataFormItem which is the payload that is sent along with the data form save request. We catch the newly created batch ID from the save operation (red highlight).

Public Function DataFormSave(ByVal FormID As System.Guid, ByVal DataFormValues As Generic.Dictionary(Of String, String)) As System.Guid

        'This function accepts the data form instance id of a data form and 
        'a dictionary of form field keys and values.

        'The DataFormItem is the payload that is sent as part of the DataFormSaveRequest
        'We will place a list of data form field values within this object.
        Dim DataFormItem As New Blackbaud.AppFx.XmlTypes.DataForms.DataFormItem

        'We will convert the generic.dictionary of form field values into a 
        'generic.list of DataFormFieldValues which is used within AddRange a few lines of code below.
        Dim DataFormFieldValueList As New Generic.List(Of Blackbaud.AppFx.XmlTypes.DataForms.DataFormFieldValue)
        For Each DataFormValueKey In DataFormValues.Keys
            DataFormFieldValueList.Add(New Blackbaud.AppFx.XmlTypes.DataForms.DataFormFieldValue(DataFormValueKey, DataFormValues(DataFormValueKey)))
        Next

        'AddRange accepts an IList of dataformfieldvalues.  This will provide the 
        'DataFormSaveRequest with its payload of form field values to save.  
        DataFormItem.Values.AddRange(DataFormFieldValueList)

        'Get a new save request and provide the request with the payload (DataFormItem)
        Dim req As Blackbaud.AppFx.WebAPI.ServiceProxy.DataFormSaveRequest = Blackbaud.AppFx.WebAPI.DataFormServices.CreateDataFormSaveRequest(_provider, FormID)
        req.DataFormItem = DataFormItem

        'save the data (data form save request) and catch the new batch id that is created as part of the 
        'save into BatchID.  Check out the Batch table within the database.  If all goes well then at this
        'point we have created a new batch from a batch template.  
        Dim BatchID As String
        BatchID = Blackbaud.AppFx.WebAPI.DataFormServices.SaveData(_provider, req)

        'Return the batch id from the function.  
        'Our code can use the batch id later when it comes time to add data to the new batch.
        Return New System.Guid(BatchID)

    End Function

Let's check out the user interface. Below you can see the text box containing the batch description. You can click the Create Constituent Batch button to create a batch. Note that this code assumes the batch template was already created. The batch template is based upon the constituent batch type. Create your own batch template from the constituent batch type and look up the appropriate batch template ID within the BATCHTEMPLATE table. Or you can use WSREQUESTLOG to sniff out the batch template ID by logging the call to the BatchSaveRequest from the AppFxWebService.

Figure: The custom WinForm user interface used to add a new batch and add rows to the batch.

After we click the button, we can see the new batch and batch description within the Infinity shell.

We can also review the BATCH table within the database to see the row saved by the data form.

Figure: New database row