public function addForm($MOD, $DO) { $this->_skin->assign('actions', QuickBooks_Utilities::listActions()); $users = array(); $iterator = $this->_driver->authView(0, 999); while ($arr = $iterator->next()) { $users[] = $arr['qb_username']; } $this->_skin->assign('users', $users); $this->_skin->assign('error', $this->_stat() == QUICKBOOKS_FRONTEND_MODULE_RECUR_ERROR_FAILURE); switch ($this->_stat()) { case QUICKBOOKS_FRONTEND_MODULE_RECUR_ERROR_SUCCESS: $this->_skin->assign('msg', 'Successfully added the event!'); break; case QUICKBOOKS_FRONTEND_MODULE_RECUR_ERROR_FAILURE: $this->_skin->assign('msg', 'Failed to add the event: ' . $this->_msg()); break; default: $this->_skin->assign('msg', ''); break; } $this->_skin->display('Recur/addForm.tpl'); }
/** * ReceiveResponseXML() method for the QuickBooks Web Connector - Receive and handle a resonse form QuickBooks * * The stdClass object passed as a parameter will have the following members: * - ->ticket The QuickBooks Web Connector ticket * - ->response An XML response message * - ->hresult Error code * - ->message Error message * * The sole data member of the returned object should be an integer. * - The data member should be -1 if an error occured and QBWC should call ->getLastError() * - Should be an integer 0 <= x < 100 to indicate success *and* that the application should continue to call ->sendRequestXML() at least one more time (more queued items still in the queue, the integer represents the percentage complete the total batch job is) * - Should be 100 to indicate success *and* that the queue has been exhausted * * The following user-defined hooks are invoked: * - QUICKBOOKS_HANDLERS_HOOK_RECEIVERESPONSEXML * * @param stdClass $obj * @return QuickBooks_Result_ReceiveResponseXML */ public function receiveResponseXML($obj) { $this->_driver->log('receiveResponseXML()', $obj->ticket, QUICKBOOKS_LOG_VERBOSE); if ($this->_driver->authCheck($obj->ticket)) { $user = $this->_driver->authResolve($obj->ticket); $hookdata = array('username' => $user, 'ticket' => $obj->ticket); $hookerr = ''; $this->_callHook($obj->ticket, QUICKBOOKS_HANDLERS_HOOK_RECEIVERESPONSEXML, null, null, null, null, $hookerr, null, array(), $hookdata); $this->_driver->log('Incoming XML response: ' . $obj->response, $obj->ticket, QUICKBOOKS_LOG_DEBUG); // Check if we got a error message... if (strlen($obj->message) or $this->_extractStatusCode($obj->response)) { if ($requestID = $this->_extractRequestID($obj->response)) { $errnum = $this->_extractStatusCode($obj->response); //$action = current(explode('|', $requestID)); //$ident = next(explode('|', $requestID)); $action = ''; $ident = ''; $this->_parseRequestID($requestID, $action, $ident); // Fetch the request that was processed and EXPERIENCED AN ERROR! $extra = ''; if ($current = $this->_driver->queueFetch($user, $action, $ident, QUICKBOOKS_STATUS_PROCESSING)) { if ($current['extra']) { $extra = unserialize($current['extra']); } } if ($obj->message) { $errmsg = $obj->message; } else { if ($status = $this->_extractStatusMessage($obj->response)) { $errmsg = $status; } } $errerr = ''; $continue = $this->_handleError($obj->ticket, $errnum, $errmsg, $requestID, $action, $ident, $extra, $errerr, $obj->response, array()); // $errnum, $errmsg, $requestID, $action, $ident, $extra, &$err, $xml, $qb_identifiers = array() if ($errerr) { // The error handler returned an error too... $this->_driver->log('An error occured while handling quickbooks error ' . $errnum . ': ' . $errmsg . ': ' . $errerr, $obj->ticket, QUICKBOOKS_LOG_NORMAL); } } else { $errerr = ''; $continue = $this->_handleError($obj->ticket, $obj->hresult, $obj->message, null, null, null, null, $errerr, $obj->response, array()); if ($errerr) { // The error handler returned an error too... $this->_driver->log('An error occured while handling generic error ' . $obj->hresult . ': ' . $obj->message . ': ' . $errerr, $obj->ticket, QUICKBOOKS_LOG_NORMAL); } } // Calculate the percentage done $progress = $this->_calculateProgress($obj->ticket); if (!$continue) { $progress = -1; } $this->_driver->log('Transaction error at ' . $progress . '% complete... ', $obj->ticket, QUICKBOOKS_LOG_VERBOSE); return new QuickBooks_Result_ReceiveResponseXML($progress); } $action = null; $ident = null; $requestID = null; if ($requestID = $this->_extractRequestID($obj->response)) { //$action = current(explode('|', $requestID)); //$ident = end(explode('|', $requestID)); $action = ''; $ident = ''; $this->_parseRequestID($requestID, $action, $ident); // Fetch the request that's being processed $extra = ''; if ($current = $this->_driver->queueFetch($user, $action, $ident, QUICKBOOKS_STATUS_PROCESSING)) { if ($current['extra']) { $extra = unserialize($current['extra']); } } // Update the status to success (no error occured) $this->_driver->queueStatus($obj->ticket, $action, $ident, QUICKBOOKS_STATUS_SUCCESS); } // Extract ListID, TxnID, etc. from the response $identifiers = $this->_extractIdentifiers($obj->response); //$this->_driver->log(var_export($identifiers, true), $obj->ticket, QUICKBOOKS_LOG_VERBOSE); // Auto-map $ident unique identifier from web application to the QuickBooks ListID or TxnID if ($this->_config['map_application_identifiers']) { $adds = QuickBooks_Utilities::listActions('*Add*'); $mods = QuickBooks_Utilities::listActions('*Mod*'); $qbkey = QuickBooks_Utilities::keyForAction($action); $type = QuickBooks_Utilities::actionToObject($action); $EditSequence = ''; if (isset($identifiers['EditSequence'])) { $EditSequence = $identifiers['EditSequence']; } if (in_array($action, $adds) and isset($identifiers[$qbkey]) and $type) { // Try to map the $ident to the QuickBooks identifier $this->_driver->identMap($user, $type, $ident, $identifiers[$qbkey], $EditSequence); } else { if (in_array($action, $mods) and isset($identifiers[$qbkey]) and $type) { // Try to map the $ident to the QuickBooks identifier $this->_driver->identMap($user, $type, $ident, $identifiers[$qbkey], $EditSequence); } } } $err = null; $last_action_time = $this->_driver->queueActionLast($user, $action); $last_actionident_time = $this->_driver->queueActionIdentLast($user, $action, $ident); $this->_callMappedFunction(1, $user, $action, $ident, $extra, $err, $last_action_time, $last_actionident_time, $obj->response, $identifiers); // Calculate the percentage done $progress = $this->_calculateProgress($obj->ticket); if ($err) { $errerr = ''; $continue = $this->_handleError($obj->ticket, QUICKBOOKS_ERROR_HANDLER, $err, $requestID, $action, $ident, $extra, $errerr, $obj->response, $identifiers); if (!$continue) { $progress = -1; } } $this->_driver->log($progress . '% complete... ', $obj->ticket, QUICKBOOKS_LOG_VERBOSE); return new QuickBooks_Result_ReceiveResponseXML($progress); } return new QuickBooks_Result_ReceiveResponseXML(-1); }
/** * SendRequestXML method for the QuickBooks Web Connector SOAP server - Generate and send a request to QuickBooks * * The QuickBooks Web Connector calls this method to ask for things to do. * So, calling this method is the Web Connectors way of saying: "Please * send me a command so that I can pass that command on to QuickBooks." * After it passes the command to QuickBooks, it will pass the response * back via a call to receiveResponseXML(). * * The stdClass object passed as a parameter should contain these members: * - ticket The login session ticket * - strHCPResponse * - strCompanyFileName * - qbXMLCountry The country code for whatever version of QuickBooks is sitting behind the Web Connector * - qbXMLMajorVers The major version code of the QuickBooks web connector * - qbXMLMinorVers The minor version code of the QuickBooks web connector * * You should return either an empty string "" to signal an error state, or * a valid qbXML or qbposXML request. * * The following user-defined hooks are invoked by this method: * - QUICKBOOKS_HANDLERS_HOOK_SENDREQUESTXML * * @param stdClass $obj * @return QuickBooks_Result_SendRequestXML */ public function sendRequestXML($obj) { //$this->_driver->log('sendRequestXML()', $obj->ticket, QUICKBOOKS_LOG_VERBOSE); $this->_log('sendRequestXML()', $obj->ticket, QUICKBOOKS_LOG_VERBOSE); if ($this->_driver->authCheck($obj->ticket)) { $user = $this->_driver->authResolve($obj->ticket); $hookdata = array('username' => $user, 'ticket' => $obj->ticket, 'strHCPResponse' => $obj->strHCPResponse, 'strCompanyFileName' => $obj->strCompanyFileName, 'qbXMLCountry' => $obj->qbXMLCountry, 'qbXMLMajorVers' => $obj->qbXMLMajorVers, 'qbXMLMinorVers' => $obj->qbXMLMinorVers); $hookerr = ''; $this->_callHook($obj->ticket, QUICKBOOKS_HANDLERS_HOOK_SENDREQUESTXML, null, null, null, null, $hookerr, null, array(), $hookdata); // _callHook($ticket, $hook, $requestID, $action, $ident, $extra, &$err, $xml = '', $qb_identifiers = array(), $hook_data = array()) // Move recurring events which are due to run to the queue table // We *CAN'T* re-register recurring events here, otherwise, we run // the risk of re-adding an event which has occured, *before* the // entire session has finishing running. Thus, we'd create an // infinite loop of web connector that would never end. //$this->_handleRecurringEvents($obj->ticket); if ($next = $this->_driver->queueDequeue($user, true)) { //$this->_driver->log('Dequeued: ( ' . $next['qb_action'] . ', ' . $next['ident'] . ' ) ', $obj->ticket, QUICKBOOKS_LOG_DEBUG); $this->_log('Dequeued: ( ' . $next['qb_action'] . ', ' . $next['ident'] . ' ) ', $obj->ticket, QUICKBOOKS_LOG_DEBUG); //$this->_driver->queueStatus($obj->ticket, $next['qb_action'], $next['ident'], QUICKBOOKS_STATUS_PROCESSING); $this->_driver->queueStatus($obj->ticket, $next['quickbooks_queue_id'], QUICKBOOKS_STATUS_PROCESSING); /* // Here's a strange case, interactive mode handler if ($next['qb_action'] == QUICKBOOKS_INTERACTIVE_MODE) { // Set the error to "Interactive mode" $this->_driver->errorLog($obj->ticket, QUICKBOOKS_ERROR_OK, QUICKBOOKS_INTERACTIVE_MODE); // This will cause ->getLastError() to be called, and ->getLastError() will then return the string "Interactive mode" which will cause QuickBooks to call ->getInteractiveURL() and start an interactive session... I think...? return new QuickBooks_Result_SendRequestXML(''); } */ $extra = ''; if ($next['extra']) { $extra = unserialize($next['extra']); } $err = ''; $xml = ''; //$last_action_time = $this->_driver->queueActionLast($user, $next['qb_action']); //$last_actionident_time = $this->_driver->queueActionIdentLast($user, $next['qb_action'], $next['ident']); $last_action_time = null; $last_actionident_time = null; // Call the mapped function that should generate an appropriate qbXML request $xml = $this->_callMappedFunction(0, $user, $next['quickbooks_queue_id'], $next['qb_action'], $next['ident'], $extra, $err, $last_action_time, $last_actionident_time, $obj->qbXMLMajorVers . '.' . $obj->qbXMLMinorVers, $obj->qbXMLCountry, $next['qbxml']); // Make sure there's no whitespace around it $xml = trim($xml); // NoOp can be returned to skip this current operation. This will cause getLastError // to be called, at which point NoOp should be returned to tell the Web // Connector to then pause for 5 seconds before asking for another request. if ($xml == QUICKBOOKS_NOOP) { $this->_driver->errorLog($obj->ticket, 0, QUICKBOOKS_NOOP); // Mark it as a NoOp to remove it from the queue //$this->_driver->queueStatus($obj->ticket, $next['qb_action'], $next['ident'], QUICKBOOKS_STATUS_NOOP, 'Handler function returned: ' . QUICKBOOKS_NOOP); $this->_driver->queueStatus($obj->ticket, $next['quickbooks_queue_id'], QUICKBOOKS_STATUS_NOOP, 'Handler function returned: ' . QUICKBOOKS_NOOP); return new QuickBooks_WebConnector_Result_SendRequestXML(''); } // If the requestID="..." attribute was not specified, we can try to automatically add it to the request $requestID = null; if (!($requestID = $this->_extractRequestID($xml)) and $this->_config['autoadd_missing_requestid']) { // Find the <DoSomethingRq tag foreach (QuickBooks_Utilities::listActions() as $action) { $request = QuickBooks_Utilities::actionToRequest($action); if (false !== strpos($xml, '<' . $request . ' ')) { //$xml = str_replace('<' . $request . ' ', '<' . $request . ' requestID="' . $this->_constructRequestID($next['qb_action'], $next['ident']) . '" ', $xml); $xml = str_replace('<' . $request . ' ', '<' . $request . ' requestID="' . $next['quickbooks_queue_id'] . '" ', $xml); break; } else { if (false !== strpos($xml, '<' . $request . '>')) { //$xml = str_replace('<' . $request . '>', '<' . $request . ' requestID="' . $this->_constructRequestID($next['qb_action'], $next['ident']) . '">', $xml); $xml = str_replace('<' . $request . '>', '<' . $request . ' requestID="' . $next['quickbooks_queue_id'] . '">', $xml); break; } } } } else { if ($this->_config['check_valid_requestid']) { // They embedded a requestID="..." attribute, let's make sure it's valid //$embedded_action = null; //$embedded_ident = null; //$this->_parseRequestID($requestID, $embedded_action, $embedded_ident); //if ($embedded_action != $next['qb_action'] or $embedded_ident != $next['ident']) if ($next['quickbooks_queue_id'] != $requestID) { // They are sending this request with an INVALID requestID! Error this out and warn them! $err = 'This request contains an invalid embedded requestID="..." attribute; either embed the $requestID parameter, or leave out the requestID="..." attribute entirely, found [' . $requestID . ' vs. expected ' . $next['quickbooks_queue_id'] . ']!'; } } } /* if ($this->_config['convert_unix_newlines'] and false === strpos($xml, "\r") and // there are currently no Windows newlines... false !== strpos($xml, "\n")) // ... but there *are* Unix newlines! { ; // (this is currently broken/unimplemented) } */ if ($err) { //$this->_driver->errorLog($obj->ticket, QUICKBOOKS_ERROR_HANDLER, $err); //$this->_driver->log('ERROR: ' . $err, $obj->ticket, QUICKBOOKS_LOG_NORMAL); //$this->_driver->queueStatus($obj->ticket, $next['qb_action'], $next['ident'], QUICKBOOKS_STATUS_ERROR, 'Registered handler returned error: ' . $err); $errerr = ''; //$this->_handleError($obj->ticket, QUICKBOOKS_ERROR_HANDLER, $err, $this->_constructRequestID($next['qb_action'], $next['ident']), $next['qb_action'], $next['ident'], $extra, $errerr, $xml); $this->_handleError($obj->ticket, QUICKBOOKS_ERROR_HANDLER, $err, $next['quickbooks_queue_id'], $next['qb_action'], $next['ident'], $extra, $errerr, $xml); return new QuickBooks_WebConnector_Result_SendRequestXML(''); } else { //$this->_driver->log('Outgoing XML request: ' . $xml, $obj->ticket, QUICKBOOKS_LOG_DEBUG); $this->_log('Outgoing XML request: ' . $xml, $obj->ticket, QUICKBOOKS_LOG_DEBUG); if (strlen($xml) and !$this->_extractRequestID($xml)) { // Mark it as successful right now //$this->_driver->queueStatus($obj->ticket, $next['qb_action'], $next['ident'], QUICKBOOKS_STATUS_SUCCESS, 'Unverified... no requestID attribute in XML stream.'); $this->_driver->queueStatus($obj->ticket, $next['quickbooks_queue_id'], QUICKBOOKS_STATUS_SUCCESS, 'Unverified... no requestID attribute in XML stream.'); } return new QuickBooks_WebConnector_Result_SendRequestXML($xml); } } } // Reporting an error, this will cause the QBWC to call ->getLastError() return new QuickBooks_WebConnector_Result_SendRequestXML(''); }
/** * Hook which gets called when the Web Connector authenticates to the server * * @param string $requestID Not applicable to this hook * @param string $user The username of the user who connected * @param string $hook The name of the hook which connected * @param string $err If an error occurs, you should return an error message here * @param array $hook_data An array of hook data * @param array $callback_config An array of callback configuration params * @return boolean */ public static function onAuthenticate($requestID, $user, $hook, &$err, $hook_data, $callback_config) { // $requestID, $user, $hook, &$err, $hook_data, $callback_config // Driver instance $Driver = QuickBooks_Driver_Singleton::getInstance(); // Map instance $Map = new QuickBooks_Map_Qbxml($Driver); // Mode (read-onlyl, write-only, read/write) $mode = $callback_config['mode']; // How often recurring events happen $run_every = $callback_config['recur']; // $map parameter $sql_map = $callback_config['map']; // Which things do you want to query? (QuickBooks => SQL database) /* $sql_query = array(); foreach (QuickBooks_Utilities::listActions('*QUERY*') as $action) { $sql_query[$action] = QuickBooks_Utilities::priorityForAction($action); } $sql_query = QuickBooks_Callbacks_SQL_Callbacks::_filterActions($sql_query, $callback_config['_only_query'], $callback_config['_dont_query'], QUICKBOOKS_QUERY); */ //$start = microtime(true); //$_start = microtime(true); $sql_import = array(); $tmp = QuickBooks_Utilities::listActions('*IMPORT*'); //print('0.01 [' . (microtime(true) - $start) . ']' . "\n\n"); foreach ($tmp as $action) { $sql_import[$action] = QuickBooks_Utilities::priorityForAction($action); } //print('0.02 [' . (microtime(true) - $start) . ']' . "\n\n"); $sql_import = QuickBooks_Callbacks_SQL_Callbacks::_filterActions($sql_import, $callback_config['_only_import'], $callback_config['_dont_import'], QUICKBOOKS_IMPORT); //print('0.03 [' . (microtime(true) - $start) . ']' . "\n\n"); //print('0.1 [' . (microtime(true) - $start) . ']' . "\n\n"); // Which things you want to *add* to QuickBooks (SQL => QuickBooks (adds only!)) // @todo These should be changed to use QuickBooks_Utilities::listActions('*ADD*') $sql_add = array(); foreach (QuickBooks_Utilities::listActions('*ADD*') as $action) { $sql_add[$action] = QuickBooks_Utilities::priorityForAction($action); } $sql_add = QuickBooks_Callbacks_SQL_Callbacks::_filterActions($sql_add, $callback_config['_only_add'], $callback_config['_dont_add'], QUICKBOOKS_ADD); //print('0.2 [' . (microtime(true) - $start) . ']' . "\n\n"); // Which things you want to *modify* in QuickBooks (SQL => QuickBooks (modifys only!)) // @todo These should be changed to use QuickBooks_Utilities::listActions('*MOD*') $sql_mod = array(); foreach (QuickBooks_Utilities::listActions('*MOD*') as $action) { $sql_mod[$action] = QuickBooks_Utilities::priorityForAction($action); } $sql_mod = QuickBooks_Callbacks_SQL_Callbacks::_filterActions($sql_mod, $callback_config['_only_modify'], $callback_config['_dont_modify'], QUICKBOOKS_MOD); //print('0.3 [' . (microtime(true) - $start) . ']' . "\n\n"); // Which things you want to *audit* in QuickBooks (QuickBooks => SQL) $sql_audit = array(); foreach (QuickBooks_Utilities::listActions('*AUDIT*') as $action) { $sql_audit[$action] = QuickBooks_Utilities::priorityForAction($action); } //print('1 [' . (microtime(true) - $start) . ']' . "\n\n"); $start = microtime(true); // Queueing class //$Queue = new QuickBooks_Queue($dsn_or_conn); // List of all actions we're performing $actions = array(); if ($mode == QuickBooks_WebConnector_Server_SQL::MODE_READONLY or $mode == QuickBooks_WebConnector_Server_SQL::MODE_READWRITE) { //print_r($sql_query); //print_r($sql_map); // Register recurring events for things you want to query //foreach ($sql_query as $action => $priority) foreach ($sql_import as $action => $priority) { //$Driver->log('Registering recurring event for: ' . $action, null, QUICKBOOKS_LOG_DEBUG); // Make sure that there are handlers registered for this recurring action if (!isset($sql_map[$action])) { //trigger_error('No registered handler for: ' . $action); continue; } //$Queue->recurring($run_every, $action, md5(__FILE__), $priority, null, $user); $Driver->recurEnqueue($user, $run_every, $action, md5(__FILE__), true, $priority); $actions[] = $action; } if (in_array(QUICKBOOKS_QUERY_DELETEDLISTS, $callback_config['_only_misc'])) { // Also grab any deleted records $Driver->queueEnqueue($user, QUICKBOOKS_QUERY_DELETEDLISTS, 1, true, 0); } if (in_array(QUICKBOOKS_QUERY_DELETEDTXNS, $callback_config['_only_misc'])) { $Driver->queueEnqueue($user, QUICKBOOKS_QUERY_DELETEDTXNS, 1, true, 0); } if (in_array(QUICKBOOKS_DERIVE_INVENTORYLEVELS, $callback_config['_only_misc'])) { // Update inventory levels $Driver->queueEnqueue($user, QUICKBOOKS_DERIVE_INVENTORYLEVELS, 1, true, 0); } if (in_array(QUICKBOOKS_DERIVE_INVENTORYASSEMBLYLEVELS, $callback_config['_only_misc'])) { // Update inventory assembly levels $Driver->queueEnqueue($user, QUICKBOOKS_DERIVE_INVENTORYASSEMBLYLEVELS, 1, true, 0); } } //print('2 [' . (microtime(true) - $start) . ']' . "\n\n"); $start = microtime(true); /* // Queue up the audit requests // Audit requests pull in just the calculated TotalAmount and the // TimeModified timestamp from transactions, and store these in // the qbsql_audit_* fields so we can tell if there are transactions // in QuickBooks that don't match up correctly to what's in the SQL // database. foreach ($sql_audit as $action => $priority) { if (!isset($sql_map[$action])) { continue; } $Driver->queueEnqueue($user, $action, 1, true, $priority); } */ // Searching the tables can take a long time, you could potentially // have 10 or 15 seconds between when we search for new customers, and // when we search for new invoices. What we don't want to have happen // is that we search for new customers to be queued up, then a new // customer and invoice gets added, then we search for invoices to be // queued up, queue up the invoice, but the customer it depends on // didn't get queued up yet... results in an error! // // We avoid this by keeping track of when NOW is, and only queueing up // things that are older than NOW() $NOW = date('Y-m-d H:i:s'); // Limit to max adds by time per auth $time_limit = 15; // 60 *second* limit $time_start = time(); // When we started // Objects that need to be *ADDED* to QuickBooks if ($mode == QuickBooks_WebConnector_Server_SQL::MODE_WRITEONLY or $mode == QuickBooks_WebConnector_Server_SQL::MODE_READWRITE) { $mark_as_queued = false; $map = $Map->adds($sql_add, $mark_as_queued); //$Driver->log('ADDS: ' . print_r($map, true)); // Go through each action in the returned map foreach ($map as $action => $list) { //$Driver->log('Now doing: ' . $action . ', ' . print_r($list, true)); //$__start = microtime(true); // Go through each ID for each action $counter = 0; foreach ($list as $ID => $priority) { $counter++; if (time() - $time_start > $time_limit) { //print('HIT LIMIT SO WE\'RE BREAKING OUT OF HERE [' . $action . '] [ ' . $counter . ' of ' . count($list) . ']!' . "\n"); break 2; } // Queue it up to be added to QuickBooks $Driver->queueEnqueue($user, $action, $ID, true, $priority); } //print('now ' . $action . ' [' . (microtime(true) - $__start) . ']' . "\n"); } } //print('3 [' . (microtime(true) - $start) . ']' . "\n\n"); $start = microtime(true); // Objects that need to be *MODIFIED* within QuickBooks if ($mode == QuickBooks_WebConnector_Server_SQL::MODE_WRITEONLY or $mode == QuickBooks_WebConnector_Server_SQL::MODE_READWRITE) { // Check if any objects need to be pushed back to QuickBooks foreach ($sql_mod as $action => $priority) { $object = QuickBooks_Utilities::actionToObject($action); $table_and_field = array(); // Convert to table and primary key, select qbsql id QuickBooks_SQL_Schema::mapPrimaryKey($object, QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $table_and_field); //$Driver->log('Searching table: ' . print_r($table_and_field, true) . ' for MODIFIED records.', null, QUICKBOOKS_LOG_DEBUG); // If we managed to map a table, we need to search that table for changed records if (!empty($table_and_field[0]) and !empty($table_and_field[1])) { // For MODs: // - Do not sync if to_delete = 1 // - Do not sync if to_skip = 1 // - Do not sync if an error occurred on this record $sql = "\n\t\t\t\t\t\tSELECT \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_ID . ", \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER . "\n\t\t\t\t\t\tFROM \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table_and_field[0] . " \n\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_DISCOVER . " IS NOT NULL AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_RESYNC . " IS NOT NULL AND\n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY . " > " . QUICKBOOKS_DRIVER_SQL_FIELD_RESYNC . " AND\n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_TO_DELETE . " != 1 AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_FLAG_DELETED . " != 1 AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_TO_VOID . " != 1 AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_TO_SKIP . " != 1 AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY . " <= '" . $NOW . "' "; $errnum = 0; $errmsg = ''; $res = $Driver->query($sql, $errnum, $errmsg); while ($arr = $Driver->fetch($res)) { if (strlen($arr[QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER])) { // Do not sync this record until the error is resolved continue; } // Queue up this MOD request $Driver->queueEnqueue($user, $action, $arr[QUICKBOOKS_DRIVER_SQL_FIELD_ID], true, $priority); $actions[] = $action; // Mark the record as enqueued - let's wait until the hashing is in for this //$Driver->query(..., $errnum, $errmsg); } } } } //print('4 [' . (microtime(true) - $start) . ']' . "\n\n"); $start = microtime(true); if ($mode == QuickBooks_WebConnector_Server_SQL::MODE_WRITEONLY or $mode == QuickBooks_WebConnector_Server_SQL::MODE_READWRITE) { // Check if any *voided* objects need to be voided in QuickBooks foreach ($sql_add as $action => $priority) { $object = QuickBooks_Utilities::actionToObject($action); $dependency = null; if ($object == QUICKBOOKS_OBJECT_BILL) { // Bill VOID dependency is PurchaseOrderMod because we want to be able to manually close POs (but need to VOID the bills first) $dependency = QUICKBOOKS_MOD_PURCHASEORDER; } $priority = QuickBooks_Utilities::priorityForAction(QUICKBOOKS_VOID_TRANSACTION, $dependency); $table_and_field = array(); // Convert to table and primary key, select qbsql id QuickBooks_SQL_Schema::mapPrimaryKey($object, QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $table_and_field); //$Driver->log('Searching table: ' . print_r($table_and_field, true) . ' for VOIDED records.', null, QUICKBOOKS_LOG_DEBUG); if (!empty($table_and_field[0])) { $sql = "\n\t\t\t\t\t\tSELECT \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_ID . "\n\t\t\t\t\t\tFROM \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table_and_field[0] . " \n\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_DISCOVER . " IS NOT NULL AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_TO_DELETE . " != 1 AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_FLAG_DELETED . " != 1 AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_TO_VOID . " = 1 AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_FLAG_VOIDED . " != 1 AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY . " <= '" . $NOW . "' "; $errnum = 0; $errmsg = ''; $res = $Driver->query($sql, $errnum, $errmsg); $extra = array('object' => $object); while ($arr = $Driver->fetch($res)) { $Driver->queueEnqueue($user, QUICKBOOKS_VOID_TRANSACTION, $arr[QUICKBOOKS_DRIVER_SQL_FIELD_ID], true, $priority, $extra); } } } } //print('5 [' . (microtime(true) - $start) . ']' . "\n\n"); $start = microtime(true); if ($mode == QuickBooks_WebConnector_Server_SQL::MODE_WRITEONLY or $mode == QuickBooks_WebConnector_Server_SQL::MODE_READWRITE) { // Check if any *deleted* objects need to be deleted from QuickBooks foreach ($sql_add as $action => $priority) { break; $priority = 1000 - $priority; $object = QuickBooks_Utilities::actionToObject($action); $table_and_field = array(); // Convert to table and primary key, select qbsql id QuickBooks_SQL_Schema::mapPrimaryKey($object, QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $table_and_field); // Delete if it's marked for deletion and it hasn't been deleted already if (!empty($table_and_field[0])) { $sql = "\n\t\t\t\t\t\tSELECT \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_ID . "\n\t\t\t\t\t\tFROM \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table_and_field[0] . " \n\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t " . QUICKBOOKS_DRIVER_SQL_FIELD_TO_DELETE . " = 1 AND \n\t\t\t\t\t\t\t " . QUICKBOOKS_DRIVER_SQL_FIELD_FLAG_DELETED . " != 1 AND \n\t\t\t\t\t\t\t " . QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY . " <= '" . $NOW . "' "; $errnum = 0; $errmsg = ''; $res = $Driver->query($sql, $errnum, $errmsg); $key = QuickBooks_Utilities::keyForAction($action); if ($key == 'ListID') { $useAction = 'ListDel'; } else { $useAction = 'TxnDel'; } $extra['objectType'] = $object; while ($arr = $Driver->fetch($res)) { $Driver->queueEnqueue($user, $useAction, $extra['objectType'] . $arr[QUICKBOOKS_DRIVER_SQL_FIELD_ID], true, $priority, $extra); } } } } //print('6 [' . (microtime(true) - $start) . ']' . "\n\n"); $start = microtime(true); /* // This makes sure that timestamps are set up for every action we're doing (fixes a bug where timestamps never get recorded on initial sync without iterator) foreach ($actions as $action) { $module = __CLASS__; $key_curr = QuickBooks_Callbacks_SQL_Callbacks::_keySyncCurr($action); $key_prev = QuickBooks_Callbacks_SQL_Callbacks::_keySyncPrev($action); $type = null; $opts = null; $curr_sync_datetime = $Driver->configRead($user, $module, $key_curr, $type, $opts); // last sync started... $prev_sync_datetime = $Driver->configRead($user, $module, $key_prev, $type, $opts); // last sync started... $datetime = QuickBooks_Utilities::datetime('1983-01-02'); if (!$curr_sync_datetime) { $Driver->configWrite($user, $module, $key_curr, $datetime, null); } if (!$prev_sync_datetime) { $Driver->configWrite($user, $module, $key_prev, $datetime, null); } } */ //print("\n\n" . 'here [ ' . (microtime(true) - $_start) . ']' . "\n\n\n"); return true; }
public function saveAll($dir) { $Parser = new QuickBooks_XML($this->_xml); $arr_actions_adds = QuickBooks_Utilities::listActions('*Add', false); $arr_actions_mods = QuickBooks_Utilities::listActions('*Mod', false); //print_r($arr_actions_mods); //exit; $i = 0; $errnum = 0; $errmsg = ''; if ($Doc = $Parser->parse($errnum, $errmsg)) { $children = $Doc->children(); $children = $children[0]->children(); foreach ($children as $Action) { print 'Action name is: ' . $Action->name() . "\n"; //if ($Action->name() != 'VendorAddRq') //{ // continue; //} //print_r($Action); $section = $this->_extractSectionForTag($this->_xml, $Action->name()); //print($section); $wrapper = ''; if ($Action->hasChildren()) { $first = $Action->getChild(0); //print_r($first); if (in_array($first->name(), $arr_actions_mods) or in_array($first->name(), $arr_actions_adds)) { $wrapper = $first->name(); print ' WRAPPER NODE IS: ' . $wrapper . "\n"; } } //exit; $paths_datatype = array(); $paths_maxlength = array(); $paths_isoptional = array(); $paths_sinceversion = array(); $paths_isrepeatable = array(); $paths_reorder = array(); //$curdepth = 0; $lastdepth = 0; $paths = $Action->asArray(QUICKBOOKS_XML_ARRAY_PATHS); foreach ($paths as $path => $datatype) { $tmp = explode(' ', $path); $tag = end($tmp); $comment = $this->_extractCommentForTag($section, $tag); //print("\t{" . $path . '} => ' . $datatype . ' (' . $comment . ')' . "\n"); $parse = $this->_parseComment($comment); //print_r($parse); //print("\n"); $path = trim(substr($path, strlen($Action->name()))); if (strlen($wrapper) and substr($path, 0, strlen($wrapper)) == $wrapper) { $path = substr($path, strlen($wrapper) + 1); } $paths_datatype[$path] = $datatype; $paths_maxlength[$path] = $parse['maxlength']; $paths_isoptional[$path] = $parse['isoptional']; $paths_sinceversion[$path] = $parse['version']; $paths_isrepeatable[$path] = $parse['mayrepeat']; $curdepth = substr_count($path, ' '); if ($curdepth - $lastdepth > 1) { $tmp2 = explode(' ', $path); for ($i = 1; $i < count($tmp2); $i++) { $paths_reorder[] = implode(' ', array_slice($tmp2, 0, $i)); } } $lastdepth = substr_count($path, ' '); $paths_reorder[] = $path; } //print(var_export($paths_datatype)); //print(var_export($paths_maxlength)); //print(var_export($paths_isoptional)); //print(var_export($paths_sinceversion)); //print(var_export($paths_isrepeatable)); //print(var_export($paths_reorder)); $contents = file_get_contents('/home/asdg/QuickBooks/QBXML/Schema/Object/Template.php'); $contents = str_replace('Template', $Action->name(), $contents); $contents = str_replace('\'_qbxmlWrapper\'', var_export($wrapper, true), $contents); $contents = str_replace('\'_dataTypePaths\'', var_export($paths_datatype, true), $contents); $contents = str_replace('\'_maxLengthPaths\'', var_export($paths_maxlength, true), $contents); $contents = str_replace('\'_isOptionalPaths\'', var_export($paths_isoptional, true), $contents); $contents = str_replace('\'_sinceVersionPaths\'', var_export($paths_sinceversion, true), $contents); $contents = str_replace('\'_isRepeatablePaths\'', var_export($paths_isrepeatable, true), $contents); $contents = str_replace('\'_reorderPaths\'', var_export($paths_reorder, true), $contents); $fp = fopen('/home/adg/QuickBooks/tmp/' . $Action->name() . '.php', 'w+'); fwrite($fp, $contents); fclose($fp); print "\n\n"; if ($i > 150) { exit; } $i++; } } }
/** * Hook which gets called when the Web Connector authenticates to the server * * @param string $requestID Not applicable to this hook * @param string $user The username of the user who connected * @param string $hook The name of the hook which connected * @param string $err If an error occurs, you should return an error message here * @param array $hook_data An array of hook data * @param array $callback_config An array of callback configuration params * @return boolean */ public static function onAuthenticate($requestID, $user, $hook, &$err, $hook_data, $callback_config) { // $requestID, $user, $hook, &$err, $hook_data, $callback_config // Driver instance $Driver = QuickBooks_Driver_Singleton::getInstance(); // Mode (read-onlyl, write-only, read/write) $mode = $callback_config['mode']; // How often recurring events happen $run_every = $callback_config['recur']; // $map parameter $sql_map = $callback_config['map']; //print_r($callback_config); //exit; // Which things do you want to query? (QuickBooks => SQL database) $sql_query = array(); foreach (QuickBooks_Utilities::listActions('*QUERY*') as $action) { $sql_query[$action] = QuickBooks_Utilities::priorityForAction($action); } $sql_query = QuickBooks_Server_SQL_Callbacks::_filterActions($sql_query, $callback_config['_only_query'], $callback_config['_dont_query'], QUICKBOOKS_QUERY); // Which things you want to *add* to QuickBooks (SQL => QuickBooks (adds only!)) // @todo These should be changed to use QuickBooks_Utilities::listActions('*ADD*') foreach (QuickBooks_Utilities::listActions('*ADD*') as $action) { $sql_add[$action] = QuickBooks_Utilities::priorityForAction($action); } $sql_add = QuickBooks_Server_SQL_Callbacks::_filterActions($sql_add, $callback_config['_only_add'], $callback_config['_dont_add'], QUICKBOOKS_ADD); // Which things you want to *modify* in QuickBooks (SQL => QuickBooks (modifys only!)) // @todo These should be changed to use QuickBooks_Utilities::listActions('*MOD*') foreach (QuickBooks_Utilities::listActions('*MOD*') as $action) { $sql_mod[$action] = QuickBooks_Utilities::priorityForAction($action); } $sql_mod = QuickBooks_Server_SQL_Callbacks::_filterActions($sql_mod, $callback_config['_only_modify'], $callback_config['_dont_modify'], QUICKBOOKS_MOD); // Queueing class //$Queue = new QuickBooks_Queue($dsn_or_conn); if ($mode == QUICKBOOKS_SERVER_SQL_MODE_READONLY or $mode == QUICKBOOKS_SERVER_SQL_MODE_READWRITE) { //print_r($sql_query); //print_r($sql_map); // Register recurring events for things you want to query foreach ($sql_query as $action => $priority) { // Make sure that there are handlers registered for this recurring action if (!isset($sql_map[$action])) { //trigger_error('No registered handler for: ' . $action); continue; } //$Queue->recurring($run_every, $action, md5(__FILE__), $priority, null, $user); $Driver->recurEnqueue($user, $run_every, $action, md5(__FILE__), true, $priority); } } // Objects that need to be *ADDED* to QuickBooks if ($mode == QUICKBOOKS_SERVER_SQL_MODE_WRITEONLY or $mode == QUICKBOOKS_SERVER_SQL_MODE_READWRITE) { // Check if any objects need to be pushed back to QuickBooks foreach ($sql_add as $action => $priority) { $object = QuickBooks_Utilities::actionToObject($action); $table_and_field = array(); // Convert to table and primary key, select qbsql id QuickBooks_SQL_Schema::mapPrimaryKey($object, QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $table_and_field); //print_r($table_and_field); if (!empty($table_and_field[0]) and !empty($table_and_field[1])) { $sql = "\n\t\t\t\t\t\tSELECT \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_ID . "\n\t\t\t\t\t\tFROM \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table_and_field[0] . " \n\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t " . QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY . " IS NOT NULL AND \n\t\t\t\t\t\t\t " . QUICKBOOKS_DRIVER_SQL_FIELD_RESYNC . " IS NULL AND \n\t\t\t\t\t\t\t " . QUICKBOOKS_DRIVER_SQL_FIELD_TO_SKIP . " != 1 "; $errnum = 0; $errmsg = ''; $res = $Driver->query($sql, $errnum, $errmsg); while ($arr = $Driver->fetch($res)) { $Driver->queueEnqueue($user, $action, $arr[QUICKBOOKS_DRIVER_SQL_FIELD_ID], true, $priority); } } } } // Objects that need to be *MODIFIED* within QuickBooks if ($mode == QUICKBOOKS_SERVER_SQL_MODE_WRITEONLY or $mode == QUICKBOOKS_SERVER_SQL_MODE_READWRITE) { // Check if any objects need to be pushed back to QuickBooks foreach ($sql_mod as $action => $priority) { $object = QuickBooks_Utilities::actionToObject($action); $table_and_field = array(); // Convert to table and primary key, select qbsql id QuickBooks_SQL_Schema::mapPrimaryKey($object, QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $table_and_field); $Driver->log('Searching table: ' . QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table_and_field[0] . ' for MODIFIED records.', null, QUICKBOOKS_LOG_DEBUG); // If we managed to map a table, we need to search that table for changed records if (!empty($table_and_field[0]) and !empty($table_and_field[1])) { $sql = "\n\t\t\t\t\t\tSELECT \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_ID . "\n\t\t\t\t\t\tFROM \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table_and_field[0] . " \n\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_DISCOVER . " IS NOT NULL AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_RESYNC . " IS NOT NULL AND\n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY . " > " . QUICKBOOKS_DRIVER_SQL_FIELD_RESYNC . " AND\n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_TO_DELETE . " != 1 AND \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_TO_SKIP . " != 1 "; $errnum = 0; $errmsg = ''; $res = $Driver->query($sql, $errnum, $errmsg); while ($arr = $Driver->fetch($res)) { $Driver->queueEnqueue($user, $action, $arr[QUICKBOOKS_DRIVER_SQL_FIELD_ID], true, $priority); } } } } if ($mode == QUICKBOOKS_SERVER_SQL_MODE_WRITEONLY or $mode == QUICKBOOKS_SERVER_SQL_MODE_READWRITE) { // Check if any *deleted* objects need to be deleted from QuickBooks foreach ($sql_add as $action => $priority) { $priority = 1000 - $priority; $object = QuickBooks_Utilities::actionToObject($action); $table_and_field = array(); // Convert to table and primary key, select qbsql id QuickBooks_SQL_Schema::mapPrimaryKey($object, QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $table_and_field); if (!empty($table_and_field[0])) { $sql = "\n\t\t\t\t\t\tSELECT \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_FIELD_ID . "\n\t\t\t\t\t\tFROM \n\t\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table_and_field[0] . " \n\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t " . QUICKBOOKS_DRIVER_SQL_FIELD_TO_DELETE . " = 1 "; $errnum = 0; $errmsg = ''; $res = $Driver->query($sql, $errnum, $errmsg); $key = QuickBooks_Utilities::keyForAction($action); if ($key == 'ListID') { $useAction = 'ListDel'; } else { $useAction = 'TxnDel'; } $extra['objectType'] = $object; while ($arr = $Driver->fetch($res)) { $Driver->queueEnqueue($user, $useAction, $extra['objectType'] . $arr[QUICKBOOKS_DRIVER_SQL_FIELD_ID], true, $priority, $extra); } } } } return true; }
/** * * * */ protected function &_createForMirror($mode, $user, $date_from, $date_to, $fetch_full_record, $restrict) { $Driver = $this->_driver; $report = array(); $do_restrict = count($restrict) > 0; $actions = QuickBooks_Utilities::listActions('*IMPORT*'); //print_r($actions); //print_r($restrict); foreach ($actions as $action) { $object = QuickBooks_Utilities::actionToObject($action); //print('checking object [' . $object . ']' . "<br />"); if ($do_restrict and !in_array($object, $restrict)) { continue; } //print('doing object: ' . $object . '<br />'); $pretty = $this->_prettyName($object); $report[$pretty] = array(); QuickBooks_SQL_Schema::mapPrimaryKey($object, QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $table_and_field); //print_r($table_and_field); if (!empty($table_and_field[0]) and !empty($table_and_field[1])) { $sql = "\n\t\t\t\t\tSELECT \n\t\t\t\t\t\t*\n\t\t\t\t\tFROM \n\t\t\t\t\t\t" . QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table_and_field[0] . " \n\t\t\t\t\tWHERE "; if ($mode == QuickBooks_Status_Report::MODE_MIRROR_ERRORS) { $sql .= " LENGTH(" . QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER . ") > 0 "; } else { $sql .= " 1 "; } if ($timestamp = strtotime($date_from) and $timestamp > 0) { $sql .= " AND TimeCreated >= '" . date('Y-m-d H:i:s', $timestamp) . "' "; } if ($timestamp = strtotime($date_to) and $timestamp > 0) { $sql .= " AND TimeCreated <= '" . date('Y-m-d H:i:s', $timestamp) . "' "; } $sql .= " ORDER BY qbsql_id DESC "; //print($sql); $errnum = 0; $errmsg = ''; $res = $Driver->query($sql, $errnum, $errmsg); while ($arr = $Driver->fetch($res)) { $record = null; if ($fetch_full_record) { $record = $arr; } if ($arr[QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER]) { $details = QuickBooks_Status_Report::describe($arr[QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER], $arr[QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_MESSAGE]); } else { if ($arr[QUICKBOOKS_DRIVER_SQL_FIELD_RESYNC] == $arr[QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY]) { $details = 'Synced successfully.'; } else { if ($arr[QUICKBOOKS_DRIVER_SQL_FIELD_MODIFY] > $arr[QUICKBOOKS_DRIVER_SQL_FIELD_RESYNC]) { $details = 'Waiting to sync.'; } } } $report[$pretty][] = array($arr[QUICKBOOKS_DRIVER_SQL_FIELD_ID], $this->_fetchSomeField($arr, array('ListID', 'TxnID')), $this->_fetchSomeField($arr, array('FullName', 'Name', 'RefNumber')), $this->_fetchSomeField($arr, array('TxnDate')), $this->_fetchSomeField($arr, array('Customer_FullName', 'Vendor_FullName')), $arr[QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_NUMBER], $arr[QUICKBOOKS_DRIVER_SQL_FIELD_ERROR_MESSAGE], $details, $arr[QUICKBOOKS_DRIVER_SQL_FIELD_DEQUEUE_TIME], $record); } } } return $report; }