/** * * * */ public static function TxnDelResponse($requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $xml, $idents, $config = array()) { $Driver = QuickBooks_Driver_Singleton::getInstance(); $Parser = new QuickBooks_XML_Parser($xml); $errnum = 0; $errmsg = ''; $Doc = $Parser->parse($errnum, $errmsg); $Root = $Doc->getRoot(); $List = $Root->getChildAt('QBXML QBXMLMsgsRs TxnDelRs'); $Node = $List; if ($errnum == 0) { $map = array(); $others = array(); QuickBooks_SQL_Schema::mapToSchema(trim(QuickBooks_Utilities::objectToXMLElement($extra['objectType'])), QUICKBOOKS_SQL_SCHEMA_MAP_TO_SQL, $map, $others); $sqlObject = new QuickBooks_SQL_Object($map[0], trim(QuickBooks_Utilities::objectToXMLElement($extra['objectType']))); $table = $sqlObject->table(); $multipart = array("TxnID" => $Node->getChildDataAt("TxnDelRs TxnID")); //$config['delete'] = //Check the delete mode and if desired, just flag them rather than remove the rows. // @todo Fix this wrong delete flag field //mysql_query("UPDATE qb_bill SET qbsql_to_delete = 0, qbsql_flag_deleted = 1 WHERE TxnID = '" . $Node->getChildDataAt('TxnDelRs TxnID') . "' LIMIT 1"); /* if (isset($config['delete']) and $config['delete'] == QUICKBOOKS_SERVER_SQL_DELETE_FLAG) { //@todo Make the Boolean TRUE value used in the QUICKBOOKS_DRIVER_SQL_FIELD_DELETED_FLAG field a constant, // in case the sql driver used uses something other than 1 and 0. $sqlObject->set(QUICKBOOKS_DRIVER_SQL_FIELD_DELETED_FLAG, 1); $sqlObject->set("TxnID", $Node->getChildDataAt("TxnDelRs TxnID")); $Driver->update(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, $sqlObject, array( $multipart )); //Now Delete/Flag all the children. QuickBooks_Callbacks_SQL_Callbacks::_DeleteChildren($table, $user, $action, $ID, $sqlObject, $extra, $config, true, true); } else { //Otherwise we actually remove the rows. $Driver->delete(QUICKBOOKS_DRIVER_SQL_PREFIX_SQL . $table, array( $multipart )); $sqlObject->set("TxnID", $Node->getChildDataAt("TxnDelRs TxnID")); //Now Delete/Flag all the children. QuickBooks_Callbacks_SQL_Callbacks::_DeleteChildren($table, $user, $action, $ID, $sqlObject, $extra, $config, true, true); } */ } 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; }
/** * @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; }