3

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.

  • Post the logs. Look at the quickbooks_queue table and see if things are processing in the order you're expecting them in. – Keith Palmer Jr. Feb 03 '17 at 17:58
  • I added the quickbooks_queue and quickbooks_log results. It looks like there may be something amiss with the queue, but I'm not exactly sure what I'm seeing. – Andrew Smith Feb 03 '17 at 18:35
  • The response is going to have the number of items left and an iterator id. Are you sending the iterator id to get more data? – William Lorfing Feb 03 '17 at 19:33
  • @WilliamLorfing, The response that I left out does include an iterator ID and says `iteratorRemainingCount="5513"`. I pass this along in the VendorRequest xml, see code above. I added some additional logging to see what I'm getting in the $extra variable. – Andrew Smith Feb 03 '17 at 19:50
  • @KeithPalmerJr. It actually looks like the $extra variable coming into my handler function is empty on the second run. Which would mean it isn't getting the iterator ID. This part of the code matches the documentation, and the queue shows this in the `extra` column: `a:1:{s:10:"iteratorID";s:38:"{3a096324-6e5e-4fcb-a6ac-9a2600372029}";}` – Andrew Smith Feb 03 '17 at 20:37

3 Answers3

2

What is QB_QUICKBOOKS_MAX_RETURNED value?

Once you make the 1st pass are you taking the Iterator value returned and processing with it?

William Lorfing
  • 2,656
  • 10
  • 7
  • `QB_QUICKBOOKS_MAX_RETURNED` value is set to 10. I believe I'm processing with the iterator id. Here's the vendor response handler enqueue: `$Queue->enqueue(QUICKBOOKS_IMPORT_VENDOR, null, QB_PRIORITY_VENDOR, array( 'iteratorID' => $idents['iteratorID'] ));` – Andrew Smith Feb 03 '17 at 17:51
2

This is likely your issue:

<FromModifiedDate></FromModifiedDate>

There's no date in there.

You didn't really post enough code for us to help you beyond that.

Keith Palmer Jr.
  • 27,666
  • 16
  • 68
  • 105
0

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);
}
halfer
  • 19,824
  • 17
  • 99
  • 186