Making your application robust

This page describes techniques for creating a robust application that responds appropriately to result codes and status codes received from QuickBooks. It covers the following major topics:

Error codes

Your application needs to deal with three levels of error codes:

To help you create an application that integrates successfully with QuickBooks, this chapter provides some pointers on particular result codes and status codes that are especially important to monitor. This material is meant to be a starting point for how your application should handle certain error conditions. Beyond that, of course, are many application- specific concerns that cannot be anticipated here.

Communications-level codes: monitoring HRESULTs and HTTP errors

Error codes related to communicating with QuickBooks must be monitored and handled appropriately. The section in this chapter called “Synchronizing Data between Your Application and QuickBooks” describes a general strategy for implementing an error recovery routine that is invoked each time your application starts up. The error recovery routine checks for possible processing problems in the previous session. It is also invoked whenever certain error codes having to do with processing or communication problems are generated.

An example of an HRESULT returned by the qbXML Request Processor is:

0x80040416: If QuickBooks is not running, the company data file name must be supplied to BeginSession.

Status codes in response messages lists the HRESULTs returned by the QBFC COM API, which are very similar to those sent by the qbXML Request Processor API.

Message-set level codes: monitoring message set status codes

This status code deals with the message set as a whole and is returned in response to a status check or clear status. It is also returned if some specific error recovery operation is invoked and fails, such as a standard check for a valid message set ID.

Response-level codes: monitoring status codes

Status codes (and their accompanying status message and status severity) are included in the response for each request. This chapter provides special sections describing how your application might deal with status codes related to versioning issues, and with status codes related to problems in synchronizing company data between your application and QuickBooks.

An example of a status code is:

1
<CustomerAddRs requestID=”2” status code=”3231” statusSeverity=”Error” statusMessage=”The request has not been processed.”/>

Status codes in response messages contains relevant error/status codes. It lists the status codes, status messages, and status severity, which are all returned as attributes in the response message for every request,

Using the log file

When your application integrates with QuickBooks, QuickBooks creates a log file (qbsdklog.txt)in the common files directory.

As your application interacts with QuickBooks, three categories of information are logged:

In some cases, the log file can help you to identify specific problematic fields (elements) in a request that raised an error. Sometimes the message will refer to a field or element that borders an area in error. This log information might occur, for instance, if the error occurred because a required field is missing. The following table shows an example of a log file.

Date and time Level PID SDK component Event message
20011107.145109 I 1884 RequestProcessor Started Connection
20011107.145109 I 1884 RequestProcessor Connection opened by app named ‘SdkTest’
20011107.145109 I 1884 CertVerifier The file does not contain an Authenticode signature.
20011107.145109 I 1884 RequestProcessor Opening the file in the DoNotCare mode.
20011107.145120 I 1936 QBSDKProcessRequest Application named ‘SdkTest’ starting requests (process 1884).
20011107.145122 I 1936 QBSDKMsgSetHandler QUERY: Invoice
20011107.145128 I 1936 QBSDKMsgSetHandler Request 234578 completed successfully.
20011107.145128 I 1936 MsgSetHandler Finished.
20011107.145128 I 1936 QBSDKProcessRequest Application named ‘SdkTest’ finishing requests (process 1884), ret = 0.
20020219.144648 I 393 SpecVersion Current version of qbXML in use: 3.0
20011107.145129 I 1884 RequestProcessor Connection closed by app named ‘SdkTest’
20011107.145129 I 1884 RequestProcessor Ended Connection
Software versions

The following checklist summarizes important tasks your application must address in order to work with multiple versions of QuickBooks:

  1. Handle an error creating the qbXML Request Processor object (for example, an HRESULT of 0x80040404: The version of QuickBooks currently running does not support qbXML.).
  2. After a session has begun, check the qbXML specification supported by the qbXML Request Processor object. (See “Checking the QuickBooks Version.”)
  3. Reference the appropriate qbXML prolog for that specification. (See “Checking the QuickBooks Version.”)
  4. Guard newer features to prevent their running against an older QuickBooks version. Be sure to consider both requests and responses. (Note that the prolog applies to all requests contained within the XML stream. You can’t mix 1.0, 1.1, 2.0, 2.1, and 3.0 requests in one stream.) (See “Dealing with Unsupported Features.”)
Checking the QuickBooks version

At runtime, your application needs to determine what versions of qbXML are supported by the version of QuickBooks that is processing your requests. Once it determines the current version, it should then load the appropriate prolog citing the correct version of the qbXML specification. (Or, if your application is using QBFC, it should create a message set request that corresponds to the correct version of the QuickBooks Request Processor.) The following two code examples (one for qbXML and one for QBFC) illustrate the tasks your application must complete in order to accommodate any version of QuickBooks that might be running when your application is launched.

