Intuit QuickBooks web connector IteratorID not valid error - quickbooks

I have been trying to integrate a PHP based solution with QuickBooks Enterprise edition. I use QuickBooks PHP Dev Kit (http://consolibyte.com/quickbooks-open-source/) to connect the application to sync multiple modules bidirectionally.
The first module (QuickBooks to VtigerCRM) with multiple records are getting synced perfectly. When I try with my next module, only few records are getting synced and throws a error IteratorID is not valid. Then, I tried sending the params with new IteratorID in XML with no success.
20180821.05:13:03 UTC : QBWebConnector.SOAPWebService.do_receiveResponseXML() : hresult=""
20180821.05:13:03 UTC : QBWebConnector.SOAPWebService.do_receiveResponseXML() : message=""
20180821.05:13:03 UTC : QBWebConnector.SOAPWebService.do_receiveResponseXML() : Received from receiveResponseXML() following parameters:
20180821.05:13:03 UTC : QBWebConnector.SOAPWebService.do_receiveResponseXML() : more="-1">
20180821.05:13:03 UTC : QBWebConnector.SOAPWebService.do_getLastError() : * Calling getLastError() with following parameter:
20180821.05:13:03 UTC : QBWebConnector.SOAPWebService.do_getLastError() : wcTicket="403c7856-d600-c664-5187-3d9e2036c0cc"
20180821.05:13:04 UTC : QBWebConnector.SOAPWebService.do_getLastError() : Received from getLastError() following parameter:
20180821.05:13:04 UTC : QBWebConnector.SOAPWebService.do_getLastError() : errorMsg="3391: The iteratorID "{80c35df0-ae92-43a3-af49-946da1c306fb}" is not valid."
20180821.05:13:04 UTC : QBWebConnector.SOAPWebService.do_getLastError() : Received error from application: 3391: The iteratorID "{80c35df0-ae92-43a3-af49-946da1c306fb}" is not valid.
20180821.05:13:04 UTC : QBWebConnector.CompanyFileLock.Send_CompanyQueryRqXML() : XML dump follows: -
<?xml version="1.0"?><?qbxml version="13.0"?><QBXML><QBXMLMsgsRq onError="stopOnError"><CompanyQueryRq requestID="1"><OwnerID>{90A44FB7-33D9-4815-AC85-AC86A7E7D1EB}</OwnerID></CompanyQueryRq></QBXMLMsgsRq></QBXML>
I have added the code of the Helper.php
<?php
// Support URL
if (!empty($_GET['support']))
{
header('Location: http://www.consolibyte.com/');
exit;
}
// We need to make sure the correct timezone is set, or some PHP installations will complain
if (function_exists('date_default_timezone_set'))
{
// * MAKE SURE YOU SET THIS TO THE CORRECT TIMEZONE! *
// List of valid timezones is here: http://us3.php.net/manual/en/timezones.php
date_default_timezone_set('America/New_York');
}
// Require the framework
require_once 'QuickBooks.php';
require_once('config.inc.php');
global $dbconfig;
$user = 'quickbooks';
$pass = 'password';
define('QB_QUICKBOOKS_CONFIG_LAST', 'last');
define('QB_QUICKBOOKS_CONFIG_CURR', 'curr');
define('QB_QUICKBOOKS_MAX_RETURNED', 1);
define('QB_PRIORITY_PURCHASEORDER', 1);
define('QB_PRIORITY_ITEM', 6);
define('QB_PRIORITY_CUSTOMER', 5);
define('QB_PRIORITY_SALESORDER', 2);
/**
* Request priorities, invoices last...
*/
define('QB_PRIORITY_INVOICE', 3);
define('QB_PRIORITY_ESTIMATE',4);
define('QB_QUICKBOOKS_MAILTO', 'keith#consolibyte.com');
// Map QuickBooks actions to handler functions
$map = array(
QUICKBOOKS_IMPORT_CUSTOMER => array( '_quickbooks_customer_import_request', '_quickbooks_customer_import_response' ),
QUICKBOOKS_IMPORT_ITEM => array( '_quickbooks_item_import_request', '_quickbooks_item_import_response' ),
QUICKBOOKS_IMPORT_ESTIMATE =>array( '_quickbooks_quote_import_request', '_quickbooks_quote_import_response' ),
QUICKBOOKS_IMPORT_SALESORDER => array( '_quickbooks_salesorder_import_request', '_quickbooks_salesorder_import_response' ),
QUICKBOOKS_IMPORT_INVOICE => array( '_quickbooks_invoice_import_request', '_quickbooks_invoice_import_response' ),
QUICKBOOKS_IMPORT_PURCHASEORDER => array( '_quickbooks_purchaseorder_import_request', '_quickbooks_purchaseorder_import_response' ),
);
// Error handlers
$errmap = array(
500 => '_quickbooks_error_e500_notfound', // Catch errors caused by searching for things not present in QuickBooks
1 => '_quickbooks_error_e500_notfound',
'*' => '_quickbooks_error_catchall', // Catch any other errors that might occur
);
// An array of callback hooks
$hooks = array(
QuickBooks_WebConnector_Handlers::HOOK_LOGINSUCCESS => '_quickbooks_hook_loginsuccess', // call this whenever a successful login occurs
);
// Logging level
//$log_level = QUICKBOOKS_LOG_NORMAL;
//$log_level = QUICKBOOKS_LOG_VERBOSE;
//$log_level = QUICKBOOKS_LOG_DEBUG; // Use this level until you're sure everything works!!!
$log_level = QUICKBOOKS_LOG_DEVELOP;
// What SOAP server you're using
//$soapserver = QUICKBOOKS_SOAPSERVER_PHP; // The PHP SOAP extension, see: www.php.net/soap
$soapserver = QUICKBOOKS_SOAPSERVER_BUILTIN; // A pure-PHP SOAP server (no PHP ext/soap extension required, also makes debugging easier)
$soap_options = array( // See http://www.php.net/soap
);
$handler_options = array( // See the comments in the QuickBooks/Server/Handlers.php file
'deny_concurrent_logins' => false,
'deny_reallyfast_logins' => false,
);
$driver_options = array( // See the comments in the QuickBooks/Driver/<YOUR DRIVER HERE>.php file ( i.e. 'Mysql.php', etc. )
);
$callback_options = array(
);
$dsn = $dbconfig['db_type']."://".$dbconfig['db_username'].":".$dbconfig['db_password']."#".$dbconfig['db_server']."/".$dbconfig['db_name'];
//$dsn = 'mysql://root:smackcoders#localhost/vtiger2';
//$dsn = 'mysql://testuser:testpassword#localhost/testdatabase';
define('QB_QUICKBOOKS_DSN', $dsn);
// If we haven't done our one-time initialization yet, do it now!
if (!QuickBooks_Utilities::initialized($dsn))
{
// Create the example tables
$file = dirname(__FILE__) . '/example.sql';
if (file_exists($file))
{
$contents = file_get_contents($file);
foreach (explode(';', $contents) as $sql)
{
if (!trim($sql))
{
continue;
}
mysql_query($sql) or die(trigger_error(mysql_error()));
}
}
else
{
die('Could not locate "./example.sql" to create the demo SQL schema!');
}
// Create the database tables
QuickBooks_Utilities::initialize($dsn);
// Add the default authentication username/password
QuickBooks_Utilities::createUser($dsn, $user, $pass);
}
// Initialize the queue
QuickBooks_WebConnector_Queue_Singleton::initialize($dsn);
// Create a new server and tell it to handle the requests
$Server = new QuickBooks_WebConnector_Server($dsn, $map, $errmap, $hooks, $log_level, $soapserver, QUICKBOOKS_WSDL, $soap_options, $handler_options, $driver_options, $callback_options);
$response = $Server->handle(true, true);
/**
* Login success hook - perform an action when a user logs in via the Web Connector
*
*
*/
function _quickbooks_hook_loginsuccess($requestID, $user, $hook, &$err, $hook_data, $callback_config)
{
// For new users, we need to set up a few things
// Fetch the queue instance
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$date = date('Y-m-d H:i:s');
// Set up the invoice imports
if (!_quickbooks_get_last_run($user, QUICKBOOKS_IMPORT_INVOICE))
{
// And write the initial sync time
_quickbooks_set_last_run($user, QUICKBOOKS_IMPORT_INVOICE, $date);
}
// Do the same for customers
if (!_quickbooks_get_last_run($user, QUICKBOOKS_IMPORT_CUSTOMER))
{
_quickbooks_set_last_run($user, QUICKBOOKS_IMPORT_CUSTOMER, $date);
}
// ... and for sales orders
if (!_quickbooks_get_last_run($user, QUICKBOOKS_IMPORT_SALESORDER))
{
_quickbooks_set_last_run($user, QUICKBOOKS_IMPORT_SALESORDER, $date);
}
// ... and for items
if (!_quickbooks_get_last_run($user, QUICKBOOKS_IMPORT_ITEM))
{
_quickbooks_set_last_run($user, QUICKBOOKS_IMPORT_ITEM, $date);
}
// Make sure the requests get queued up
$Queue->enqueue(QUICKBOOKS_IMPORT_CUSTOMER, 1, QB_PRIORITY_CUSTOMER, null, $user);
$Queue->enqueue(QUICKBOOKS_IMPORT_ITEM, 1, QB_PRIORITY_ITEM, null, $user);
$Queue->enqueue(QUICKBOOKS_IMPORT_ESTIMATE, 1, QB_PRIORITY_ESTIMATE, null, $user);
$Queue->enqueue(QUICKBOOKS_IMPORT_SALESORDER, 1, QB_PRIORITY_SALESORDER, null, $user);
$Queue->enqueue(QUICKBOOKS_IMPORT_INVOICE, 1, QB_PRIORITY_INVOICE, null, $user);
$Queue->enqueue(QUICKBOOKS_IMPORT_PURCHASEORDER, 1, QB_PRIORITY_PURCHASEORDER, null, $user);
}
/**
* Get the last date/time the QuickBooks sync ran
*
* #param string $user The web connector username
* #return string A date/time in this format: "yyyy-mm-dd hh:ii:ss"
*/
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);
}
/**
* Build a request to import invoices already in QuickBooks into our application
*/
function _quickbooks_invoice_import_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
// Iterator support (break the result set into small chunks)
$attr_iteratorID = '';
$attr_iterator = ' iterator="Start" ';
if (empty($extra['iteratorID']))
{
$last = _quickbooks_get_last_run($user, $action);
_quickbooks_set_last_run($user, $action); // Update the last run time to NOW()
// Set the current run to $last
_quickbooks_set_current_run($user, $action, $last);
}
else
{
$extra['iteratorID'] = $saved_iterator_id; // From Table
// This is a continuation of a batch
$attr_iteratorID = ' iteratorID="' . $extra['iteratorID'] . '" ';
$attr_iterator = ' iterator="Continue" ';
$last = _quickbooks_get_current_run($user, $action);
}
// Build the request
$xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="' . $version . '"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<InvoiceQueryRq ' . $attr_iterator . ' ' . $attr_iteratorID . ' requestID="' . $requestID . '">
<MaxReturned>' . QB_QUICKBOOKS_MAX_RETURNED . '</MaxReturned>
<ModifiedDateRangeFilter>
<FromModifiedDate>' . $last . '</FromModifiedDate>
</ModifiedDateRangeFilter>
<IncludeLineItems>true</IncludeLineItems>
<OwnerID>0</OwnerID>
</InvoiceQueryRq>
</QBXMLMsgsRq>
</QBXML>';
return $xml;
}
/**
* Handle a response from QuickBooks
*/
function _quickbooks_invoice_import_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
if (!empty($idents['iteratorRemainingCount']))
{
// Queue up another request
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$Queue->enqueue(QUICKBOOKS_IMPORT_INVOICE, null, QB_PRIORITY_INVOICE, array( 'iteratorID' => $idents['iteratorID'] ), $user);
}else{
return true;
}
// This piece of the response from QuickBooks is now stored in $xml. You
// can process the qbXML response in $xml in any way you like. Save it to
// a file, stuff it in a database, parse it and stuff the records in a
// database, etc. etc. etc.
//
// The following example shows how to use the built-in XML parser to parse
// the response and stuff it into a database.
// Import all of the records
$errnum = 0;
$errmsg = '';
$Parser = new QuickBooks_XML_Parser($xml);
if ($Doc = $Parser->parse($errnum, $errmsg))
{
$Root = $Doc->getRoot();
$List = $Root->getChildAt('QBXML/QBXMLMsgsRs/InvoiceQueryRs');
$smackHelper = new Quickbooks_vtigerHelper();
$smackHelper->addInvoice($List->children());
}
return true;
}
/**
* Build a request to import customers already in QuickBooks into our application
*/
function _quickbooks_customer_import_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
// Iterator support (break the result set into small chunks)
$attr_iteratorID = '';
$attr_iterator = ' iterator="Start" ';
if (empty($extra['iteratorID']))
{
// This is the first request in a new batch
$last = _quickbooks_get_last_run($user, $action);
_quickbooks_set_last_run($user, $action); // Update the last run time to NOW()
// Set the current run to $last
_quickbooks_set_current_run($user, $action, $last);
}
else
{
$extra['iteratorID'] = $saved_iterator_id; // From Table
// This is a continuation of a batch
$attr_iteratorID = ' iteratorID="' . $extra['iteratorID'] . '" ';
$attr_iterator = ' iterator="Continue" ';
$last = _quickbooks_get_current_run($user, $action);
}
// Build the request
$xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="' . $version . '"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<CustomerQueryRq ' . $attr_iterator . ' ' . $attr_iteratorID . ' requestID="' . $requestID . '">
<MaxReturned>' . QB_QUICKBOOKS_MAX_RETURNED . '</MaxReturned>
<FromModifiedDate>' . $last . '</FromModifiedDate>
<OwnerID>0</OwnerID>
</CustomerQueryRq>
</QBXMLMsgsRq>
</QBXML>';
return $xml;
}
/**
* Handle a response from QuickBooks
*/
function _quickbooks_customer_import_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
if (!empty($idents['iteratorRemainingCount']))
{
// Queue up another request
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$Queue->enqueue(QUICKBOOKS_IMPORT_CUSTOMER, null, QB_PRIORITY_CUSTOMER, array( 'iteratorID' => $idents['iteratorID'] ), $user);
}else{
return true;
}
// Import all of the records
$errnum = 0;
$errmsg = '';
$Parser = new QuickBooks_XML_Parser($xml);
if ($Doc = $Parser->parse($errnum, $errmsg))
{
$Root = $Doc->getRoot();
$List = $Root->getChildAt('QBXML/QBXMLMsgsRs/CustomerQueryRs');
$smackHelper = new Quickbooks_vtigerHelper();
$smackHelper->addContacts($List->children());
}
return true;
}
function _quickbooks_quote_import_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
if (!empty($idents['iteratorRemainingCount']))
{
// Queue up another request
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$Queue->enqueue(QUICKBOOKS_IMPORT_ESTIMATE, null, QB_PRIORITY_ESTIMATE, array( 'iteratorID' => $idents['iteratorID'] ), $user);
}else{
return true;
}
// Import all of the records
$errnum = 0;
$errmsg = '';
$Parser = new QuickBooks_XML_Parser($xml);
if ($Doc = $Parser->parse($errnum, $errmsg))
{
$Root = $Doc->getRoot();
$List = $Root->getChildAt('QBXML/QBXMLMsgsRs/EstimateQueryRs');
$smackHelper = new Quickbooks_vtigerHelper();
$smackHelper->addQuotes($List->children());
}
return true;
}
function _quickbooks_quote_import_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
if (empty($extra['iteratorID']))
{
$last = _quickbooks_get_last_run($user, $action);
_quickbooks_set_last_run($user, $action); // Update the last run time to NOW()
$attr_iterator = ' iterator="Start" ';
// Set the current run to $last
_quickbooks_set_current_run($user, $action, $last);
}
else
{
$extra['iteratorID'] = $saved_iterator_id; // From Table
// This is a continuation of a batch
$attr_iteratorID = ' iteratorID="' . $extra['iteratorID'] . '" ';
$attr_iterator = ' iterator="Continue" ';
$last = _quickbooks_get_current_run($user, $action);
//$last = '2015-08-18T01:03:41';
//$last = _quickbooks_get_current_run($user, $action);
}
// Build the request
$xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="' . $version . '"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<EstimateQueryRq ' . $attr_iterator . ' ' . $attr_iteratorID . ' requestID="' . $requestID . '">
<MaxReturned>' . QB_QUICKBOOKS_MAX_RETURNED . '</MaxReturned>
<ModifiedDateRangeFilter>
<FromModifiedDate>' . $last . '</FromModifiedDate>
</ModifiedDateRangeFilter>
<IncludeLineItems>true</IncludeLineItems>
<OwnerID>0</OwnerID>
</EstimateQueryRq>
</QBXMLMsgsRq>
</QBXML>';
return $xml;
}
function _quickbooks_salesorder_import_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
// Iterator support (break the result set into small chunks)
$attr_iteratorID = '';
$attr_iterator = ' iterator="Start" ';
if (empty($extra['iteratorID']))
{
// This is the first request in a new batch
$last = _quickbooks_get_last_run($user, $action);
_quickbooks_set_last_run($user, $action); // Update the last run time to NOW()
// Set the current run to $last
_quickbooks_set_current_run($user, $action, $last);
}
else
{
$extra['iteratorID'] = $saved_iterator_id; // From Table
// This is a continuation of a batch
$attr_iteratorID = ' iteratorID="' . $extra['iteratorID'] . '" ';
$attr_iterator = ' iterator="Continue" ';
$last = _quickbooks_get_current_run($user, $action);
}
// Build the request
$xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="' . $version . '"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<SalesOrderQueryRq ' . $attr_iterator . ' ' . $attr_iteratorID . ' requestID="' . $requestID . '">
<MaxReturned>' . QB_QUICKBOOKS_MAX_RETURNED . '</MaxReturned>
<ModifiedDateRangeFilter>
<FromModifiedDate>' . $last . '</FromModifiedDate>
</ModifiedDateRangeFilter>
<IncludeLineItems>true</IncludeLineItems>
<OwnerID>0</OwnerID>
</SalesOrderQueryRq>
</QBXMLMsgsRq>
</QBXML>';
return $xml;
}
/**
* Handle a response from QuickBooks
*/
function _quickbooks_salesorder_import_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
if (!empty($idents['iteratorRemainingCount']))
{
// Queue up another request
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$Queue->enqueue(QUICKBOOKS_IMPORT_SALESORDER, null, QB_PRIORITY_SALESORDER, array( 'iteratorID' => $idents['iteratorID'] ), $user);
}else{
return true;
}
// Import all of the records
$errnum = 0;
$errmsg = '';
$Parser = new QuickBooks_XML_Parser($xml);
if ($Doc = $Parser->parse($errnum, $errmsg))
{
$Root = $Doc->getRoot();
$List = $Root->getChildAt('QBXML/QBXMLMsgsRs/SalesOrderQueryRs');
$smackHelper = new Quickbooks_vtigerHelper();
$smackHelper->addSalesOrder($List->children());
}
return true;
}
/**
* Build a request to import customers already in QuickBooks into our application
*/
function _quickbooks_item_import_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
// Iterator support (break the result set into small chunks)
$attr_iteratorID = '';
$attr_iterator = ' iterator="Start" ';
if (empty($extra['iteratorID']))
{
// This is the first request in a new batch
$last = _quickbooks_get_last_run($user, $action);
_quickbooks_set_last_run($user, $action); // Update the last run time to NOW()
// Set the current run to $last
_quickbooks_set_current_run($user, $action, $last);
}
else
{
$extra['iteratorID'] = $saved_iterator_id; // From Table
// This is a continuation of a batch
$attr_iteratorID = ' iteratorID="' . $extra['iteratorID'] . '" ';
$attr_iterator = ' iterator="Continue" ';
$last = _quickbooks_get_current_run($user, $action);
}
// Build the request
$xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="' . $version . '"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<ItemQueryRq ' . $attr_iterator . ' ' . $attr_iteratorID . ' requestID="' . $requestID . '">
<MaxReturned>' . QB_QUICKBOOKS_MAX_RETURNED . '</MaxReturned>
<FromModifiedDate>' . $last . '</FromModifiedDate>
<OwnerID>0</OwnerID>
</ItemQueryRq>
</QBXMLMsgsRq>
</QBXML>';
return $xml;
}
/**
* Handle a response from QuickBooks
*/
function _quickbooks_item_import_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
if (!empty($idents['iteratorRemainingCount']))
{
// Queue up another request
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$Queue->enqueue(QUICKBOOKS_IMPORT_ITEM, null, QB_PRIORITY_ITEM, array( 'iteratorID' => $idents['iteratorID'] ), $user);
}else{
return true;
}
// Import all of the records
$errnum = 0;
$errmsg = '';
$Parser = new QuickBooks_XML_Parser($xml);
if ($Doc = $Parser->parse($errnum, $errmsg))
{
$Root = $Doc->getRoot();
$List = $Root->getChildAt('QBXML/QBXMLMsgsRs/ItemQueryRs');
$smackHelper = new Quickbooks_vtigerHelper();
$smackHelper->addProducts($List->children(),'Products');
}
return true;
}
/**
* Build a request to import invoices already in QuickBooks into our application
*/
function _quickbooks_purchaseorder_import_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
// Iterator support (break the result set into small chunks)
$attr_iteratorID = '';
$attr_iterator = ' iterator="Start" ';
if (empty($extra['iteratorID']))
{
// This is the first request in a new batch
$last = _quickbooks_get_last_run($user, $action);
_quickbooks_set_last_run($user, $action); // Update the last run time to NOW()
// Set the current run to $last
_quickbooks_set_current_run($user, $action, $last);
}
else
{
$extra['iteratorID'] = $saved_iterator_id; // From Table
// This is a continuation of a batch
$attr_iteratorID = ' iteratorID="' . $extra['iteratorID'] . '" ';
$attr_iterator = ' iterator="Continue" ';
$last = _quickbooks_get_current_run($user, $action);
}
// Build the request
$xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="' . $version . '"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<PurchaseOrderQueryRq ' . $attr_iterator . ' ' . $attr_iteratorID . ' requestID="' . $requestID . '">
<MaxReturned>' . QB_QUICKBOOKS_MAX_RETURNED . '</MaxReturned>
<!--<ModifiedDateRangeFilter>
<FromModifiedDate>' . $last . '</FromModifiedDate>
</ModifiedDateRangeFilter>-->
<IncludeLineItems>true</IncludeLineItems>
<OwnerID>0</OwnerID>
</PurchaseOrderQueryRq>
</QBXMLMsgsRq>
</QBXML>';
return $xml;
}
/**
* Handle a response from QuickBooks
*/
function _quickbooks_purchaseorder_import_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
if (!empty($idents['iteratorRemainingCount']))
{
// Queue up another request
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$Queue->enqueue(QUICKBOOKS_IMPORT_PURCHASEORDER, null, QB_PRIORITY_PURCHASEORDER, array( 'iteratorID' => $idents['iteratorID'] ), $user);
}else{
return true;
}
// This piece of the response from QuickBooks is now stored in $xml. You
// can process the qbXML response in $xml in any way you like. Save it to
// a file, stuff it in a database, parse it and stuff the records in a
// database, etc. etc. etc.
//
// The following example shows how to use the built-in XML parser to parse
// the response and stuff it into a database.
// Import all of the records
$errnum = 0;
$errmsg = '';
$Parser = new QuickBooks_XML_Parser($xml);
if ($Doc = $Parser->parse($errnum, $errmsg))
{
$Root = $Doc->getRoot();
$List = $Root->getChildAt('QBXML/QBXMLMsgsRs/PurchaseOrderQueryRs');
// TODO
}
return true;
}
/**
* Handle a 500 not found error from QuickBooks
*
* Instead of returning empty result sets for queries that don't find any
* records, QuickBooks returns an error message. This handles those error
* messages, and acts on them by adding the missing item to QuickBooks.
*/
function _quickbooks_error_e500_notfound($requestID, $user, $action, $ID, $extra, &$err, $xml, $errnum, $errmsg)
{
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
if ($action == QUICKBOOKS_IMPORT_INVOICE)
{
return true;
}
else if ($action == QUICKBOOKS_IMPORT_CUSTOMER)
{
return true;
}
else if ($action == QUICKBOOKS_IMPORT_SALESORDER)
{
return true;
}
else if ($action == QUICKBOOKS_IMPORT_ITEM)
{
return true;
}
else if ($action == QUICKBOOKS_IMPORT_PURCHASEORDER)
{
return true;
}
return false;
}
function _quickbooks_error_catchall($requestID, $user, $action, $ID, $extra, &$err, $xml, $errnum, $errmsg)
{
$message = '';
$message .= 'Request ID: ' . $requestID . "\r\n";
$message .= 'User: ' . $user . "\r\n";
$message .= 'Action: ' . $action . "\r\n";
$message .= 'ID: ' . $ID . "\r\n";
$message .= 'Extra: ' . print_r($extra, true) . "\r\n";
//$message .= 'Error: ' . $err . "\r\n";
$message .= 'Error number: ' . $errnum . "\r\n";
$message .= 'Error message: ' . $errmsg . "\r\n";
mail(QB_QUICKBOOKS_MAILTO,
'QuickBooks error occured!',
$message);
}

