/** * Return the XML object as an XML string * * @return string */ public function asXML($todo_for_empty_elements = true, $indent = "\t") { return $this->_root->asXML(); }
/** * Send an *Add request to QuickBooks to add an object to the QuickBooks database * * @param string $type The QuickBooks object type * @param object $Object The object to add to QuickBooks * @param string $requestID The requestID to use in the qbXML request * @param string $user The username of the Web Connector user * @param string $action The action type (example: "CustomerAdd", QUICKBOOKS_ADD_CUSTOMER) * @param string $ID The qbsql_id value of the object to add * @param mixed $extra Any extra data * @param string $err Set this to an error message if an error occurs * @param integer $last_action_time The UNIX timestamp indicating the last time this type of action occured * @param integer $last_actionident_time The UNIX timestamp indicating the (last time this type of action and $ID value) occured * @param float $version The maximum qbXML version supports * @param string $locale The QuickBooks locale (example: "US") * @param array $config Callback configuration information * @return string The qbXML string */ protected static function _AddRequest($type, $Object, $requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale, $config = array()) { // Driver instance... $Driver = QuickBooks_Driver_Singleton::getInstance(); $type = strtolower($type); // This should actually always happen now that we fixed the Driver->get method to return an array if (!is_object($Object) and is_array($Object)) { $Object = new QuickBooks_SQL_Object(null, null, $Object); } $xml = ''; $xml .= '<?xml version="1.0" encoding="utf-8"?>' . QUICKBOOKS_CRLF; $xml .= '<?qbxml version="' . QuickBooks_Callbacks_SQL_Callbacks::_version($version, $locale) . '"?>' . QUICKBOOKS_CRLF; $xml .= "\t" . '<QBXML>' . QUICKBOOKS_CRLF; $xml .= "\t" . "\t" . '<QBXMLMsgsRq onError="' . QUICKBOOKS_SERVER_SQL_ON_ERROR . '">' . QUICKBOOKS_CRLF; $xml .= "\t" . "\t" . "\t" . '<' . QuickBooks_Utilities::actionToRequest($action) . ' requestID="' . $requestID . '">' . QUICKBOOKS_CRLF; $file = '/QuickBooks/QBXML/Schema/Object/' . QuickBooks_Utilities::actionToRequest($action) . '.php'; $class = 'QuickBooks_QBXML_Schema_Object_' . QuickBooks_Utilities::actionToRequest($action); QuickBooks_Loader::load($file); $schema_object = new $class(); //print_r($Object->asArray()); //exit; $Node = new QuickBooks_XML_Node($action); foreach ($Object->asArray() as $field => $value) { $map = ''; $others = array(); QuickBooks_SQL_Schema::mapToSchema($type . '.' . $field, QUICKBOOKS_SQL_SCHEMA_MAP_TO_XML, $map, $others); // Some drivers case fold all of the field names, which means that // we can't depend on the field names to use in our XML requests // // If that happens, we need to go over to the schema object and // try to fetch the correct, unfolded XML node name and set that // instead. $unmapped = null; if ($Driver->foldsToLower()) { $unmapped = $map; $retpos = strpos($map, 'Ret '); $retval = substr($map, 0, $retpos + 4); $map = substr($map, $retpos + 4); $map = $retval . $schema_object->unfold($map); } //print('dealing with: [' . $map . '] unmapped from (' . $unmapped . ')' . "\n"); if (!$map) { // This schema field doesn't map to anything in QuickBooks... continue; } else { if (!strlen($value)) { // There's no value there, don't send it // There are some special cases here... addresses commonly get // changes to set blank lines for some of the address lines, // and we need to send these blank values to overwrite the // existing values in QuickBooks. // // Here, we send these blank address lines only if at least one // address line is being sent. $begi = substr($field, 0, -5); $last = substr($field, -5, 5); if (($last == 'Addr2' or $last == 'Addr3' or $last == 'Addr4' or $last == 'Addr5') and strlen($Object->get($begi . 'Addr1'))) { // ... but don't allow 4 or 5 if they set the city, state, zip, or country? // EDIT: NEVER ALLOW ADDR4 OR ADDR5, IT JUST F***S SHIT UP! I HATE YOU INTUIT! // WHAT DOES THIS MEAN?!? "The "address" field has an invalid value "". QuickBooks error message: The parameter is incorrect." if ($last == 'Addr4' or $last == 'Addr5') { continue; } /* and ( strlen($Object->get($begi . 'City')) or strlen($Object->get($begi . 'State')) or strlen($Object->get($begi . 'Country')) or strlen($Object->get($begi . 'PostalCode')) )) { continue; } */ // <Judge Roy Snyder>... I'll allow it! } else { continue; } } else { if ($value == QUICKBOOKS_SERVER_SQL_VALUE_CLEAR) { $value = ''; } $use_abbrevs = false; $htmlspecialchars = true; //print('THIS RAN [' . $value . ']'); $value = QuickBooks_Cast::cast(null, null, $value, $use_abbrevs, $htmlspecialchars); //print(' => [' . $value . ']' . "\n"); } } // Special handling for non-US versions of QuickBooks $begi = substr($field, 0, -5); $last = substr($field, -5, 5); if ($locale == QUICKBOOKS_LOCALE_UK and $last == 'State' and !strlen($Object->get($begi . 'County'))) { $Object->set($begi . 'County', $value); //print_r($map); $map = substr($map, 0, -5) . 'County'; //die(); } // OK, the paths look like this: // CustomerRet FirstName // // We don't need the 'CustomerRet' part of it, that's actually incorrect, so we'll strip it off $explode = explode(' ', $map); $first = trim(current($explode)); $map = trim(implode(' ', array_slice($explode, 1))); if (stripos($action, 'add') !== false) { $map = str_replace('Ret', 'Add', $map); } else { $map = str_replace('Ret', 'Mod', $map); } //print(' OK, handling [' . $map . ']' . "\n"); if (false === strpos($map, ' ')) { if ($schema_object->exists($map)) { $use_in_request = true; // If this version doesn't support this field, skip it if ($schema_object->sinceVersion($map) > $version and $schema_object->sinceVersion($map) < 100.0) { $use_in_request = false; } switch ($schema_object->dataType($map)) { case 'AMTTYPE': $value = str_replace(',', '', number_format($value, 2)); break; case 'DATETYPE': if (!$value or $value == '0000-00-00') { $use_in_request = false; } else { $value = QuickBooks_Utilities::date($value); } break; case 'DATETIMETYPE': if (!$value or $value == '0000-00-00 00:00:00') { $use_in_request = false; } else { $value = QuickBooks_Utilities::datetime($value); } break; case 'BOOLTYPE': if ($value == 1) { $value = 'true'; } else { if ($value == 0) { $value = 'false'; } else { $use_in_request = false; } } break; default: break; } if ($use_in_request) { $Child = new QuickBooks_XML_Node($map); $Child->setData($value); $Node->addChild($Child); } } else { // ignore it } } else { $parts = explode(' ', $map); foreach ($parts as $key => $part) { if (stripos($action, 'Mod') !== false) { if ($part == 'SalesAndPurchase' or $part == 'SalesOrPurchase') { $parts[$key] = $part . 'Mod'; } } else { if (stripos($action, 'Add') !== false) { } } } $map = implode(' ', $parts); if ($schema_object->exists($map)) { $use_in_request = true; // If this version doesn't support this field, skip it if ($schema_object->sinceVersion($map) > $version and $schema_object->sinceVersion($map) < 100.0) { $use_in_request = false; } switch ($schema_object->dataType($map)) { case 'AMTTYPE': $value = str_replace(',', '', number_format($value, 2)); break; case 'DATETYPE': if (!$value or $value == '0000-00-00') { $use_in_request = false; } else { $value = QuickBooks_Utilities::date($value); } break; case 'DATETIMETYPE': if (!$value or $value == '0000-00-00 00:00:00') { $use_in_request = false; } else { $value = QuickBooks_Utilities::datetime($value); } break; case 'BOOLTYPE': if ($value == 1) { $value = 'true'; } else { if ($value == 0) { $value = 'false'; } else { $use_in_request = false; } } break; default: break; } if ($use_in_request) { $Node->setChildDataAt($action . ' ' . $map, $value, true); } } } } // Get the child tables here - make sure they're in the proper order for the xml schema $children = QuickBooks_Callbacks_SQL_Callbacks::_getChildTables(strtolower($type)); //print('for type: [' . $type . ']'); //print_r($children); //exit; if (!empty($children)) { $data = QuickBooks_Callbacks_SQL_Callbacks::_queryChildren($children, $Object->get($children[0]['parent'])); } else { // @todo Why not just assign an empty array here...? $data = $children; } $nodes = QuickBooks_Callbacks_SQL_Callbacks::_childObjectsToXML(strtolower($type), $action, $data); //print('NODES:'); //print_r($nodes); //exit; if (count($nodes)) { foreach ($nodes as $nd) { $Node->addChild($nd); } } else { // If we're adding a payment, but not applying it to anything, set IsAutoApply to TRUE if ($action == QUICKBOOKS_ADD_RECEIVEPAYMENT) { $Node->setChildDataAt($action . ' IsAutoApply', 'true', true); } else { if ($action == QUICKBOOKS_MOD_RECEIVEPAYMENT) { } } } //print('ACTION IS: [' . $action . ']'); //print_r($Node); $xml .= $Node->asXML(QuickBooks_XML::XML_PRESERVE); // Bad hack... $xml = str_replace('&#', '&#', $xml); $xml = str_replace('&amp;', '&', $xml); $xml = str_replace('&quot;', '"', $xml); $xml .= '</' . QuickBooks_Utilities::actionToRequest($action) . '> </QBXMLMsgsRq> </QBXML>'; return $xml; }
public function asQBXML($request, $version = null, $locale = null, $root = null) { $todo_for_empty_elements = QuickBooks_XML::XML_DROP; $indent = "\t"; // Call any cleanup routines $this->_cleanup(); // if (strtolower(substr($request, -2, 2)) != 'rq') { $request .= 'Rq'; } $Request = new QuickBooks_XML_Node($request); if ($schema = $this->_schema($request)) { $tmp = array(); // Restrict it to a specific qbXML version? if ($version) { } // Restrict it to a specific qbXML locale? if ($locale) { // List of fields which are not supported for some versions of qbXML if (strlen($locale) == 2) { // The OSR lists locales as 'QBOE', 'QBUK', 'QBCA', etc. vs. our QUICKBOOKS_LOCALE_* constants of just 'OE', 'UK', 'CA', etc. $locale = 'QB' . $locale; } $locales = $schema->localePaths(); } $thelist = $this->asList($request); $reordered = $schema->reorderPaths(array_keys($thelist)); foreach ($reordered as $key => $path) { $value = $this->_object[$path]; if (is_array($value)) { $tmp[$path] = array(); foreach ($value as $arr) { $tmp2 = array(); foreach ($arr->asList('') as $inkey => $invalue) { $arr->set($path . ' ' . $inkey, $invalue); } foreach ($schema->reorderPaths(array_keys($arr->asList(''))) as $subkey => $subpath) { // We need this later, so store it $fullpath = $subpath; if ($locale and isset($locales[$subpath])) { if (in_array($locale, $locales[$subpath])) { // //print('found: ' . $subpath . ' (' . $locale . ') so skipping!' . "\n"); } else { $subpath = substr($subpath, strlen($path) + 1); $tmp2[$subpath] = $arr->get($subpath); } } else { $subpath = substr($subpath, strlen($path) + 1); $tmp2[$subpath] = $arr->get($subpath); } if ($schema->dataType($fullpath) == QUICKBOOKS_QBXML_SCHEMA_TYPE_AMTTYPE and isset($tmp2[$subpath])) { $tmp2[$subpath] = sprintf('%01.2f', $tmp2[$subpath]); } } $tmp2 = new QuickBooks_QBXML_Object_Generic($tmp2, $arr->object()); $tmp[$path][] = $tmp2; } } else { // Do some simple data type casting... if ($schema->dataType($path) == QUICKBOOKS_QBXML_SCHEMA_TYPE_AMTTYPE) { $this->_object[$path] = sprintf('%01.2f', $this->_object[$path]); } if ($locale and isset($locales[$path])) { // Check to see if it's supported by the given locale if (in_array($locale, $locales[$path])) { // It's not supported by this locale, don't show add it } else { $tmp[$path] = $this->_object[$path]; } } else { // If we don't know whether or not it's supported, return it! $tmp[$path] = $this->_object[$path]; } } } // *DO NOT* change the source values of the original object! //$this->_object = $tmp; if ($wrapper = $schema->qbxmlWrapper()) { $Node = $this->asXML($wrapper, null, $tmp); $Request->addChild($Node); return $Request->asXML($todo_for_empty_elements, $indent); } else { if (count($this->_object) == 0) { // This catches the cases where we just want to get *all* objects // back (no filters) and thus the root level qbXML element is *empty* // and we need to *preserve* this empty element rather than just // drop it (which results in an empty string, and thus invalid query) $Node = $this->asXML($request, null, $tmp); return $Node->asXML(QuickBooks_XML::XML_PRESERVE, $indent); } else { $Node = $this->asXML($request, null, $tmp); return $Node->asXML($todo_for_empty_elements, $indent); } } } return ''; }
/** * Send an *Add request to QuickBooks to add an object to the QuickBooks database * * @param string $type The QuickBooks object type * @param object $Object The object to add to QuickBooks * @param string $requestID The requestID to use in the qbXML request * @param string $user The username of the Web Connector user * @param string $action The action type (example: "CustomerAdd", QUICKBOOKS_ADD_CUSTOMER) * @param string $ID The qbsql_id value of the object to add * @param mixed $extra Any extra data * @param string $err Set this to an error message if an error occurs * @param integer $last_action_time The UNIX timestamp indicating the last time this type of action occured * @param integer $last_actionident_time The UNIX timestamp indicating the (last time this type of action and $ID value) occured * @param float $version The maximum qbXML version supports * @param string $locale The QuickBooks locale (example: "US") * @param array $config Callback configuration information * @return string The qbXML string */ protected static function _AddRequest($type, $Object, $requestID, $user, $action, $ID, $extra, &$err, $last_action_time, $last_actionident_time, $version, $locale, $config = array()) { $type = strtolower($type); $xml = ''; $xml .= '<?xml version="1.0" encoding="utf-8"?>' . QUICKBOOKS_CRLF; $xml .= '<?qbxml version="' . QuickBooks_Server_SQL_Callbacks::_version($version, $locale) . '"?>' . QUICKBOOKS_CRLF; $xml .= "\t" . '<QBXML>' . QUICKBOOKS_CRLF; $xml .= "\t" . "\t" . '<QBXMLMsgsRq onError="' . QUICKBOOKS_SERVER_SQL_ON_ERROR . '">' . QUICKBOOKS_CRLF; $xml .= "\t" . "\t" . "\t" . '<' . QuickBooks_Utilities::actionToRequest($action) . ' requestID="' . $requestID . '">' . QUICKBOOKS_CRLF; $file = 'QuickBooks/QBXML/Schema/Object/' . QuickBooks_Utilities::actionToRequest($action) . '.php'; $class = 'QuickBooks_QBXML_Schema_Object_' . QuickBooks_Utilities::actionToRequest($action); require_once $file; $schema_object = new $class(); $Node = new QuickBooks_XML_Node($action); foreach ($Object->asArray() as $field => $value) { $map = ''; $others = array(); QuickBooks_SQL_Schema::mapToSchema($type . '.' . $field, QUICKBOOKS_SQL_SCHEMA_MAP_TO_XML, $map, $others); /* print($field . ' => ' . $value . "\n"); print_r($map); print("\n\n"); */ if (!strlen($value)) { continue; } else { if (stripos($type, 'dataext') !== false) { // DataExt related fields $field = str_replace("_", " ", $field); if (false === strpos($field, ' ')) { if ($schema_object->exists($field)) { switch ($schema_object->dataType($field)) { case 'AMTTYPE': $value = str_replace(",", "", number_format($value, 2)); break; default: break; } if ($field == "DataExtValue" and $Object->get("DataExtType") == "AMTTYPE") { $value = str_replace(",", "", number_format($value, 2)); } $Child = new QuickBooks_XML_Node($map); $Child->setData($value); $Node->addChild($Child); } else { //ignore it } } else { if ($schema_object->exists($field)) { $use_in_request = true; switch ($schema_object->dataType($field)) { case 'AMTTYPE': $value = str_replace(",", "", number_format($value, 2)); break; case 'BOOLTYPE': if ($value == 1) { $value = 'true'; } else { if ($value == 0) { $value = 'false'; } else { $use_in_request = false; } } break; default: break; } if ($use_in_request) { $Node->setChildDataAt($action . ' ' . $field, $value, true); } } } continue; } else { if (!$map) { continue; } } } // OK, the paths look like this: // CustomerRet FirstName // // We don't need the 'CustomerRet' part of it, that's actually incorrect, so we'll strip it off $explode = explode(' ', $map); $first = trim(current($explode)); $map = trim(implode(' ', array_slice($explode, 1))); if (stripos($action, "add") !== false) { $map = str_replace("Ret", "Add", $map); } else { $map = str_replace("Ret", "Mod", $map); } if (false === strpos($map, ' ')) { if ($schema_object->exists($map)) { $use_in_request = true; switch ($schema_object->dataType($map)) { case 'AMTTYPE': $value = str_replace(',', '', number_format($value, 2)); break; case 'DATETYPE': if (!$value or $value == '0000-00-00') { $use_in_request = false; } break; case 'BOOLTYPE': if ($value == 1) { $value = 'true'; } else { if ($value == 0) { $value = 'false'; } else { $use_in_request = false; } } break; default: break; } if ($use_in_request) { $Child = new QuickBooks_XML_Node($map); $Child->setData($value); $Node->addChild($Child); } } else { //ignore it } } else { $tempParts = explode(' ', $map); foreach ($tempParts as $key => $part) { if (stripos($action, "Mod") !== false) { if ($part == "SalesAndPurchase" or $part == "SalesOrPurchase") { $tempParts[$key] = $part . "Mod"; } } else { if (stripos($action, "Add") !== false) { } } } $map = implode(' ', $tempParts); if ($schema_object->exists($map)) { $use_in_request = true; switch ($schema_object->dataType($map)) { case 'AMTTYPE': $value = str_replace(",", "", number_format($value, 2)); break; case 'BOOLTYPE': if ($value == 1) { $value = 'true'; } else { if ($value == 0) { $value = 'false'; } else { $use_in_request = false; } } break; default: break; } if ($use_in_request) { $Node->setChildDataAt($action . ' ' . $map, $value, true); } } } } //Get the Children Tables here -- make sure they're in the proper order for the xml schema $daKids = QuickBooks_Server_SQL_Callbacks::_GetChildrenTables(strtolower($type)); //mail("*****@*****.**", "April16PO", $action."\n\n".strtolower($type)."\n\n".var_export($daKids)); if (!empty($daKids)) { $kidData = QuickBooks_Server_SQL_Callbacks::_QueryChildren($daKids, $Object->get($daKids[0]['parentKey'])); } else { $kidData = $daKids; } $nodes = QuickBooks_Server_SQL_Callbacks::_ChildObjectsToXML(strtolower($type), $action, $kidData); if (count($nodes)) { foreach ($nodes as $nd) { $Node->addChild($nd); } } else { // If we're adding a payment, but not applying it to anything, set IsAutoApply to TRUE if ($action == QUICKBOOKS_ADD_RECEIVEPAYMENT) { $Node->setChildDataAt($action . ' IsAutoApply', 'true', true); } else { if ($action == QUICKBOOKS_MOD_RECEIVEPAYMENT) { } } } //print($Node->asXML()); $xml .= $Node->asXML(); $xml .= '</' . QuickBooks_Utilities::actionToRequest($action) . '> </QBXMLMsgsRq> </QBXML>'; return $xml; }
/** * Convert this object to a valid qbXML request/response * * @todo What should this function return if a schema can't be found...? * * @param boolean $compress_empty_elements * @param string $indent * @param string $root * @return string */ public function asQBXML($request, $todo_for_empty_elements = QUICKBOOKS_XML_XML_DROP, $indent = "\t", $root = null) { if (strtolower(substr($request, -2, 2)) != 'rq') { $request .= 'Rq'; } $Request = new QuickBooks_XML_Node($request); if ($schema = $this->_schema($request)) { $tmp = array(); //print_r(array_keys($this->asList($request))); foreach ($schema->reorderPaths(array_keys($this->asList($request))) as $key => $path) { $value = $this->_object[$path]; if (is_array($value)) { $tmp[$path] = array(); foreach ($value as $arr) { $tmp2 = array(); foreach ($arr->asList('') as $inkey => $invalue) { $arr->set($path . ' ' . $inkey, $invalue); } foreach ($schema->reorderPaths(array_keys($arr->asList(''))) as $subkey => $subpath) { $subpath = substr($subpath, strlen($path) + 1); $tmp2[$subpath] = $arr->get($subpath); } $tmp2 = new QuickBooks_Object_Generic($tmp2, $arr->object()); $tmp[$path][] = $tmp2; } } else { $tmp[$path] = $this->_object[$path]; } } $this->_object = $tmp; if ($wrapper = $schema->qbxmlWrapper()) { $Node = $this->asXML($wrapper); $Request->addChild($Node); return $Request->asXML($todo_for_empty_elements, $indent); } else { if (count($this->_object) == 0) { // This catches the cases where we just want to get *all* objects // back (no filters) and thus the root level qbXML element is *empty* // and we need to *preserve* this empty element rather than just // drop it (which results in an empty string, and thus invalid query) $Node = $this->asXML($request); return $Node->asXML(QUICKBOOKS_XML_XML_PRESERVE, $indent); } else { $Node = $this->asXML($request); return $Node->asXML($todo_for_empty_elements, $indent); } } } return ''; }