Example in qbXML

The following code excerpt contains two functions. The first function (qbXMLLatestVersion) shows using the HostQuery function to obtain the supported versions of the QuickBooks Request Processor that are currently running. It then loops through the versions to determine the highest supported version.

The second function (qbXMLAddProlog) then adds the qbXML prolog that corresponds to this latest version of the QuickBooks Request Processor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
Function qbXMLLatestVersion(rp As requestProcessor, ticket As String) As String
Dim strXMLVersions() As String

'Create a DOM document object for creating our request.
Dim xml As New DOMDocument

'Create the QBXML aggregate Dim rootElement As IXMLDOMNode
Set rootElement = xml.createElement("QBXML")
xml.appendChild rootElement

'Add the QBXMLMsgsRq aggregate to the QBXML aggregate
Dim QBXMLMsgsRqNode As IXMLDOMNode
Set QBXMLMsgsRqNode = xml.createElement("QBXMLMsgsRq")
rootElement.appendChild QBXMLMsgsRqNode
Dim onErrorAttr As IXMLDOMAttribute
Set onErrorAttr = xml.createAttribute("onError")
onErrorAttr.Text = "stopOnError"
QBXMLMsgsRqNode.Attributes.setNamedItem onErrorAttr

'Add the HostQuery aggregate to QBXMLMsgsRq aggregate
Dim HostQuery As IXMLDOMNode
Set HostQuery = xml.createElement("HostQueryRq")
QBXMLMsgsRqNode.appendChild HostQuery

'Add a lowest-common-denominator prolog' Dim strXMLRequest As String strXMLRequest = _
"

<?xml version=""1.0"" ?>" & _
"

<!DOCTYPE QBXML PUBLIC '-//INTUIT//DTD QBXML QBD 1.0//EN'_
'http://developer.intuit.com'>" _ & rootElement.xml
Dim strXMLResponse As String
strXMLResponse = rp.ProcessRequest(ticket, strXMLRequest)
Dim QueryResponse As New DOMDocument

'Parse the response XML
QueryResponse.async = False QueryResponse.loadXML (strXMLResponse)
Dim supportedVersions As IXMLDOMNodeList
Set supportedVersions = QueryResponse.getElementsByTagName("SupportedQBXMLVersion")
Dim VersNode As IXMLDOMNode
Dim i As Long
Dim vers As Double
Dim LastVers As Double
LastVers = 0
For i = 0 To supportedVersions.length - 1
Set VersNode = supportedVersions.Item(i)
vers = VersNode.firstChild.Text
If (vers > LastVers)
   Then LastVers = vers
   qbXMLLatestVersion = VersNode.firstChild.Text
End If
Next i
End Function

'Get an XML prolog that is appropriate for the latest version of qbXML
Function qbXMLAddProlog(supportedVersion As String, xml As String) As String
Dim qbXMLVersionSpec As String
If (Val(supportedVersion) >= 2)
Then qbXMLVersionSpec = "
   <?qbxml version=""" & supportedVersion & """?>"
ElseIf (supportedVersion = "1.1") Then
   qbXMLVersionSpec= "
      <!DOCTYPE QBXML PUBLIC '-//INTUIT//DTD QBXML QBD " _
      & supportedVersion & "//EN' 'http://developer.intuit.com'>"
Else
   MsgBox "You are apparently running QuickBooks 2002 Release 1, we strongly
   recommend that you use QuickBooks' online update
   feature to obtain the latest fixes and enhancements", vbExclamation
   qbXMLVersionSpec = "

      <!DOCTYPE QBXML PUBLIC '-//INTUIT//DTD QBXML QBD "
      _
   & supportedVersion & "//EN' 'http://developer.intuit.com'>"
End If

qbXMLAddProlog = "

<?xml version=""1.0""?>" & vbCrLf & qbXMLVersionSpec & xml
End Function
Example in QBFC

This example parallels the preceding example, but uses QBFC syntax. The first function (QBFCLatestVersion) determines the highest version of QuickBooks that is currently running. The second function (GetLatestMsgSetRequest) creates a message set request for the current version of QuickBooks.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Function QBFCLatestVersion(SessionManager As QBSessionManager) As String
Dim strXMLVersions() As String
Dim msgset As IMsgSetRequest