So I see at least a few potential issues, and have some questions. First, some background --
Iterators in QuickBooks work just like a database cursor. Part of that means that you can't have two iterators open at once - you have to finish with one iterator before you start and/or continue another iterator. So if you do this, you'll get the error you're seeing:
Start iterator #1
Fetch some data from iterator #1
Start iterator #2
Try to fetch some data from iterator #1 again
That will fail because starting the second iterator has closed/killed the first iterator. This is probably what's happening to you.
What is this?
$extra['iteratorID'] = $saved_iterator_id; // From Table
That looks like an undefined variable to me. Did you check your PHP error logs?
Why are you saving this to a table, and what table are you saving it to? Where in the code are you pulling it back out of the table? I don't see any code that gets it from the table.

Related

QBSDK iterator support in consolibyte

I have to fetch a large amount of data and so I want to use the iterator feature of QBSDK. I wanted to know if consolibyte library has any inherent support for it.
Several of the examples included with the lib use iterators:
https://github.com/consolibyte/quickbooks-php/blob/master/docs/web_connector/example_web_connector_import.php#L321
function _quickbooks_invoice_import_request($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale)
{
// Iterator support (break the result set into small chunks)
$attr_iteratorID = '';
$attr_iterator = ' iterator="Start" ';
if (empty($extra['iteratorID']))
{
// This is the first request in a new batch
$last = _quickbooks_get_last_run($user, $action);
_quickbooks_set_last_run($user, $action); // Update the last run time to NOW()
// Set the current run to $last
_quickbooks_set_current_run($user, $action, $last);
}
else
{
// This is a continuation of a batch
$attr_iteratorID = ' iteratorID="' . $extra['iteratorID'] . '" ';
$attr_iterator = ' iterator="Continue" ';
$last = _quickbooks_get_current_run($user, $action);
}
// Build the request
$xml = '<?xml version="1.0" encoding="utf-8"?>
<?qbxml version="' . $version . '"?>
<QBXML>
<QBXMLMsgsRq onError="stopOnError">
<InvoiceQueryRq ' . $attr_iterator . ' ' . $attr_iteratorID . ' requestID="' . $requestID . '">
...

Get error response from APNS when send push notification?

I have used this code to send push notification to iOS device. When the device token is correct it work great, I can receive notification and the response from APNS to my server is nothing. But when I change the device token to wrong token, I still receive nothing from APNS, there is no error code in reponse. I want to get error response from APNS when pushing invalid token.
<?php
error_reporting(0);
header("Content-type: application/json; charset=utf-8");
//include "dbconn.php";
$data = array();
$json= array();
$users = array();
$deviceToken = 'gddhfkgjhkjkgkgkgkgkjk2c2967b1fbc03fbebccb4109bbc509493938dcf70c3e674b949e1f81475ea742';
// Put your private key's passphrase here:
$passphrase = 'testpush1234';
// Put your alert message here:
$message = 'My first push notification!';
$token = $deviceToken;
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
# Open a connection to the APNS server
$fp = stream_socket_client('ssl://gateway.push.apple.com:2195', $err,
$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
if (!$fp){
echo "Error fp: ".$err;
exit;
}
echo "\n".'Connected to APNS Push Notification' . PHP_EOL;
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
$payload = json_encode($body);
//echo "\n$payload" . PHP_EOL;
// Build the binary notification
$apple_expiry = time() + (90 * 24 * 60 * 60);
$apple_identifier = 1;
$msg = pack("C", 1) . pack("N", $apple_identifier) . pack("N", $apple_expiry) . pack("n", 32) . pack('H*', str_replace(' ', '', $deviceToken)) . pack("n", strlen($payload)) . $payload;
// Send it to the server
$result = fwrite($fp, $msg, strlen($msg));
//set blocking
stream_set_blocking($fp,0);
//Check response
checkAppleErrorResponse($fp);
// Close the connection to the server
fclose($fp);
function checkAppleErrorResponse($fp) {
$apple_error_response = fread($fp, 6); //byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID). Should return nothing if OK.
//NOTE: Make sure you set stream_set_blocking($fp, 0) or else fread will pause your script and wait forever when there is no response to be sent.
echo 'apple_error_response ' . $apple_error_response;
if ($apple_error_response) {
$error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response); //unpack the error response (first byte 'command" should always be 8)
echo "==============";
echo $error_response;
echo "==============";
if ($error_response['status_code'] == '0') {
$error_response['status_code'] = '0-No errors encountered';
} else if ($error_response['status_code'] == '1') {
$error_response['status_code'] = '1-Processing error';
} else if ($error_response['status_code'] == '2') {
$error_response['status_code'] = '2-Missing device token';
} else if ($error_response['status_code'] == '3') {
$error_response['status_code'] = '3-Missing topic';
} else if ($error_response['status_code'] == '4') {
$error_response['status_code'] = '4-Missing payload';
} else if ($error_response['status_code'] == '5') {
$error_response['status_code'] = '5-Invalid token size';
} else if ($error_response['status_code'] == '6') {
$error_response['status_code'] = '6-Invalid topic size';
} else if ($error_response['status_code'] == '7') {
$error_response['status_code'] = '7-Invalid payload size';
} else if ($error_response['status_code'] == '8') {
$error_response['status_code'] = '8-Invalid token';
} else if ($error_response['status_code'] == '255') {
$error_response['status_code'] = '255-None (unknown)';
} else {
$error_response['status_code'] = $error_response['status_code'].'-Not listed';
}
echo '<br><b>+ + + + + + ERROR</b> Response Command:<b>' . $error_response['command'] . '</b> Identifier:<b>' . $error_response['identifier'] . '</b> Status:<b>' . $error_response['status_code'] . '</b><br>';
echo 'Identifier is the rowID (index) in the database that caused the problem, and Apple will disconnect you from server. To continue sending Push Notifications, just start at the next rowID after this Identifier.<br>';
return true;
}
echo "\nPush respnse OK" . PHP_EOL;
return false;
}
?>

Quickbooks PHP Integration error " No registered functions for action"

I want to connect to database of Quickbooks desktop version using PHP. For this Iam using the PHP dev kit downloaded from the link http://consolibyte.com/downloads/quickbooks-php-devkit/ .
Here I connected to Quickbooks databse successfully using the given code in inside docs/example_app_web_connector folder. In it adding of customer to databse is working. But when I tried to add function for fetching all employees from the database, it shows error like No registered functions for action.
Where should I include this function to work perfectly??
In functions page I coded like this:-
function _quickbooks_customer_query_response($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents)
{
if (!empty($idents['iteratorRemainingCount']))
{
// Queue up another request
$Queue = QuickBooks_WebConnector_Queue_Singleton::getInstance();
$Queue->enqueue(QUICKBOOKS_QUERY_CUSTOMER, null, 0, array( 'iteratorID' => $idents['iteratorID'] ));
}
// This piece of the response from QuickBooks is now stored in $xml. You
// can process the qbXML response in $xml in any way you like. Save it to
// a file, stuff it in a database, parse it and stuff the records in a
// database, etc. etc. etc.
//
// The following example shows how to use the built-in XML parser to parse
// the response and stuff it into a database.
// Import all of the records
$errnum = 0;
$errmsg = '';
$Parser = new QuickBooks_XML_Parser($xml);
if ($Doc = $Parser->parse($errnum, $errmsg))
{
$Root = $Doc->getRoot();
$List = $Root->getChildAt('QBXML/QBXMLMsgsRs/CustomerQueryRs');
foreach ($List->children() as $Customer)
{
$values = array(
'ListID' => $Customer->getChildDataAt('CustomerRet ListID'),
'FullName' => $Customer->getChildDataAt('CustomerRet FullName'),
'FirstName' => $Customer->getChildDataAt('CustomerRet FirstName'),
'LastName' => $Customer->getChildDataAt('CustomerRet LastName'),
);
print_r($values);
foreach ($Customer->children() as $Node)
{
// Be careful! Custom field names are case sensitive!
if ($Node->name() === 'DataExtRet' and
$Node->getChildDataAt('DataExtRet DataExtName') == 'Your Custom Field Name Goes Here')
{
$values['Your Custom Field Names Goes Here'] = $Node->getChildDataAt('DataExtRet DataExtValue');
}
}
$fullname=$values['FullName'];
$firstname=$values['FirstName'];
$lastname=$values['LastName'];
$listid=$values['ListID'];
// Do something with that data...
mysql_query("INSERT INTO `my_customer_table` (name,fname,lname,quickbooks_listid) VALUES ('$fullname','$firstname','$lastname','$listid') ");
exit;
}
}
return true;
}
And in main code called like this
:-
require_once dirname(FILE) . '/config.php';
$Queue = new QuickBooks_WebConnector_Queue($dsn);
$fg=$Queue->enqueue(QUICKBOOKS_QUERY_CUSTOMER);
Sorry for trouble.. atlast got it... Actually In that folder their is a file named qbwc.php that I didnt noticed. In it we have to add functions inside $map variable...
<?php
require_once dirname(__FILE__) . '/config.php';
/**
* Require some callback functions
*/
require_once dirname(__FILE__) . '/functions.php';
// Map QuickBooks actions to handler functions
$map = array(
QUICKBOOKS_ADD_CUSTOMER => array( '_quickbooks_customer_add_request', '_quickbooks_customer_add_response' ),
QUICKBOOKS_QUERY_CUSTOMER => array( '_quickbooks_customer_query_request', '_quickbooks_customer_query_response' ),
);
// This is entirely optional, use it to trigger actions when an error is returned by QuickBooks
$errmap = array(
'*' => '_quickbooks_error_catchall', // Using a key value of '*' will catch any errors which were not caught by another error handler
);
// An array of callback hooks
$hooks = array(
);
// Logging level
$log_level = QUICKBOOKS_LOG_DEVELOP; // Use this level until you're sure everything works!!!
// What SOAP server you're using
$soapserver = QUICKBOOKS_SOAPSERVER_BUILTIN; // A pure-PHP SOAP server (no PHP ext/soap extension required, also makes debugging easier)
$soap_options = array( // See http://www.php.net/soap
);
$handler_options = array(
'deny_concurrent_logins' => false,
'deny_reallyfast_logins' => false,
); // See the comments in the QuickBooks/Server/Handlers.php file
$driver_options = array( // See the comments in the QuickBooks/Driver/<YOUR DRIVER HERE>.php file ( i.e. 'Mysql.php', etc. )
);
$callback_options = array(
);
// Create a new server and tell it to handle the requests
// __construct($dsn_or_conn, $map, $errmap = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_PHP, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array(), $callback_options = array()
$Server = new QuickBooks_WebConnector_Server($dsn, $map, $errmap, $hooks, $log_level, $soapserver, QUICKBOOKS_WSDL, $soap_options, $handler_options, $driver_options, $callback_options);
$response = $Server->handle(true, true);
?>

Back end for iOS app with relational databases

I am currently trying to build an ios application that will need to be able to retrieve and store data in a remote relational database. I have investigated using RestKit for the front end, which seems adequate. I have experience developing with both mysql and php, though I am not sure where to start in getting a web server to send information that is not html. (php enhanced html, that is)
I would appreciate any input on where I might get started. Thanks in advance!
I use PHP to query a mySQL database on my server and format the response as XML which I then parse in the app.
Here is a PHP example from my server....This also sends a size response to allow the App to track the download completion percentage and a file name response too. Also, if the database is empty, it will send a dummy record.
<?php
header("Content-type: text/text");
//get the values from the url query string
$user = $_GET['user'];
$pass = $_GET['password'];
$db = $_GET['database'];
$dbhost = 'localhost';
//create connection to mysql or tell user something has gone wrong
$connection = mysql_connect( $dbhost, $user, $pass ) or die('Could not connect to MySQL');
//select database or tell user something has gone wrong
mysql_select_db( $db ) or die('Could not select database');
// Construct and run query.
$sql = 'SELECT * FROM itemInfo WHERE itemStatus = "active" ORDER BY itemSortKey ASC';
//echo $sql;
$result = mysql_query($sql) or die('<result>fail</result>');
$xml_output = "<?xml version=\"1.0\"?>\n";
$xml_output .= "<bodyimage>\n";
while($record = mysql_fetch_assoc($result)) {
$xml_output .= "\t<storeitems>\n";
$xml_output .= "\t\t<itemSortKey>" . $record['itemSortKey'] . "</itemSortKey>\n";
$xml_output .= "\t\t<itemDesc>" . $record['itemDesc'] . "</itemDesc>\n";
$xml_output .= "\t\t<itemPrice>" . $record['itemPrice'] . "</itemPrice>\n";
$xml_output .= "\t\t<productID>" . $record['productID'] . "</productID>\n";
$xml_output .= "\t\t<field1>" . $record['field1'] . "</field1>\n";
$xml_output .= "\t\t<field2>" . $record['field2'] . "</field2>\n";
$xml_output .= "\t\t<field3>" . $record['field3'] . "</field3>\n";
$xml_output .= "\t\t<field4>" . $record['field4'] . "</field4>\n";
$xml_output .= "\t\t<dateCreated>" . $record['dateCreated'] . "</dateCreated>\n";
$xml_output .= "\t\t<dateModified>" . $record['dateModified'] . "</dateModified>\n";
// Escaping illegal characters
//$row['text'] = str_replace("&", "&", $row['text']);
//$row['text'] = str_replace("<", "<", $row['text']);
//$row['text'] = str_replace(">", ">", $row['text']);
//$row['text'] = str_replace("\"", """, $row['text']);
//$xml_output .= "\t\t<text>" . $row['text'] . "</text>\n";
$xml_output .= "\t</storeitems>\n";
//add this record to the output
//$output = '|facilityname|' .$record['facilityname'] . '|/facilityname||customername|' . $record['customername'] . '|/customername||imageurl|' . $record['imageurl'] . '|/imageurl|';
//print $output;
}
$xml_output .= "</bodyimage>";
header("HTTP/1.0 200 OK");
header('Content-type: text/xml');
header('Content-Disposition: attachment; filename="storeitems.xml"');
header("Content-Length: ".strlen ( $xml_output ));
// redundent.....$xml_output .= "</bmrcontrol>";
//echo strlen( $xml_output );
//the next statement relates if there are NO lines in the table - it sends a message
if ( strlen( $xml_output ) == 61 ) {
$xml_output = "<?xml version=\"1.0\"?>\n";
$xml_output .= "<bodyimage>\n";
$xml_output .= "\t<storeitems>\n";
$xml_output .= "\t\t<itemSortKey>aaaa</itemSortKey>\n";
$xml_output .= "\t\t<itemDesc>Store Items Missing</itemDesc>\n";
$xml_output .= "\t\t<itemPrice>0.0</itemPrice>\n";
$xml_output .= "\t\t<productID>blank</productID>\n";
$xml_output .= "\t\t<field1>.</field1>\n";
$xml_output .= "\t\t<field2>.</field2>\n";
$xml_output .= "\t\t<field3>.</field3>\n";
$xml_output .= "\t\t<field4>.</field4>\n";
$xml_output .= "\t\t<dateCreated>2012-02-13 09:03:49</dateCreated>\n";
$xml_output .= "\t\t<dateModified>2012-02-13 09:03:49</dateModified>\n";
// Escaping illegal characters
//$row['text'] = str_replace("&", "&", $row['text']);
//$row['text'] = str_replace("<", "<", $row['text']);
//$row['text'] = str_replace(">", ">", $row['text']);
//$row['text'] = str_replace("\"", """, $row['text']);
//$xml_output .= "\t\t<text>" . $row['text'] . "</text>\n";
$xml_output .= "\t</storeitems>\n";
$xml_output .= "</bodyimage>";
}
echo $xml_output;
if(mysql_num_rows($result)) {
//mysql_close($connection);
}
else
die('<result>fail</result>');
//mysql_close($connection);
?>

How to get the total number of tweets, retweets and replies on a particular tag or account, in Twitter using its API?

I have a requirement to get the total no.of tweets, retweets and replies on a particular tag or user account. How to get these numbers efficiently? The numbers should be exact and not like 100+.
I also need to get the total no.of direct messages.
Using this site as a starting point I've been trying the same thing:
Pull Twitter feed into your site
<?php
require_once 'db-functions.inc.php' ; //custom database functions
function saveTweets($screen_name) {
global $link;
$screen_name = dbEscape(strtolower(trim($screen_name)));
if (!$screen_name) { echo "<p><strong>Error: No screen name declared.</strong></p>\n"; return false; }
$row = dbGetRow("SELECT `id` FROM `retweet` WHERE `screen_name`='$screen_name' ORDER BY `id` DESC LIMIT 1");
$last_id = $row['id'];
$url = "http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=$screen_name&count=1500&include_rts=true" ;
if ($last_id) { $url .= "&since_id=$last_id" ; }
$ch = curl_init($url);
curl_setopt ($ch, CURLOPT_RETURNTRANSFER, TRUE);
$xml = curl_exec ($ch);
curl_close ($ch);
$affected = 0;
$twelement = new SimpleXMLElement($xml);
foreach ($twelement->status as $status) {
$text = dbEscape(trim($status->text));
$time = strtotime($status->created_at);
$id = $status->id;
$retweet_count = $status->retweet_count;
dbQuery("INSERT INTO `twit` (`id`,`screen_name`,`time`,`text`,`hidden`,`retweet_count`) VALUES ('$id','$screen_name','$time','$text','n','$retweet_count')");
$affected = $affected + dbAffectedRows();
}
return "<p>".number_format($affected)." new tweets from $screen_name saved.</p>\n" ;
}
echo saveTweets('stackoverflow');
echo saveTweets('Apple');
echo saveTweets('Android');
echo saveTweets('Google');
?>
<h3>Stackoverflow</h3>
<?php
require_once 'databaseconnection.php' ; //database connection function
$result = dbQuery("SELECT * FROM `retweet` WHERE `hidden` != 'y' ORDER BY `retweet_count` DESC");
while ($row = mysql_fetch_array($result)) {
echo $row[0];
echo "<br>";
echo date("l, M j, Y, G:i a",$row[3]);
echo " : ";
echo stripslashes($row[2]);
echo "<br>";
echo stripslashes($row[4]);
echo "<br>Retweet: ";
echo stripslashes($row[6]);
echo "<br>";
echo "<br>";
}
?>
At the present time this doesn't have an "UPDATE" clause in it to take into account the retweet_count increasing, and you can remove:
return "<p>".number_format($affected)." new tweets from $screen_name saved.</p>\n" ;
If you don't need to see what has been updated.
Hope that helps

Resources