/** * * */ public static function initialize($dsn_or_conn, $options = array(), $hooks = array(), $log_level = null) { if ($obj = QuickBooks_Driver_Singleton::getInstance($dsn_or_conn, $options, $hooks, $log_level)) { return true; } return false; }
public static function prebuiltBridgeResponse($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents, $config = array()) { //print_r($config); //print_r($extra); if (is_object($config['__transport_output'])) { $Transport =& $config['__transport_output']; $Driver = QuickBooks_Driver_Singleton::getInstance(); $Transport->output($Driver, $extra['__method'], $action, $ID, $extra['__replace'], $extra['__priority'], $extra['__extra'], $xml, $extra['__id']); } }
/** * Handle a QuickBooks error indicating that nothing matched a search * * For whatever strange reason, instead of returning an empty result set * when you do a search that returns no results, QuickBooks instead returns * an error message. The error message code might be either 1 or 500 * depending on what filters you use in your query. * * @TODO This should use the QuickBooks_Callbacks class, and not it's own custom callback code * * @param string $requestID * @param string $user * @param string $action * @param mixed $ident * @param mixed $extra * @param string $err * @param string $xml * @param integer $errnum * @param string $errmsg * @param array $config * @return boolean */ public static function e500_notfound($requestID, $user, $action, $ident, $extra, &$err, $xml, $errnum, $errmsg, $config) { //$requestID, $user, $action, $ident, $extra, $errerr, $xml, $errnum, $errmsg, $this->_callback_config // Not found, *still call the callback!* /* $extra['callbacks'], $method, $action, $ID, $err, $qbxml, $qbobject, $qbres */ // Get the driver instance $Driver = QuickBooks_Driver_Singleton::getInstance(); if (!isset($extra['callbacks'])) { $extra['callbacks'] = array(); } if (!is_array($extra['callbacks'])) { $extra['callbacks'] = array($extra['callbacks']); } $method = null; if (isset($extra['method'])) { $method = $extra['method']; } $err = ''; $qbobject = new QuickBooks_Iterator(array()); $qbres = null; foreach ($extra['callbacks'] as $func) { if (false !== strpos($func, '::') and true) { // Callback *static method* $tmp = explode('::', $func); $return = call_user_func(array($tmp[0], $tmp[1]), $method, $action, $ident, $err, $xml, $qbobject, $qbres); } else { if (function_exists($func)) { // Callback *function* $return = call_user_func($func, $method, $action, $ident, $err, $xml, $qbobject, $qbres); } else { $err = 'Could not call function or method: ' . $func; $Driver->log('API: ' . $err, null, QUICKBOOKS_LOG_NORMAL); return false; } } if (!$return) { break; } } if ($err) { return false; } return true; }
/** * @TODO Change this to return false by default, and only catch the specific errors we're concerned with. * */ public static function catchall($requestID, $user, $action, $ident, $extra, &$err, $xml, $errnum, $errmsg, $config) { $Driver = QuickBooks_Driver_Singleton::getInstance(); /* $Parser = new QuickBooks_XML($xml); $errnumTemp = 0; $errmsgTemp = ''; $Doc = $Parser->parse($errnumTemp, $errmsgTemp); $Root = $Doc->getRoot(); $emailStr = var_export($Root->children(), true); $List = $Root->getChildAt('QBXML QBXMLMsgsRs '.QuickBooks_Utilities::actionToResponse($action)); $Node = current($List->children()); */ $map = array(); $others = array(); QuickBooks_SQL_Schema::mapToSchema(trim(QuickBooks_Utilities::actionToXMLElement($action)), QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $map, $others); $sqlObject = new QuickBooks_SQL_Object($map[0], trim(QuickBooks_Utilities::actionToXMLElement($action))); $table = $sqlObject->table(); switch ($errnum) { case 1: // These errors occur when we search for something and it doesn't exist // These errors occur when we search for something and it doesn't exist case 500: // i.e. we query for invoices modified since xyz, but there are none that have been modified since then // This isn't really an error, just ignore it return true; case 1000: // An internal error occured // @todo Hopefully at some point we'll have a better idea of how to handle this error... return true; case 3200: // Ignore EditSequence errors (the record will be picked up and a conflict reported next time it runs... maybe?) // @todo Think about this one more return true; case 3250: // This feature is not enabled or not available in this version of QuickBooks. // Do nothing (this can be safely ignored) return true; case 3100: // Name of List Element is already in use. $multipart = array(QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ident); $sqlObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER, $errnum); $sqlObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_MESSAGE, $errmsg); $Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $sqlObject, array($multipart)); break; case 3260: // Insufficient permission level to perform this action. // There's nothing we can do about this, if they don't grant the user permission, just skip it return true; case 3200: // The provided edit sequence is out-of-date. if (!($tmp = $Driver->get(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, array(QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ident)))) { return true; } switch ($config['conflicts']) { case QUICKBOOKS_SERVER_SQL_CONFLICT_LOG: $multipart = array(QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ident); $sqlObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER, $errnum); $sqlObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_MESSAGE, $errmsg); $Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $sqlObject, array($multipart)); break; case QUICKBOOKS_SERVER_SQL_CONFLICT_NEWER: $Parser = new QuickBooks_XML_Parser($xml); $errnumTemp = 0; $errmsgTemp = ''; $Doc = $Parser->parse($errnumTemp, $errmsgTemp); $Root = $Doc->getRoot(); $List = $Root->getChildAt('QBXML QBXMLMsgsRs ' . QuickBooks_Utilities::actionToResponse($action)); $TimeModified = $Root->getChildDataAt('QBXML QBXMLMsgsRs ' . QuickBooks_Utilities::actionToResponse($action) . ' ' . QuickBooks_Utilities::actionToXMLElement($action) . ' TimeModified'); $EditSequence = $Root->getChildDataAt('QBXML QBXMLMsgsRs ' . QuickBooks_Utilities::actionToResponse($action) . ' ' . QuickBooks_Utilities::actionToXMLElement($action) . ' EditSequence'); $multipart = array(QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ident); if (QuickBooks_Utilities::compareQBTimeToSQLTime($TimeModified, $tmp->get(QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY)) >= 0 && $config['mode'] != QUICKBOOKS_SERVER_SQL_MODE_WRITEONLY) { //@TODO: Make this get only a single item, not the whole table $Driver->queueEnqueue($user, QuickBooks_Utilities::convertActionToQuery($action), __FILE__, true, QUICKBOOKS_SERVER_SQL_CONFLICT_QUEUE_PRIORITY, $extra); } else { if (QuickBooks_Utilities::compareQBTimeToSQLTime($TimeModified, $tmp->get(QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY)) < 0) { //Updates the EditSequence without marking the row as resynced. $tmpSQLObject = new QuickBooks_SQL_Object($table, null); $tmpSQLObject->set("EditSequence", $EditSequence); $Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $tmpSQLObject, array($multipart)); $Driver->queueEnqueue($user, QuickBooks_Utilities::convertActionToMod($action), $tmp->get(QUICKBOOKS_DRIVER_SQL_FIELD_ID), true, QUICKBOOKS_SERVER_SQL_CONFLICT_QUEUE_PRIORITY, $extra); } else { //Trash it, set synced. $tmpSQLObject = new QuickBooks_SQL_Object($table, null); $tmpSQLObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_MESSAGE, "Read/Write Mode is WRITEONLY, and Conflict Mode is NEWER, and Quickbooks has Newer data, so no Update Occured."); $Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $tmpSQLObject, array($multipart)); } } break; case QUICKBOOKS_SERVER_SQL_CONFLICT_QUICKBOOKS: if ($config['mode'] == QUICKBOOKS_SERVER_SQL_MODE_READWRITE) { //@TODO: Make this get only a single item, not the whole table $Driver->queueEnqueue($user, QuickBooks_Utilities::convertActionToQuery($action), null, true, QUICKBOOKS_SERVER_SQL_CONFLICT_QUEUE_PRIORITY, $extra); $multipart = array(QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ident); $sqlObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER, $errnum); $sqlObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_MESSAGE, $errmsg); $Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $sqlObject, array($multipart)); //Use what's on quickbooks, and trash whatever is here. } else { $multipart = array(QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ident); $sqlObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER, $errnum); $sqlObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_MESSAGE, $errmsg); $Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $sqlObject, array($multipart)); // @TODO: Raise Notification that the conflicts level requires writing to SQL table, but Mode disallows this } break; case QUICKBOOKS_SERVER_SQL_CONFLICT_SQL: // Updates the EditSequence without marking the row as resynced. $tmpSQLObject = new QuickBooks_SQL_Object($table, null); $tmpSQLObject->set("EditSequence", $EditSequence); $Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $tmpSQLObject, array($multipart)); $Driver->queueEnqueue($user, QuickBooks_Utilities::convertActionToMod($action), $tmp->get(QUICKBOOKS_DRIVER_SQL_FIELD_ID), true, QUICKBOOKS_SERVER_SQL_CONFLICT_QUEUE_PRIORITY, $extra); break; case QUICKBOOKS_SERVER_SQL_CONFLICT_CALLBACK: break; default: break; } break; default: if (strstr($xml, 'statusSeverity="Info"') === false) { // } break; } // Please don't change this, it stops us from knowing what's actually // going wrong. If an error occurs, we should either catch it if it's // recoverable, or treated as a fatal error so we know about it and // can address it later. //return false; return true; }
<?php require_once '../../QuickBooks.php'; $instance = QuickBooks_Driver_Singleton::getInstance('mysql://root:@localhost/quickbooks'); $requestID = ''; $user = '******'; $ID = 'bla'; $extra = null; $err = ''; $xml = ''; $idents = array(); $last_action_time = 0; $last_actionident_time = 0; $action = 'CustomerQuery'; $xml = file_get_contents(dirname(__FILE__) . '/../docs/responses/CustomerQuery.xml'); QuickBooks_Server_SQL_Callbacks::CustomerQueryResponse($requestID, $user, $action, $ID, $extra, $err, $last_action_time, $last_actionident_time, $xml, $idents); $action = 'InvoiceQuery'; $xml = file_get_contents(dirname(__FILE__) . '/../docs/responses/InvoiceQuery.xml'); QuickBooks_Server_SQL_Callbacks::InvoiceQueryResponse($requestID, $user, $action, $ID, $extra, $err, $last_action_time, $last_actionident_time, $xml, $idents); $action = 'AccountQuery'; $xml = file_get_contents(dirname(__FILE__) . '/../docs/responses/AccountQuery.xml'); QuickBooks_Server_SQL_Callbacks::AccountQueryResponse($requestID, $user, $action, $ID, $extra, $err, $last_action_time, $last_actionident_time, $xml, $idents); $action = 'EmployeeQuery'; $xml = file_get_contents(dirname(__FILE__) . '/../docs/responses/EmployeeQuery.xml'); QuickBooks_Server_SQL_Callbacks::EmployeeQueryResponse($requestID, $user, $action, $ID, $extra, $err, $last_action_time, $last_actionident_time, $xml, $idents); $action = 'ClassQuery'; $xml = file_get_contents(dirname(__FILE__) . '/../docs/responses/ClassQuery.xml'); QuickBooks_Server_SQL_Callbacks::ClassQueryResponse($requestID, $user, $action, $ID, $extra, $err, $last_action_time, $last_actionident_time, $xml, $idents); $action = 'ItemQuery'; $xml = file_get_contents(dirname(__FILE__) . '/../docs/responses/ItemQuery.xml'); QuickBooks_Server_SQL_Callbacks::ItemQueryResponse($requestID, $user, $action, $ID, $extra, $err, $last_action_time, $last_actionident_time, $xml, $idents);
/** * * * */ protected function _bridge() { $Driver = QuickBooks_Driver_Singleton::getInstance(); $count = 0; while ($this->_transport_in->ready()) { if ($this->_transport_in->input($Driver)) { $count++; } } return $count; }
/** * Used to build the xml that limits the results to only updated results * * @param string $user Same deal: pass along the $user parameter * @param string $action Ditto with $action parameter * @param array $extra Simply pass in the $extra value that is passed to the function you're calling this from. * @param boolean $filter_wrap * @return string */ protected static function _buildFilter($user, $action, $extra, $filter_wrap = false) { $Driver = QuickBooks_Driver_Singleton::getInstance(); $xml = ''; $type = ''; $key_prev = QuickBooks_Callbacks_SQL_Callbacks::_keySyncPrev($action); $key_curr = QuickBooks_Callbacks_SQL_Callbacks::_keySyncCurr($action); $module = __CLASS__; //$action = null; $type = null; $opts = null; // configRead($user, $module, $key, &$type, &$opts) $prev_sync_datetime = $Driver->configRead($user, $module, $key_prev, $type, $opts); // last sync started... if (!$prev_sync_datetime) { // If this query has *never* run before, let's get *all* of the records $timestamp = time() - 60 * 60 * 24 * 365 * 25; $prev_sync_datetime = date('Y-m-d', $timestamp) . 'T' . date('H:i:s', $timestamp); $extra = array(); // If an iterator exists, get rid of it (this should *never* happen... how could it?) // configWrite($user, $module, $key, $value, $type, $opts $Driver->configWrite($user, $module, $key_prev, $prev_sync_datetime, null); } // @TODO MAKE SURE THIS DOESN'T BREAK ANYTHING! $prev_sync_datetime = date('Y-m-d', strtotime($prev_sync_datetime) - 600) . 'T' . date('H:i:s', strtotime($prev_sync_datetime) - 600); if (!is_array($extra) or empty($extra['iteratorID'])) { // Start of a new iterator! // Store when we started to do this iterator (this will become the $prev_sync_datetime after we finish with this iterator) $curr_sync_datetime = date('Y-m-d') . 'T' . date('H:i:s'); $Driver->configWrite($user, $module, $key_curr, $curr_sync_datetime, null); if ($filter_wrap) { if ($action == QUICKBOOKS_QUERY_DELETEDLISTS or $action == QUICKBOOKS_QUERY_DELETEDTXNS) { $xml .= '<DeletedDateRangeFilter>' . "\n"; $xml .= ' <FromDeletedDate>' . $prev_sync_datetime . '</FromDeletedDate>' . "\n"; $xml .= '</DeletedDateRangeFilter>' . "\n"; } else { $xml .= '<ModifiedDateRangeFilter>' . "\n"; $xml .= ' <FromModifiedDate>' . $prev_sync_datetime . '</FromModifiedDate>' . "\n"; $xml .= '</ModifiedDateRangeFilter>' . "\n"; } } else { $xml .= '<FromModifiedDate>' . $prev_sync_datetime . '</FromModifiedDate>'; } } else { if ($filter_wrap) { if ($action == QUICKBOOKS_QUERY_DELETEDLISTS or $action == QUICKBOOKS_QUERY_DELETEDTXNS) { $xml .= '<DeletedDateRangeFilter>' . "\n"; $xml .= ' <FromDeletedDate>' . $prev_sync_datetime . '</FromDeletedDate>' . "\n"; $xml .= '</DeletedDateRangeFilter>' . "\n"; } else { $xml .= '<ModifiedDateRangeFilter>' . "\n"; $xml .= ' <FromModifiedDate>' . $prev_sync_datetime . '</FromModifiedDate>' . "\n"; $xml .= '</ModifiedDateRangeFilter>' . "\n"; } } else { $xml .= '<FromModifiedDate>' . $prev_sync_datetime . '</FromModifiedDate>'; } } return $xml; }
function this_hook_gets_called_when_orderlines_are_received_from_foxycart($requestID, $user, $hook, &$err, $hook_data, $callback_config) { $Driver = QuickBooks_Driver_Singleton::getInstance(); $Driver->log('Got in (4): ' . print_r($hook_data, true)); }
/** * * * You can run this server in one of three modes: * - QUICKBOOKS_SERVER_SQL_MODE_READONLY: Data will only be read from * QuickBooks; changes to data in the SQL database will never be * pushed back to QuickBooks. * - QUICKBOOKS_SERVER_SQL_MODE_WRITEONLY: Data will only be pushed to * QuickBooks, and nothing that already exists in QuickBooks will be * imported into the SQL database. * - QUICKBOOKS_SERVER_SQL_MODE_READWRITE: The server will do it's best to * try to import all QuickBooks data into the SQL database, and then * push changes that occur in either location to the other location. * The server will try to syncronise the two locations as much as is * possible. * * @param string $dsn_or_conn DSN-style connection string or an already opened connection to the driver * @param string $how_often The maximum time we wait between updates/syncs (you can use any valid interval: "1 hour", "15 minutes", 60, etc.) * @param char $mode The mode the server should run in (see constants above) * @param char $conflicts The steps towards update conflict resolution the server should take (see constants above) * @param mixed $users The user (or an array of users) who will be using the SQL server * @param array $map * @param array $onerror * @param string $wsdl * @param array $soap_options * @param array $handler_options * @param array $driver_options */ public function __construct($dsn_or_conn, $how_often, $mode, $delete, $conflicts, $users = null, $map = array(), $onerror = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_BUILTIN, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array(), $sql_options = array(), $callback_options = array()) { // $dsn_or_conn, $map, $onerror = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_BUILTIN, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array() if (!is_array($users)) { $users = array($users); } // Map of callback handlers $sql_map = array(); foreach (get_class_methods('QuickBooks_Server_SQL_Callbacks') as $method) { if (strtolower(substr($method, -7)) == 'request') { $action = substr($method, 0, -7); $sql_map[$action] = array('QuickBooks_Server_SQL_Callbacks::' . $action . 'Request', 'QuickBooks_Server_SQL_Callbacks::' . $action . 'Response'); } } // Default error handlers $sql_onerror = array('*' => 'QuickBooks_Server_SQL_Errors::catchall'); $sql_onerror = $this->_merge($sql_onerror, $onerror, false); // Default hooks $sql_hooks = array(QUICKBOOKS_HANDLERS_HOOK_LOGINSUCCESS => array('QuickBooks_Server_SQL_Callbacks::onAuthenticate')); // Merge with user-defined hooks $sql_hooks = $this->_merge($hooks, $sql_hooks, true); // @TODO Prefix these with _ so that people don't accidentally overwrite them $sql_callback_options = array('hooks' => $sql_hooks, 'conflicts' => $conflicts, 'mode' => $mode, 'delete' => $delete, 'recur' => QuickBooks_Utilities::intervalToSeconds($how_often), 'map' => $sql_map); //print_r($sql_options); //exit; $defaults = $this->_sqlDefaults($sql_options); $sql_callback_options['_only_query'] = $defaults['only_query']; $sql_callback_options['_dont_query'] = $defaults['dont_query']; $sql_callback_options['_only_add'] = $defaults['only_add']; $sql_callback_options['_dont_add'] = $defaults['dont_add']; $sql_callback_options['_only_modify'] = $defaults['only_modify']; $sql_callback_options['_dont_modify'] = $defaults['dont_modify']; // Merge default values with passed in values // (in this case, we are *required* to have these values present, so // we make sure that the SQL options override any user-defined options $sql_callback_options = $this->_merge($callback_options, $sql_callback_options, false); // Initialize the Driver singleton $Driver = QuickBooks_Driver_Singleton::getInstance($dsn_or_conn, $driver_options, $sql_hooks, $log_level); // $dsn_or_conn, $map, $onerror = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_BUILTIN, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array() parent::__construct($dsn_or_conn, $sql_map, $sql_onerror, $sql_hooks, $log_level, $soap, $wsdl, $soap_options, $handler_options, $driver_options, $sql_callback_options); }
/** * Converts a MySQL timestamp value to the timezone of the PHP server this script is running on. * * @deprecated This need to be removed and moved to a driver class! * * Expects $datetime in the formation of "YYYY-MM-DD HH:MM:SS" * * @TODO Double check that a lack of a Driver Instance properly returns false. * @TODO Investigate possible bug if within a few hours of daylight savings change. * @TODO This should *not* be in the QuickBooks_Utilties class, any database queries that arn't abstracted need to be in QuickBooks/Driver/Sql/your-sql-file-here.php */ public static function mysqlTZToPHPTZ($datetime) { $Driver = QuickBooks_Driver_Singleton::getInstance(); $sql = " SELECT UTC_TIME() AS theUtcTime, CURTIME() AS theCurTime "; $res = $Driver->query($sql, $errnum, $errmsg); if (!$res) { return false; } if (!($arr = $Driver->fetch($res))) { return false; } // get the time bits: $utcTime = explode(":", $arr['theUtcTime']); $curTime = explode(":", $arr['theCurTime']); // create unix timestamps for each // since we're calculating a relative time only: $utc_t = mktime($utcTime[0], $utcTime[1], $utcTime[2]); $cur_t = mktime($curTime[0], $curTime[1], $curTime[2]); $mysqlOffset = $cur_t - $utc_t; $phpOffset = (int) date('Z'); //mail("*****@*****.**","Offsets","MysqlOffset: ".($mysqlOffset)."\n\n\nPHPOffset: ".$phpOffset); $timezoneDiff = $mysqlOffset - $phpOffset; $tempTime = explode(" ", $datetime); if (count($tempTime) != 2) { return FALSE; } $mysqlTime = explode(":", $tempTime[1]); $mysql_t = mktime($mysqlTime[0], $mysqlTime[1], $mysqlTime[2]); $newMysqlTime = $mysql_t - $timezoneDiff; //mail("*****@*****.**","TimeZone Diff","TimeZone Diff: ".($timezoneDiff)); return $tempTime[0] . " " . date("H:i:s", $newMysqlTime); }
/** * QuickBooks API server class * * @param string $dsn_or_conn * @param array $map * @param array $onerror * @param array $hooks * @param integer $log_level * @param string $soap * @param string $wsdl * @param array $soap_options * @param array $handler_options * @param array $driver_options * @param array $callback_options */ public function __construct($dsn_or_conn, $user, $map = array(), $onerror = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_BUILTIN, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array(), $api_options = array(), $source_options = array(), $callback_options = array()) { // NORMAL: // $dsn_or_conn, $map, $onerror = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_BUILTIN, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array() // SQL: // $dsn_or_conn, $how_often, $mode, $conflicts, $users = null, $map = array(), $onerror = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_BUILTIN, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array()) $api_map = array(); foreach (get_class_methods('QuickBooks_Callbacks_API_Callbacks') as $method) { if (strtolower(substr($method, -7)) == 'request') { $action = substr($method, 0, -7); $api_map[$action] = array('QuickBooks_Callbacks_API_Callbacks::' . $action . 'Request', 'QuickBooks_Callbacks_API_Callbacks::' . $action . 'Response'); } } // Register default API error handlers, and merge them with user-supplied handlers $api_onerror = array(1 => 'QuickBooks_Callbacks_API_Errors::e1_notfound', 500 => 'QuickBooks_Callbacks_API_Errors::e500_notfound'); // By default, we register our own error handlers. If the user wants to // register their own error handlers, their error handlers override // the API error handlers, and they'll be expected to handle all of // those errors themselves. $api_onerror = $this->_merge($api_onerror, $onerror, false); // Register default API hooks, and merge them with user-supplied hooks $api_hooks = array(); // Merge user-defined hooks with our API hooks $api_hooks = $this->_merge($api_hooks, $hooks, true); // Initialize the Driver singleton $tmp = QuickBooks_Driver_Singleton::getInstance($dsn_or_conn, $driver_options, $hooks, $log_level); $source_type = QUICKBOOKS_API_SOURCE_WEB; $source_dsn = null; // Initialize the API singleton $tmp = QuickBooks_API_Singleton::getInstance($dsn_or_conn, $user, $source_type, $source_dsn, $api_options, $source_options, $driver_options, $callback_options); // $dsn_or_conn, $map, $onerror = array(), $hooks = array(), $log_level = QUICKBOOKS_LOG_NORMAL, $soap = QUICKBOOKS_SOAPSERVER_BUILTIN, $wsdl = QUICKBOOKS_WSDL, $soap_options = array(), $handler_options = array(), $driver_options = array() parent::__construct($dsn_or_conn, $api_map, $api_onerror, $api_hooks, $log_level, $soap, $wsdl, $soap_options, $handler_options, $driver_options, $callback_options); }
/** * Map a type and application primary key to a QuickBooks EditSequence string * * @param string $type The type of object * @param mixed $ID The application primary key * @return string The QuickBooks EditSequence string */ protected static function _mapToEditSequence($func, $user, $type, $ID) { if (strlen($func)) { if (false === strpos($func, '::')) { return $func($type, $ID); } else { $tmp = explode('::', $func); return call_user_func(array($tmp[0], $tmp[1]), $type, $ID); } } else { $editsequence = ''; $extra = null; $Driver = QuickBooks_Driver_Singleton::getInstance(); $Driver->identToQuickBooks($user, $type, $ID, $editsequence, $extra); return $editsequence; } }
/** * @TODO Change this to return false by default, and only catch the specific errors we're concerned with. * */ public static function catchall($requestID, $user, $action, $ident, $extra, &$err, $xml, $errnum, $errmsg, $config) { $Driver = QuickBooks_Driver_Singleton::getInstance(); $ignore = array(QUICKBOOKS_IMPORT_DELETEDTXNS => true, QUICKBOOKS_QUERY_DELETEDTXNS => true, QUICKBOOKS_IMPORT_DELETEDLISTS => true, QUICKBOOKS_QUERY_DELETEDLISTS => true, QUICKBOOKS_VOID_TRANSACTION => true, QUICKBOOKS_DELETE_TRANSACTION => true, QUICKBOOKS_DELETE_LIST => true); if (isset($ignore[$action])) { // Ignore errors for these requests return true; } /* $Parser = new QuickBooks_XML($xml); $errnumTemp = 0; $errmsgTemp = ''; $Doc = $Parser->parse($errnumTemp, $errmsgTemp); $Root = $Doc->getRoot(); $emailStr = var_export($Root->children(), true); $List = $Root->getChildAt('QBXML QBXMLMsgsRs '.QuickBooks_Utilities::actionToResponse($action)); $Node = current($List->children()); */ $map = array(); $others = array(); QuickBooks_SQL_Schema::mapToSchema(trim(QuickBooks_Utilities::actionToXMLElement($action)), QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $map, $others); $object = new QuickBooks_SQL_Object($map[0], trim(QuickBooks_Utilities::actionToXMLElement($action))); $table = $object->table(); $existing = null; if ($table and is_numeric($ident)) { $multipart = array(QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ident); $existing = $Driver->get(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $multipart); } switch ($errnum) { case 1: // These errors occur when we search for something and it doesn't exist // These errors occur when we search for something and it doesn't exist case 500: // i.e. we query for invoices modified since xyz, but there are none that have been modified since then // This isn't really an error, just ignore it if ($action == QUICKBOOKS_DERIVE_CUSTOMER) { // Tried to derive, doesn't exist, add it $Driver->queueEnqueue($user, QUICKBOOKS_ADD_CUSTOMER, $ident, true, QuickBooks_Utilities::priorityForAction(QUICKBOOKS_ADD_CUSTOMER)); } else { if ($action == QUICKBOOKS_DERIVE_INVOICE) { // Tried to derive, doesn't exist, add it $Driver->queueEnqueue($user, QUICKBOOKS_ADD_INVOICE, $ident, true, QuickBooks_Utilities::priorityForAction(QUICKBOOKS_ADD_INVOICE)); } else { if ($action == QUICKBOOKS_DERIVE_RECEIVEPAYMENT) { // Tried to derive, doesn't exist, add it $Driver->queueEnqueue($user, QUICKBOOKS_ADD_RECEIVEPAYMENT, $ident, true, QuickBooks_Utilities::priorityForAction(QUICKBOOKS_ADD_RECEIVEPAYMENT)); } } } return true; case 1000: // An internal error occured // @todo Hopefully at some point we'll have a better idea of how to handle this error... return true; //case 3120: // 3120 errors are handled in the 3210 error handler section // break; //case 3120: // 3120 errors are handled in the 3210 error handler section // break; case 3170: // This list has been modified by another user. // This list has been modified by another user. case 3175: case 3176: case 3180: // This error can occur in several different situations, so we test per situation if (false !== strpos($errmsg, 'list has been modified by another user') or false !== strpos($errmsg, 'internals could not be locked') or false !== strpos($errmsg, 'failed to acquire the lock') or false !== strpos($errmsg, 'list element is in use')) { // This is *not* an error, we can just send the request again, and it'll go through just fine return true; } break; case 3200: // Ignore EditSequence errors (the record will be picked up and a conflict reported next time it runs... maybe?) if ($action == QUICKBOOKS_MOD_CUSTOMER and $existing) { // Queue up a derive customer request // Tried to derive, doesn't exist, add it $Driver->queueEnqueue($user, QUICKBOOKS_DERIVE_CUSTOMER, $ident, true, 9999, array('ListID' => $existing['ListID'])); } else { if ($action == QUICKBOOKS_MOD_INVOICE and $existing) { // Queue up a derive customer request // Tried to derive, doesn't exist, add it $Driver->queueEnqueue($user, QUICKBOOKS_DERIVE_INVOICE, $ident, true, 9999, array('TxnID' => $existing['TxnID'])); } } return true; case 3120: case 3210: //print_r($existing); //print('TXNID: [' . $existing['TxnID'] . ']'); // 3210: The "AppliedToTxnAdd payment amount" field has an invalid value "129.43". QuickBooks error message: You cannot pay more than the amount due. if ($action == QUICKBOOKS_ADD_RECEIVEPAYMENT and (false !== strpos($errmsg, 'pay more than the amount due') or false !== strpos($errmsg, 'cannot be found')) and $existing) { // If this happens, we're going to try to re-submit the payment, *without* the AppliedToTxn element $db_errnum = null; $db_errmsg = null; $Driver->query("\n\t\t\t\t\t\tUPDATE \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . "receivepayment_appliedtotxn \n\t\t\t\t\t\tSET \n\t\t\t\t\t\t\tqbsql_to_skip = 1 \n\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\tReceivePayment_TxnID = '%s' ", $db_errnum, $db_errmsg, null, null, array($existing['TxnID'])); return true; } break; case 3250: // This feature is not enabled or not available in this version of QuickBooks. // Do nothing (this can be safely ignored) return true; case 3260: // Insufficient permission level to perform this action. // Insufficient permission level to perform this action. case 3261: // The integrated application has no permission to ac... // There's nothing we can do about this, if they don't grant the user permission, just skip it return true; case 3100: // Name of List Element is already in use. break; case '0x8004040D': // The ticket parameter is invalid (how does this happen!?!) return true; } // This is our catch-all which marks the item as errored out if (strstr($xml, 'statusSeverity="Info"') === false) { $multipart = array(QUICKBOOKS_DRIVER_SQL_FIELD_ID => $ident); $object->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER, $errnum); $object->set(QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_MESSAGE, $errmsg); // Do not set the resync field, we want resync and modified timestamps to be different $update_resync_field = false; $update_discov_field = false; $update_derive_field = false; if ($table and is_numeric($ident)) { // Set the error message $Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $object, array($multipart), $update_resync_field, $update_discov_field, $update_derive_field); } } // Please don't change this, it stops us from knowing what's actually // going wrong. If an error occurs, we should either catch it if it's // recoverable, or treated as a fatal error so we know about it and // can address it later. //return false; // I'm changing it because otherwise the sync never completes if a // single error occurs... we need a way to skip errored-out records return true; }