'Use oldest version to ensure that we work with any QuickBooks (US)
Set msgset = SessionManager.CreateMsgSetRequest(1, 0)
msgset.AppendHostQueryRq
Dim QueryResponse As QBFC2Lib.IMsgSetResponse
Set QueryResponse = SessionManager.DoRequests(msgset)
Dim response As QBFC2Lib.IResponse

' The response list contains only one response,
' which corresponds to our single HostQuery request
Set response = QueryResponse.ResponseList.GetAt(0)
Dim HostResponse As IHostRet
Set HostResponse = response.Detail Dim supportedVersions As IBSTRList
Set supportedVersions = HostResponse.SupportedQBXMLVersionList
Dim i As Long
Dim vers As Double
Dim LastVers As Double LastVers = 0
For i = 0 To supportedVersions.Count - 1 vers = Val(supportedVersions.GetAt(i))
If (vers > LastVers) Then
   LastVers = vers
   QBFCLatestVersion = supportedVersions.GetAt(i)
End If
Next i
End Function

'QBFC: Get a MsgSetRequest that supports the latest possible version
'of qbXML
Public Function GetLatestMsgSetRequest(SessionManager As QBSessionManager) As IMsgSetRequest
Dim supportedVersion as String
supportedVersion = QBFCLatestVersion(SessionManager)

If (Val(supportedVersion) >= 3) Then
Set GetLatestMsgSetRequest = SessionManager.CreateMsgSetRequest("US", 3, 0) addr4supported = True
ElseIf (Val(supportedVersion) >= 2) Then
Set GetLatestMsgSetRequest = SessionManager.CreateMsgSetRequest("US", 2, 0) addr4supported = True
ElseIf (Val(supportedVersion) = 1.1) Then
Set GetLatestMsgSetRequest = SessionManager.CreateMsgSetRequest("US", 1, 1)
Else
MsgBox "You are apparently running QuickBooks 2002 Release 1, we strongly recommend that you use QuickBooks' online update feature to obtain the latest fixes and enhancements", vbExclamation
Set GetLatestMsgSetRequest = SessionManager.CreateMsgSetRequest("US", 1, 0)
End If
Dealing with unsupported features

If your application takes advantage of features added in later versions of QuickBooks that are unsupported in earlier versions (for example, transaction Modify requests), it needs to control what will happen when it is running against one of the earlier versions of QuickBooks that does not contain the later functionality.

Branching when versions differ

The following QBFC function is an example of a useful way to test for support of a particular version and then branch according to the version support currently available. For example, you could place this function at the beginning of the application and then check the Boolean value set by the function when support for Version 2.0 is required. The CustomerAdd example included in the QuickBooks SDK uses this function to check whether Version 2.0 is supported. If it is, the application sets the value for Addr4 (a 2.0 feature). If not, it does not set the Addr4 value.

1
2
3
4
5
Dim booSupports2dot0 as boolean booSupports2dot0 = False
Dim supportedVersion As String
supportedVersion = QBFCLatestVersion(SessionManager)
If (val(supportedVersion) >= 2.0) Then booSupports2dot0 = True
Set requestSet = SessionManager.CreateMsgSetRequest("US",3,0) End If
Alerting the user to version issues

If your application requires use of a new feature, it should display an appropriate error message and deal gracefully with earlier versions of QuickBooks that do not contain the feature. The following qbXML example shows checking the version and exiting when appropriate QuickBooks support is unavailable.

1
2
3
4
5
6
7
8
9
   Dim supportedVersion As String
   supportedVersion = qbXMLLatestVersion(qbXMLRP, ticket)
   If (val(supportedVersion) < 2.0)
   Then
   MsgBox "This sample requires support for qbXML 2.0 or later (QuickBooks 2003 or later) " & _
   "Expect a parsing error when attempting to send requests to QuickBooks",
   _
   vbExclamation
   End If

In most cases, an actual application would not need to take the drastic measure of exiting; it would deal gracefully with the version issues and alert the user that it cannot integrate successfully with QuickBooks.

Error recovery

An important feature of every robust application is error recovery. Details on implementing error recovery are provided in Error recovery.

Synchronizing data between your application and QuickBooks

If your application maintains a database or internal file that stores portions of data from the QuickBooks company file, the application needs to ensure that the two sets of data are synchronized with each other. Event notification, described in Subscribing to events and processing event notifications, is a useful way to maintain synchronization.

There are two main actions that lead to a lack of synchronization:

In the first case, your application might need to update the QuickBooks version of the company file to reflect the changes made by your application to that data. Before you update the QuickBooks file, be sure to query the user and obtain his or her approval.

In the second case, your application would need to update its own database to reflect the changes made in the QuickBooks company file.

Monitor status codes

