Select your user interface:
Use the AppFxWebService to Add Rows to an Existing Batch
In the previous example, we created a batch from a batch template. We have a batch template named "Sample Constituent Batch Template" that is based on the batch type named "Constituent Batch."
Figure: Batch template
We created a batch from the template. The batch contains two rows from prior activity within the Infinity shell user interface. I am adding a third row to the batch using the shell UI. The third row has been added to the batch UI grid but has not yet been saved.
Figure: Add a third row to the batch
I have turned the web service logging utility by un-commenting the appropriate tag within the Blackbaud CRM web application's Web.config file. This file is located in the application's bbappfx\vroot folder.
Figure: Modify Web.config
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 "BatchSaveRequest," then only these types of requests are logged into the table.
Figure: Log batch save requests
Saving the row within the batch to the database yields the following results within the WSREQUESTLOG table in the Blackbaud CRM database on SQL Server:
SELECT TOP 1000 [CLIENTAPP] ,[REQUESTNAME] ,[REQUESTXML] ,[REPLYXML] ,[ERRORMSG] FROM [BBInfinity].[dbo].[WSREQUESTLOG]
Figure: Use WSREQUESTLOG to spy on the web service
The REQUESTXML and REPLYXML columns in the WSREQUESTLOG table are of data type XML.
Figure: WSREQUESTLOG columns
Selecting the value in the REQUESTXML column within the results displays the request XML message sent to the AppFxWebService from the Blackbaud CRM client user interface (shell).
<BatchSaveRequest 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="Blackbaud Enterprise CRM" SessionKey="52a33b11-7ed8-4bf7-8fde-cacadd0820b0" TimeOutSeconds="120" /> <BatchID>b104b77d-a82b-45e3-bda6-f3377756ec1a</BatchID> <Rows> <BatchDataRow> <ID>fa129d43-018a-40bf-aeb2-e07e6b60cd5e</ID> <AddRow>true</AddRow> <DataFormItem> <Values xmlns="bb_appfx_dataforms"> <fv ID="ISORGANIZATION"> <Value xsi:type="xsd:string">0</Value> </fv> <fv ID="KEYNAME"> <Value xsi:type="xsd:string">Jefferson</Value> </fv> <fv ID="ADDRESS_ADDRESSTYPECODEID"> <Value xmlns:q1="http://microsoft.com/wsdl/types/" xsi:type="q1:guid">e5403bad-5b97-4644-aade-ad612a48ddba</Value> </fv> <fv ID="ADDRESS_COUNTRYID"> <Value xmlns:q1="http://microsoft.com/wsdl/types/" xsi:type="q1:guid">d81cef85-7569-4b2e-8f2e-f7cf998a3342</Value> </fv> <fv ID="ADDRESS_ADDRESSBLOCK"> <Value xsi:type="xsd:string">101 Home Sweet Home Lane</Value> </fv> <fv ID="ADDRESS_CITY"> <Value xsi:type="xsd:string">Santa Monica</Value> </fv> <fv ID="ADDRESS_STATEID"> <Value xmlns:q1="http://microsoft.com/wsdl/types/" xsi:type="q1:guid">5c72589b-3898-4760-880c-4189cd8c5f7d</Value> </fv> <fv ID="ADDRESS_POSTCODE"> <Value xsi:type="xsd:string">90401</Value> </fv> <fv ID="SEQUENCE"> <Value xsi:type="xsd:int">1</Value> </fv> <fv ID="SPOUSE_RELATIONSHIPTYPECODEID" /> <fv ID="SPOUSE_RECIPROCALTYPECODEID" /> <fv ID="PRIMARYBUSINESS_RELATIONSHIPTYPECODEID" /> <fv ID="PRIMARYBUSINESS_RECIPROCALTYPECODEID" /> <fv ID="EDUCATIONALINVOLVEMENT" /> <fv ID="TAXDECLARATIONS" /> <fv ID="SOLICITCODES" /> </Values> </DataFormItem> <ExceptionMessageTypeCode>GeneralError</ExceptionMessageTypeCode> <ClearUserMessage>false</ClearUserMessage> <ClearSystemMessage>false</ClearSystemMessage> <IgnoreDuplicate>false</IgnoreDuplicate> </BatchDataRow> </Rows> <DeletedRows /> </BatchSaveRequest>
With a little work we can translate the XML above into the code necessary within a new custom client application. Within the BatchHelper.vb class file created in the previous exercise, a new function is created that accepts a BatchID, a set of values to save which represent a new batch row, and a sequence number of where that row should be placed amongst the other batch rows.
Public Function BatchSaveRequest(ByVal BatchID As System.Guid, ByVal BatchRowValues As Generic.Dictionary(Of String, String), ByVal BatchRowSequence As Integer) As System.Guid
First we create a BatchSaveRequest object.
Dim req As New Blackbaud.AppFx.WebAPI.ServiceProxy.BatchSaveRequest
Then we create a reply object to catch the results of saving the batch row.
Dim reply As New Blackbaud.AppFx.WebAPI.ServiceProxy.BatchSaveReply
Next we make a call to a helper function GetRequestHeader(), which points us to the correct Infinity database.
req.ClientAppInfo = GetRequestHeader()
Next we set the BatchID on the request object to appropriately identify the batch created from the batch template. This ID can be found within the BATCH table in the database.
req.BatchID = BatchID
Next we create a BatchDataRow to represent our batch row and the column values. We also declare an array of the same data type. We are only adding one row, so we have dimensioned the array to hold a single row. We will provide a new GUID for the row ID and designate that this is a new row to be added to the batch.
Dim BatchDataRow As New Blackbaud.AppFx.WebAPI.ServiceProxy.BatchDataRow Dim BatchDataRows(0) As Blackbaud.AppFx.WebAPI.ServiceProxy.BatchDataRow BatchDataRow.ID = System.Guid.NewGuid.ToString BatchDataRow.AddRow = True
We will convert the generic.dictionary of form field values passed into our function into a generic.list of DataFormFieldValues which is used within AddRange a few lines of code below.
Dim DataFormItem As New Blackbaud.AppFx.XmlTypes.DataForms.DataFormItem Dim DataFormFieldValueList As New Generic.List(Of Blackbaud.AppFx.XmlTypes.DataForms.DataFormFieldValue) For Each DataFormValueKey In BatchRowValues.Keys DataFormFieldValueList.Add(New Blackbaud.AppFx.XmlTypes.DataForms.DataFormFieldValue(DataFormValueKey, BatchRowValues(DataFormValueKey))) Next
We will add a final DataFormFieldValue to represent the row sequence:
DataFormFieldValueList.Add(New Blackbaud.AppFx.XmlTypes.DataForms.DataFormFieldValue("SEQUENCE", BatchRowSequence))
AddRange accepts an IList of dataformfieldvalues. This will provide the DataFormSaveRequest with its payload of form field values to save.
DataFormItem.Values.AddRange(DataFormFieldValueList)
Next we add the DataFormItem that contains our row values to the BatchDataRow and then we add the BatchDataRow to the array and finally add the array to the request object.
BatchDataRow.DataFormItem = DataFormItem BatchDataRow.ExceptionMessageTypeCode = Blackbaud.AppFx.WebAPI.ServiceProxy.BatchMessageType.GeneralError BatchDataRow.ClearUserMessage = False BatchDataRow.ClearSystemMessage = False BatchDataRow.IgnoreDuplicate = False BatchDataRows(0) = BatchDataRow req.Rows = BatchDataRows
Next we call BatchSave using an object of type Blackbaud.AppFx.WebAPI.ServiceProxy.AppFxWebService and catch the reply. We can inspect the reply object for exceptions, if there are any we throw a custom exception containing the errors.
reply = _service.BatchSave(req) If Not reply.Exceptions Is Nothing Then If reply.Exceptions.Count > 0 Then Dim ex As New BatchException("Batch Save Error") ex.BatchSaveReply = reply Throw ex Exit Function End If End If End Function
Now let's see the BatchSaveRequest in action within our custom user interface. We provide the constituent and address information and select the Add Row To Batch button to call BatchSaveRequest (yellow highlight below).
Private Sub cmdAddRowToBatch_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdAddRowToBatch.Click
Dim SaveRequestGUID As System.Guid
Dim BatchRowValues As New Generic.Dictionary(Of String, String)
Dim ISORGANIZATION As System.Int16
Try
If Me.cboConstituentType.Text = "Individual" Then
ISORGANIZATION = 0
Else
ISORGANIZATION = 1
End If
BatchRowValues.Add(Me.cboConstituentType.Tag.ToString, ISORGANIZATION.ToString)
BatchRowValues.Add(Me.txtKeyName.Tag.ToString, Me.txtKeyName.Text.ToString)
BatchRowValues.Add(Me.cboAddressTypeCodeID.Tag.ToString, CType(Me.cboAddressTypeCodeID.SelectedItem, SimpleAddressType).AddressType.ToString)
BatchRowValues.Add(Me.txtAddressBlock.Tag.ToString, Me.txtAddressBlock.Text.ToString)
BatchRowValues.Add(Me.txtCity.Tag.ToString, Me.txtCity.Text.ToString)
BatchRowValues.Add(Me.cboStateID.Tag.ToString, CType(Me.cboStateID.SelectedItem, SimpleState).StateID.ToString)
BatchRowValues.Add(Me.cboCountryID.Tag.ToString, CType(Me.cboCountryID.SelectedItem, SimpleCountry).CountryID.ToString)
BatchRowValues.Add(Me.txtPostCode.Tag.ToString, Me.txtPostCode.Text.ToString)
_batchRowSequence += 1
SaveRequestGUID = _helper.BatchSaveRequest(_currentBatchID, BatchRowValues, _batchRowSequence)
ClearBatchRow()
cmdAddRowToBatch.Enabled = CheckEnableAddRowToBatchButton()
Catch ex As BatchException
MsgBox(ex.BatchErrorMessage, MsgBoxStyle.Exclamation)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Figure: Adding a new row from our custom user interface
We then can inspect the row within the batch in Blackbaud CRM.
Figure: Access the new row
Figure: The new row