I am looking to sync all Quickbooks vendors to my web application, however I'm running into an issue where I can only get 10 results (not the 5500 that I need) It sends the requests to QWC, gets 10 requests back, then runs again for requests updated in since the last run (seconds ago) rather than continuing to sync the remaining vendors. I'm sure I'm missing something important. I get that it syncs 10 at a time, but shouldn't it continue to get 10 at a time until there are no more to get?
My code is largely based off of this example: https://github.com/consolibyte/quickbooks-php/blob/master/docs/web_connector/example_web_connector_import.php
Here are some code snippets:
Quickbooks handling controller - index method handles QB requests. Vendor functions stored separately and are listed below.
<?php
define('QB_QUICKBOOKS_CONFIG_LAST', 'last');
define('QB_QUICKBOOKS_CONFIG_CURR', 'curr');
define('QB_QUICKBOOKS_MAX_RETURNED', 10);
define('QB_QUICKBOOKS_MAILTO', '...');
class QuickbooksController extends Controller {
public function index() {
QuickBooks_WebConnector_Queue_Singleton::initialize($this->dsn);
$queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$queue->enqueue(QUICKBOOKS_IMPORT_VENDOR, 1, QB_PRIORITY_VENDOR);
$server = new QuickBooks_WebConnector_Server($this->dsn, $this->map, $this->errmap, $this->hooks, $this->log_level, $this->soapserver, QUICKBOOKS_WSDL, $this->soap_options, $this->handler_options, $this->driver_options, $this->callback_options);
$response = $server->handle(true, true);
}
public function __construct() {
$this->loadHelper('accounting.quickbooks.vendors');
date_default_timezone_set('America/New_York');
$this->user = 'internal';
$this->pass = 'BrownBadgerPizza';
$this->map = [
QUICKBOOKS_IMPORT_VENDOR => array( 'vendorImportRequest', 'vendorImportResponse' ),
];
$this->errmap = [
500 => '_quickbooks_handle_500',
];
$this->hooks = array(
QuickBooks_WebConnector_Handlers::HOOK_LOGINSUCCESS => '_quickbooks_hook_loginsuccess',
);
$this->log_level = QUICKBOOKS_LOG_DEBUG;
$this->soapserver = QUICKBOOKS_SOAPSERVER_BUILTIN;
$this->handler_options = [
'deny_concurrent_logins' => false,
'deny_reallyfast_logins' => false,
];
$this->driver_options = [];
$this->callback_options = [];
$this->dsn = '...',
define('QB_QUICKBOOKS_DSN', $this->dsn);
}
public function support() {
header("HTTP/1.1 200 OK");
}
}
function _quickbooks_hook_loginsuccess($requestID, $user, $hook, &$err, $hook_data, $callback_config) {
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$date = '1983-01-02 12:01:01';
if (!_quickbooks_get_last_run($user, QUICKBOOKS_IMPORT_VENDOR))
{
_quickbooks_set_last_run($user, QUICKBOOKS_IMPORT_VENDOR, $date);
}
$Queue->enqueue(QUICKBOOKS_IMPORT_VENDOR, 1, QB_PRIORITY_VENDOR);
}
function _quickbooks_get_last_run($user, $action) {
$type = null;
$opts = null;
return QuickBooks_Utilities::configRead(QB_QUICKBOOKS_DSN, $user, md5(__FILE__), QB_QUICKBOOKS_CONFIG_LAST . '-' . $action, $type, $opts);
}
function _quickbooks_set_last_run($user, $action, $force = null) {
$value = date('Y-m-d') . 'T' . date('H:i:s');
if ($force) {
$value = date('Y-m-d', strtotime($force)) . 'T' . date('H:i:s', strtotime($force));
}
return QuickBooks_Utilities::configWrite(QB_QUICKBOOKS_DSN, $user, md5(__FILE__), QB_QUICKBOOKS_CONFIG_LAST . '-' . $action, $value);
}
function _quickbooks_get_current_run($user, $action)
{
$type = null;
$opts = null;
return QuickBooks_Utilities::configRead(QB_QUICKBOOKS_DSN, $user, md5(__FILE__), QB_QUICKBOOKS_CONFIG_CURR . '-' . $action, $type, $opts);
}
function _quickbooks_set_current_run($user, $action, $force = null)
{
$value = date('Y-m-d') . 'T' . date('H:i:s');
if ($force)
{
$value = date('Y-m-d', strtotime($force)) . 'T' . date('H:i:s', strtotime($force));
}
return QuickBooks_Utilities::configWrite(QB_QUICKBOOKS_DSN, $user, md5(__FILE__), QB_QUICKBOOKS_CONFIG_CURR . '-' . $action, $value);
}
function _quickbooks_handle_500($requestID, $user, $action, $ID, $extra, &$err, $xml, $errnum, $errmsg) {
return true;
}
Vendor Request:
function vendorImportRequest($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale) {
$attr_iteratorID = '';
$attr_iterator = ' iterator="Start" ';
if (empty($extra['iteratorID'])) {
$last = _quickbooks_get_last_run($user, $action);
_quickbooks_set_last_run($user, $action);
_quickbooks_set_current_run($user, $action, $last);
} else {
$attr_iteratorID = ' iteratorID="' . $extra['iteratorID'] . '" ';
$attr_iterator = ' iterator="Continue" ';
$last = _quickbooks_get_current_run($user, $action);
}
$xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="' . $version . '"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<VendorQueryRq ' . $attr_iterator . ' ' . $attr_iteratorID . ' requestID="' . $requestID . '">
<MaxReturned>' . QB_QUICKBOOKS_MAX_RETURNED . '</MaxReturned>
<FromModifiedDate>' . $last . '</FromModifiedDate>
<OwnerID>0</OwnerID>
</VendorQueryRq>
</QBXMLMsgsRq>
</QBXML>';
return $xml;
}
Vendor Response:
function vendorImportResponse($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents) {
if (!empty($idents['iteratorRemainingCount'])) {
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$Queue->enqueue(QUICKBOOKS_IMPORT_VENDOR, null, QB_PRIORITY_VENDOR, array( 'iteratorID' => $idents['iteratorID'] ));
}
... do things with XML
Thank you very much for any assistance.
EDIT:
quickbooks_queue table results after the first attempt to sync all vendors
qb_action ident extra qbxml priority qb_status enqueue_datetime dequeue_datetime msg
VendorImport 1 4 e 2017-02-03 13:15:34 2017-02-03 13:15:341: A query request did not find a matching object in QuickBooks
VendorImport 1 4 q 2017-02-03 13:15:38 NULL
quickbooks_log table
Handler is starting up...: Array
(
[qb_company_file] =>
[qbwc_min_version] =>
[qbwc_wait_before_next_update] =>
[qbwc_min_run_every_n_seconds] =>
[qbwc_version_warning_message] =>
[qbwc_version_error_message] =>
[qbwc_interactive_url] =>
[autoadd_missing_requestid] => 1
[check_valid_requestid] => 1
[server_version] => PHP QuickBooks SOAP Server v3.0 at /route.php/quickbooks
[authenticate] =>
[authenticate_dsn] =>
[map_application_identifiers] => 1
[allow_remote_addr] => Array
(
)
[deny_remote_addr] => Array
(
)
[convert_unix_newlines] => 1
[deny_concurrent_logins] =>
[deny_concurrent_timeout] => 60
[deny_reallyfast_logins] =>
[deny_reallyfast_timeout] => 600
[masking] => 1
)
sendRequestXML()
Dequeued: ( VendorImport, 1 )
Outgoing XML request: <?xml version="1.0" encoding="utf-8"?>
<?qbxml version="13.0"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<VendorQueryRq iterator="Start" requestID="318">
<MaxReturned>10</MaxReturned>
<FromModifiedDate></FromModifiedDate>
<OwnerID>0</OwnerID>
</VendorQueryRq>
</QBXMLMsgsRq>
</QBXML>
Handler is starting up...: Array
(
[qb_company_file] =>
[qbwc_min_version] =>
[qbwc_wait_before_next_update] =>
[qbwc_min_run_every_n_seconds] =>
[qbwc_version_warning_message] =>
[qbwc_version_error_message] =>
[qbwc_interactive_url] =>
[autoadd_missing_requestid] => 1
[check_valid_requestid] => 1
[server_version] => PHP QuickBooks SOAP Server v3.0 at /route.php/quickbooks
[authenticate] =>
[authenticate_dsn] =>
[map_application_identifiers] => 1
[allow_remote_addr] => Array
(
)
[deny_remote_addr] => Array
(
)
[convert_unix_newlines] => 1
[deny_concurrent_logins] =>
[deny_concurrent_timeout] => 60
[deny_reallyfast_logins] =>
[deny_reallyfast_timeout] => 600
[masking] => 1
)
receiveResponseXML()
Incoming XML response:
10 vendors of data ...
25% complete...
Handler is starting up...: Array
(
[qb_company_file] =>
[qbwc_min_version] =>
[qbwc_wait_before_next_update] =>
[qbwc_min_run_every_n_seconds] =>
[qbwc_version_warning_message] =>
[qbwc_version_error_message] =>
[qbwc_interactive_url] =>
[autoadd_missing_requestid] => 1
[check_valid_requestid] => 1
[server_version] => PHP QuickBooks SOAP Server v3.0 at /route.php/quickbooks
[authenticate] =>
[authenticate_dsn] =>
[map_application_identifiers] => 1
[allow_remote_addr] => Array
(
)
[deny_remote_addr] => Array
(
)
[convert_unix_newlines] => 1
[deny_concurrent_logins] =>
[deny_concurrent_timeout] => 60
[deny_reallyfast_logins] =>
[deny_reallyfast_timeout] => 600
[masking] => 1
)
sendRequestXML()
Dequeued: ( VendorImport, 1 )
Outgoing XML request: <?xml version="1.0" encoding="utf-8"?>
<?qbxml version="13.0"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<VendorQueryRq iterator="Start" requestID="1">
<MaxReturned>10</MaxReturned>
<FromModifiedDate>2017-02-03T13:15:32</FromModifiedDate>
<OwnerID>0</OwnerID>
</VendorQueryRq>
</QBXMLMsgsRq>
</QBXML>
Handler is starting up...: Array
(
[qb_company_file] =>
[qbwc_min_version] =>
[qbwc_wait_before_next_update] =>
[qbwc_min_run_every_n_seconds] =>
[qbwc_version_warning_message] =>
[qbwc_version_error_message] =>
[qbwc_interactive_url] =>
[autoadd_missing_requestid] => 1
[check_valid_requestid] => 1
[server_version] => PHP QuickBooks SOAP Server v3.0 at /route.php/quickbooks
[authenticate] =>
[authenticate_dsn] =>
[map_application_identifiers] => 1
[allow_remote_addr] => Array
(
)
[deny_remote_addr] => Array
(
)
[convert_unix_newlines] => 1
[deny_concurrent_logins] =>
[deny_concurrent_timeout] => 60
[deny_reallyfast_logins] =>
[deny_reallyfast_timeout] => 600
[masking] => 1
)
receiveResponseXML()
Incoming XML response: <?xml version="1.0" ?>
<QBXML>
<QBXMLMsgsRs>
<VendorQueryRs requestID="1" statusCode="1" statusSeverity="Info" statusMessage="A query request did not find a matching object in QuickBooks" iteratorRemainingCount="0" iteratorID="{a7b4ed15-9612-4fe8-aa3c-31ee60b7b491}" />
</QBXMLMsgsRs>
</QBXML>
Attempting to handle error: 1, A query request did not find a matching object in QuickBooks
Handled error: 1: A query request did not find a matching object in QuickBooks (handler returned: )
Transaction error at -1% complete...
Handler is starting up...: Array
(
[qb_company_file] =>
[qbwc_min_version] =>
[qbwc_wait_before_next_update] =>
[qbwc_min_run_every_n_seconds] =>
[qbwc_version_warning_message] =>
[qbwc_version_error_message] =>
[qbwc_interactive_url] =>
[autoadd_missing_requestid] => 1
[check_valid_requestid] => 1
[server_version] => PHP QuickBooks SOAP Server v3.0 at /route.php/quickbooks
[authenticate] =>
[authenticate_dsn] =>
[map_application_identifiers] => 1
[allow_remote_addr] => Array
(
)
[deny_remote_addr] => Array
(
)
[convert_unix_newlines] => 1
[deny_concurrent_logins] =>
[deny_concurrent_timeout] => 60
[deny_reallyfast_logins] =>
[deny_reallyfast_timeout] => 600
[masking] => 1
)
getLastError()
Handler is starting up...: Array
(
[qb_company_file] =>
[qbwc_min_version] =>
[qbwc_wait_before_next_update] =>
[qbwc_min_run_every_n_seconds] =>
[qbwc_version_warning_message] =>
[qbwc_version_error_message] =>
[qbwc_interactive_url] =>
[autoadd_missing_requestid] => 1
[check_valid_requestid] => 1
[server_version] => PHP QuickBooks SOAP Server v3.0 at /route.php/quickbooks
[authenticate] =>
[authenticate_dsn] =>
[map_application_identifiers] => 1
[allow_remote_addr] => Array
(
)
[deny_remote_addr] => Array
(
)
[convert_unix_newlines] => 1
[deny_concurrent_logins] =>
[deny_concurrent_timeout] => 60
[deny_reallyfast_logins] =>
[deny_reallyfast_timeout] => 600
[masking] => 1
)
closeConnection()
EDIT #2
After looking through logs and the queue, what I'm seeing is that even though I'm passing the iteratorID
when I get a XML response that has additional results, for some reason, the library is not passing that ID through the $extra
parameter on the queued request.
The queue table in the database has this in the extra
column: a:1:{s:10:"iteratorID";s:38:"{3a096324-6e5e-4fcb-a6ac-9a2600372029}";}
. But extra param is blank on the request function.
Solution
I found that in my QBWC handler function, I was queuing a vendor import request. This is in addition to the enqueue on login success. After I removed this, it worked just fine.
Here's the update that made it work:
From
public function index() {
QuickBooks_WebConnector_Queue_Singleton::initialize($this->dsn);
$queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$queue->enqueue(QUICKBOOKS_IMPORT_VENDOR, 1, QB_PRIORITY_VENDOR);
$server = new QuickBooks_WebConnector_Server($this->dsn, $this->map, $this->errmap, $this->hooks, $this->log_level, $this->soapserver, QUICKBOOKS_WSDL, $this->soap_options, $this->handler_options, $this->driver_options, $this->callback_options);
$response = $server->handle(true, true);
}
To
public function index() {
QuickBooks_WebConnector_Queue_Singleton::initialize($this->dsn);
$queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$server = new QuickBooks_WebConnector_Server($this->dsn, $this->map, $this->errmap, $this->hooks, $this->log_level, $this->soapserver, QUICKBOOKS_WSDL, $this->soap_options, $this->handler_options, $this->driver_options, $this->callback_options);
$response = $server->handle(true, true);
}