The following status codes indicate a problem with data synchronization. Your application also needs to check for these status codes and take appropriate action if it receives them.

1
   > 3120 Object not found. Object <value> specified in the request cannot be found.

Your application attempted to modify an object that it previously added to QuickBooks or that it obtained using a Query request, and the application received an error stating that the object doesn’t exist. At this point, your application might enter its synchronization routine to attempt to determine whether the object was intentionally deleted by the user or whether the company file was restored.

1
   > 3140 Reference not found or 3130 Parent reference not found

If your application received one of these messages and it had previously referenced the object successfully, there is the possibility that the object was intentionally deleted or that the company file was restored. Your application would need to take some action to ensure that the error condition didn’t occur again because the referenced object no longer exists.

1
   > 3240 Time creation mismatch. File has been restored.

If the company file was restored, your application could receive this message, for example, after issuing a modify request for an object that it previously added, specifying the ListID assigned to the object and returned by QuickBooks. Because the user restored a version the company file that pre-dates addition of the object, the ListID (and the object it pertains to) do not exist. This message unambiguously indicates that the user restored the company file. Your application needs to take appropriate action to re-sync its data with QuickBooks.

Example of synchronizing data with QuickBooks

The following example outlines a useful procedure for keeping data in sync, based on the following requests:

Suppose an application requires a list of active customers and needs to keep this list in sync with the list contained in the QuickBooks company file. The application uses the ListID as the primary key of the customer records because the ListID, unlike the FullName, is guaranteed not to change.

The first time the application connects to QuickBooks, it needs to obtain the entire active customer list. The following figure shows the steps for using this request to synchronize the data. Immediately before this request, the application needs to obtain and record the date and time of this action. In this example, this date/time is referred to as the last sync datetime.

../../../_images/Image_667.jpg

The following example shows the request:

1
2
3
   <CustomerQueryRq requestID = "UUIDTYPE">
   <ActiveStatus>ActiveOnly</ActiveStatus>
   </CustomerQueryRq>

Note that since the default ActiveStatus value is Active Only, that field could actually have been omitted from the above.

Periodically, the application needs to re-sync its data with QuickBooks.

To do so, it first issues a Company ActivityQueryRq to obtain the LastRestoreTime:

Request that checks for customers that have been added, modified, or deleted:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
   <CustomerQueryRq requestID = "1">
      <ActiveStatus>All</ActiveStatus>
      <FromModifiedDate> last sync datetime</FromModifiedDate>
   </CustomerQueryRq>
   <ListDeletedQueryRq requestID = "2">
      <ListDelType>Customer</ListDelType>
      <DeletedDateRangeFilter>
         <FromDeletedDate> last sync datetime </FromDeletedDate>
      </DeletedDateRangeFilter>
   </ListDeletedQueryRq>

Based on the CustomerQuery response, the application needs to add or refresh existing customer records, matching ListIDs from the response to the cached list. Note that the response also contains the Inactive customers so that the customers that have changed status from Active to Inactive can be taken out of the list. The Inactive customers that are returned and not found in the cached list should be ignored, since the sample shows keeping the active customer list in sync. Next, based on the ListDeletedQuery response, the application needs to remove customer records that are no longer necessary.

Three-Month limit for ListDeletedQueryRq

There are a few considerations and limitations to the synchronization process described above. Be aware that ListDeletedQueryRq returns only elements deleted for the previous three months. If the most recent re-sync happened more than three months ago, a full synchronization is necessary.

Modification time

Unlike single-user mode, in multi-user mode, the QuickBooks recorded modification time is the time of the system where the data file resides, not the time of the system where QuickBooks is running. The clocks on these two systems (that is, the clock on the system where the data file resides and the clock on the system that runs the QuickBooks executable file and runs the SDK requests) may be out of sync. To account for such differences, it is strongly recommended that you expand the query time range and move the “last sync datetime” a few hours in the past. The application needs to be prepared to deal with response duplicates that have already been taken into account in the previous sync as a result of this time overlap.

Cases needing complete re-sync

Depending on what data you want to keep in sync, you may need to re-fetch the entire list every time you want to re-sync because the list of list objects or transactions that have been modified since the given “last sync datetime” is not accurate.

Examples are:

Check with the user

Your application should always interact with the user before modifying the QuickBooks version of the company file as a result of synchronization problems. How you do this is application-dependent.

If your application discovers that the QuickBooks company file has been restored, then you might want to prompt the user further to inquire whether the user would like you to update the company file in QuickBooks to reflect the more recent changes stored in your application’s database.This interaction lets the user know about new information that might otherwise be confusing.

For instance, you might present a window that includes a question similar to this one: