Search code examples
phpquickbooksintuit-partner-platformqbwc

Consolibyte Quickbooks PHP web connector cannot sync all vendors


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

  • 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);
    }