[ Index ] |
PHP Cross Reference of osCMax 2.0.4 |
[Summary view] [Print] [Text view]
1 <?php 2 /* 3 $Id: upsxml.php 3 2006-05-27 04:59:07Z user $ 4 5 Original Copyright 2006 osCMax2003 Torin Walker 6 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License 7 as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 8 This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 9 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 See the GNU General Public License for more details. 11 You should have received a copy of the GNU General Public License along with this program; 12 If not, you may obtain one by writing to and requesting one from: 13 The Free Software Foundation, Inc., 14 59 Temple Place, Suite 330, 15 Boston, MA 02111-1307 USA 16 17 Written by Torin Walker. 18 Some code/style borrowed from both Fritz Clapp's UPS Choice 1.7 Module, 19 and Kelvin, Kenneth, and Tom St.Croix's Canada Post 3.1 Module. 20 Insurance support by Joe McFrederick 21 */ 22 23 // Incorporate the XML conversion library 24 if (PHP_VERSION >= '5.0.0') { // PHP 5 does not need to use call-time pass by reference 25 require_once (DIR_WS_CLASSES . 'xml_5.php'); 26 } else { 27 require_once (DIR_WS_CLASSES . 'xml.php'); 28 } 29 30 class upsxml { 31 var $code, $title, $description, $icon, $enabled, $types, $boxcount; 32 33 //*************** 34 function upsxml() { 35 global $order, $packing; 36 $this->code = 'upsxml'; 37 $this->title = MODULE_SHIPPING_UPSXML_RATES_TEXT_TITLE; 38 $this->description = MODULE_SHIPPING_UPSXML_RATES_TEXT_DESCRIPTION; 39 $this->sort_order = MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER; 40 $this->icon = DIR_WS_ICONS . 'shipping_ups.gif'; 41 $this->tax_class = MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS; 42 $this->enabled = ((MODULE_SHIPPING_UPSXML_RATES_STATUS == 'True') ? true : false); 43 $this->access_key = MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY; 44 $this->access_username = MODULE_SHIPPING_UPSXML_RATES_USERNAME; 45 $this->access_password = MODULE_SHIPPING_UPSXML_RATES_PASSWORD; 46 $this->access_account_number = MODULE_SHIPPING_UPSXML_RATES_UPS_ACCOUNT_NUMBER; 47 $this->use_negotiated_rates = MODULE_SHIPPING_UPSXML_RATES_USE_NEGOTIATED_RATES; 48 $this->origin = MODULE_SHIPPING_UPSXML_RATES_ORIGIN; 49 $this->origin_city = MODULE_SHIPPING_UPSXML_RATES_CITY; 50 $this->origin_stateprov = MODULE_SHIPPING_UPSXML_RATES_STATEPROV; 51 $this->origin_country = MODULE_SHIPPING_UPSXML_RATES_COUNTRY; 52 $this->origin_postalcode = MODULE_SHIPPING_UPSXML_RATES_POSTALCODE; 53 $this->pickup_method = MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD; 54 $this->package_type = MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE; 55 // the variables for unit weight, unit length, and dimensions support were moved to 56 // shop admin -> Configuration -> Shipping/Packaging in 57 // version 1.3.0. Run the configuration_shipping.sql to add these to your configuration 58 if (defined('SHIPPING_UNIT_WEIGHT')) { 59 $this->unit_weight = SHIPPING_UNIT_WEIGHT; 60 } else { 61 // for those who will undoubtedly forget or not know how to run the configuration_shipping.sql 62 // we will set the default to pounds (LBS) and inches (IN) 63 $this->unit_weight = 'LBS'; 64 } 65 if (defined('SHIPPING_UNIT_LENGTH')) { 66 $this->unit_length = SHIPPING_UNIT_LENGTH; 67 } else { 68 $this->unit_length = 'IN'; 69 } 70 if (defined('SHIPPING_DIMENSIONS_SUPPORT') && SHIPPING_DIMENSIONS_SUPPORT == 'Ready-to-ship only') { 71 $this->dimensions_support = 1; 72 } elseif (defined('SHIPPING_DIMENSIONS_SUPPORT') && SHIPPING_DIMENSIONS_SUPPORT == 'With product dimensions') { 73 $this->dimensions_support = 2; 74 } else { 75 $this->dimensions_support = 0; 76 } 77 $this->email_errors = ((MODULE_SHIPPING_UPSXML_EMAIL_ERRORS == 'Yes') ? true : false); 78 $this->handling_type = MODULE_SHIPPING_UPSXML_HANDLING_TYPE; 79 $this->handling_fee = MODULE_SHIPPING_UPSXML_RATES_HANDLING; 80 $this->quote_type = MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE; 81 $this->customer_classification = MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE; 82 $this->protocol = 'https'; 83 $this->host = ((MODULE_SHIPPING_UPSXML_RATES_MODE == 'Test') ? 'wwwcie.ups.com' : 'www.ups.com'); 84 $this->port = '443'; 85 $this->path = '/ups.app/xml/Rate'; 86 $this->transitpath = '/ups.app/xml/TimeInTransit'; 87 $this->version = 'UPSXML Rate 1.0001'; 88 $this->transitversion = 'UPSXML Time In Transit 1.0002'; 89 $this->timeout = '60'; 90 $this->xpci_version = '1.0001'; 91 $this->transitxpci_version = '1.0002'; 92 $this->items_qty = 0; 93 $this->timeintransit = '0'; 94 $this->timeInTransitView = MODULE_SHIPPING_UPSXML_RATES_TIME_IN_TRANSIT_VIEW; 95 $this->weight_for_timeintransit = '0'; 96 $now_unix_time = mktime(date("H"), date("i"), date("s"), date("m"), date("d"), date("Y")); 97 $this->today_unix_time = $now_unix_time; 98 $this->today = date("Ymd"); 99 // insurance addition 100 if (MODULE_SHIPPING_UPSXML_INSURE == 'False') { 101 $this->pkgvalue = 100; 102 } else { 103 $this->pkgvalue = ceil($order->info['subtotal']); // is divided by number of boxes later 104 } 105 // end insurance addition 106 // to enable logging, create an empty "upsxml.log" file at the location you set below, give it write permissions (777) and uncomment the next line 107 // $this->logfile = '/srv/www/htdocs/catalog/includes/modules/shipping/upsxml.log'; 108 109 // to enable logging of just the errors, do as above but call the file upsxml_error.log 110 // $this->ups_error_file = '/srv/www/htdocs/catalog/includes/modules/shipping/upsxml_error.log'; 111 // when cURL is not compiled into PHP (Windows users, some Linux users) 112 // you can set the next variable to "1" and then exec(curl -d $xmlRequest, $xmlResponse) 113 // will be used 114 $this->use_exec = '0'; 115 116 if (($this->enabled == true) && ((int)MODULE_SHIPPING_UPSXML_RATES_ZONE > 0)) { 117 $check_flag = false; 118 $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . MODULE_SHIPPING_UPSXML_RATES_ZONE . "' and zone_country_id = '" . $order->delivery['country']['id'] . "' order by zone_id"); 119 while ($check = tep_db_fetch_array($check_query)) { 120 if ($check['zone_id'] < 1) { 121 $check_flag = true; 122 break; 123 } elseif ($check['zone_id'] == $order->delivery['zone_id']) { 124 $check_flag = true; 125 break; 126 } 127 } 128 if ($check_flag == false) { 129 $this->enabled = false; 130 } 131 } 132 133 // Available pickup types - set in admin 134 $this->pickup_methods = array( 135 'Daily Pickup' => '01', 136 'Customer Counter' => '03', 137 'One Time Pickup' => '06', 138 'On Call Air Pickup' => '07', 139 'Suggested Retail Rates (UPS Store)' => '11', 140 'Letter Center' => '19', 141 'Air Service Center' => '20' 142 ); 143 144 // Available package types 145 $this->package_types = array( 146 'UPS Letter' => '01', 147 'Package' => '02', 148 'UPS Tube' => '03', 149 'UPS Pak' => '04', 150 'UPS Express Box' => '21', 151 'UPS 25kg Box' => '24', 152 'UPS 10kg Box' => '25' 153 ); 154 155 // Human-readable Service Code lookup table. The values returned by the Rates and Service "shop" method are numeric. 156 // Using these codes, and the administratively defined Origin, the proper human-readable service name is returned. 157 // Note: The origin specified in the admin configuration affects only the product name as displayed to the user. 158 $this->service_codes = array( 159 // US Origin 160 'US Origin' => array( 161 '01' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_01, 162 '02' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_02, 163 '03' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_03, 164 '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_07, 165 '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_08, 166 '11' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_11, 167 '12' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_12, 168 '13' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_13, 169 '14' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_14, 170 '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_54, 171 '59' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_59, 172 '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_65 173 ), 174 // Canada Origin 175 'Canada Origin' => array( 176 '01' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_01, 177 '02' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_02, 178 '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_07, 179 '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_08, 180 '11' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_11, 181 '12' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_12, 182 '13' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_13, 183 '14' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_14, 184 // service code 54 gone after January 2, 2007 185 '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_54, 186 '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_65 187 ), 188 // European Union Origin 189 'European Union Origin' => array( 190 '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_07, 191 '11' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_11, 192 '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_54, 193 '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_65, 194 // next three services Poland domestic only (Stolica) 195 '82' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_82, 196 '83' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_83, 197 '84' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_84 198 ), 199 // Puerto Rico Origin 200 'Puerto Rico Origin' => array( 201 '01' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_01, 202 '02' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_02, 203 '03' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_03, 204 '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_07, 205 '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_08, 206 '14' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_14, 207 '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_54, 208 '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_65 209 ), 210 // Mexico Origin 211 'Mexico Origin' => array( 212 '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_07, 213 '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_08, 214 '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_54, 215 '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_65 216 ), 217 // All other origins 218 'All other origins' => array( 219 // service code 7 seems to be gone after January 2, 2007 220 '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_OTHER_ORIGIN_07, 221 '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_OTHER_ORIGIN_08, 222 '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_OTHER_ORIGIN_54, 223 '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_65 224 ) 225 ); 226 } // end function upsxml 227 228 // class methods 229 function quote($method = '') { 230 global $HTTP_POST_VARS, $order, $shipping_weight, $shipping_num_boxes, $total_weight, $boxcount, $cart, $packing; 231 // UPS purports that if the origin is left out, it defaults to the account's location. Yeah, right. 232 $state = $order->delivery['state']; 233 $zone_query = tep_db_query("select zone_code from " . TABLE_ZONES . " where zone_name = '" . tep_db_input($order->delivery['state']) . "'"); 234 if (tep_db_num_rows($zone_query)) { 235 $zone = tep_db_fetch_array($zone_query); 236 $state = $zone['zone_code']; 237 } 238 $this->_upsOrigin(MODULE_SHIPPING_UPSXML_RATES_CITY, MODULE_SHIPPING_UPSXML_RATES_STATEPROV, MODULE_SHIPPING_UPSXML_RATES_COUNTRY, MODULE_SHIPPING_UPSXML_RATES_POSTALCODE); 239 $this->_upsDest($order->delivery['city'], $state, $order->delivery['country']['iso_code_2'], $order->delivery['postcode']); 240 241 // the check on $packing being an object will puzzle people who do things wrong (no changes when 242 // you enable dimensional support without changing checkout_shipping.php) but better be safe 243 if ($this->dimensions_support > 0 && is_object($packing)) { 244 $boxValue = 0; 245 $totalWeight = $packing->getTotalWeight(); 246 $shipping_num_boxes = $packing->getNumberOfBoxes(); 247 $boxesToShip = $packing->getPackedBoxes(); 248 for ($i = 0; $i < count($boxesToShip); $i++) { 249 if (MODULE_SHIPPING_UPSXML_INSURE == 'False') { 250 // to avoid the addition of an insurance fee a hardcoded pkgValue (see above around line 93) 251 // of 100 is used when no insurance is wanted 252 $boxesToShip[$i]['item_price'] = $this->pkgvalue; 253 } 254 $this->_addItem($boxesToShip[$i]['item_length'], $boxesToShip[$i]['item_width'], $boxesToShip[$i]['item_height'], $boxesToShip[$i]['item_weight'], $boxesToShip[$i]['item_price']); 255 } // end for ($i = 0; $i < count($boxesToShip); $i++) 256 } else { 257 // The old method. Let osCommerce tell us how many boxes, plus the weight of each (or total? - might be sw/num boxes) 258 $this->items_qty = 0; //reset quantities 259 if (MODULE_SHIPPING_UPSXML_INSURE == 'False') { 260 for ($i = 0; $i < $shipping_num_boxes; $i++) { 261 $this->_addItem(0, 0, 0, $shipping_weight, $this->pkgvalue); 262 } 263 } else { 264 // $this->pkgvalue has been set as order subtotal around line 86, it will cause overcharging 265 // of insurance if not divided by the number of boxes 266 for ($i = 0; $i < $shipping_num_boxes; $i++) { 267 $this->_addItem(0, 0, 0, $shipping_weight, number_format(($this->pkgvalue/$shipping_num_boxes), 2, '.', '')); 268 } 269 } // end if/else (MODULE_SHIPPING_UPSXML_INSURE == 'False') 270 } 271 272 // BOF Time In Transit: used for expected delivery dates 273 // is skipped when set to "Not" in the admin 274 if ($this->timeInTransitView != 'Not') { 275 if ($this->dimensions_support > 0) { 276 $this->weight_for_timeintransit = round($totalWeight,1); 277 } else { 278 $this->weight_for_timeintransit = round($shipping_num_boxes * $shipping_weight,1); 279 } 280 // Added to workaround time in transit error 270033 if total weight of packages is over 150lbs or 70kgs 281 if (($this->weight_for_timeintransit > 150) && ($this->unit_weight == "LBS")) { 282 $this->weight_for_timeintransit = 150; 283 } else if (($this->weight_for_timeintransit > 70) && ($this->unit_weight == "KGS")) { 284 $this->weight_for_timeintransit = 70; 285 } 286 // debug only: 287 /* echo '<pre>Packages and variables:<br />'; 288 print_r($this); 289 echo '<br />'; 290 exit; */ 291 // make sure that when TimeinTransit fails to get results (error or not available) 292 // this is not obvious to the client 293 $_upsGetTimeServicesResult = $this->_upsGetTimeServices(); 294 if ($_upsGetTimeServicesResult != false && is_array($_upsGetTimeServicesResult)) { 295 $this->servicesTimeintransit = $_upsGetTimeServicesResult; 296 } 297 if ($this->logfile) { 298 error_log("------------------------------------------\n", 3, $this->logfile); 299 error_log("Time in Transit: " . $this->timeintransit . "\n", 3, $this->logfile); 300 } 301 } // end if ($this->timeInTransitView != 'Not') 302 // EOF Time In Transit 303 304 $upsQuote = $this->_upsGetQuote(); 305 if ((is_array($upsQuote)) && (sizeof($upsQuote) > 0)) { 306 if (defined('MODULE_SHIPPING_UPSXML_WEIGHT1') && MODULE_SHIPPING_UPSXML_WEIGHT1 == 'False') { 307 $this->quotes = array('id' => $this->code, 'module' => $this->title); 308 usort($upsQuote, array($this, "rate_sort_func")); 309 } else { 310 if ($this->dimensions_support > 0) { 311 $this->quotes = array('id' => $this->code, 'module' => $this->title . ' (' . $this->boxCount . ($this->boxCount > 1 ? ' pkg(s), ' : ' pkg, ') . round($totalWeight,0) . ' ' . strtolower($this->unit_weight) . ' total)'); 312 } else { 313 $this->quotes = array('id' => $this->code, 'module' => $this->title . ' (' . $shipping_num_boxes . ($this->boxCount > 1 ? ' pkg(s) x ' : ' pkg x ') . round($shipping_weight,0) . ' ' . strtolower($this->unit_weight) . ' total)'); 314 } 315 usort($upsQuote, array($this, "rate_sort_func")); 316 } // end else/if if (defined('MODULE_SHIPPING_UPSXML_WEIGHT1') 317 $methods = array(); 318 for ($i=0; $i < sizeof($upsQuote); $i++) { 319 list($type, $cost) = each($upsQuote[$i]); 320 // BOF limit choices, behaviour changed from versions < 1.2 321 if ($this->exclude_choices($type)) continue; 322 // EOF limit choices 323 if ( $method == '' || $method == $type ) { 324 $_type = $type; 325 326 if ($this->timeInTransitView == "Raw") { 327 if (isset($this->servicesTimeintransit[$type])) { 328 $_type = $_type . ", ".$this->servicesTimeintransit[$type]["date"]; 329 } 330 } else { 331 if (isset($this->servicesTimeintransit[$type])) { 332 $eta_array = explode("-", $this->servicesTimeintransit[$type]["date"]); 333 $months = array (" ", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"); 334 $eta_arrival_date = $months[(int)$eta_array[1]]." ".$eta_array[2].", ".$eta_array[0]; 335 $_type .= ", <acronym title='Estimated Delivery Date'>EDD</acronym>: ".$eta_arrival_date; 336 } 337 } 338 339 // changed to make handling percentage based 340 if ($this->handling_type == "Percentage") { 341 $methods[] = array('id' => $type, 'title' => $_type, 'cost' => ((($this->handling_fee * $cost)/100) + $cost)); 342 } else { 343 $methods[] = array('id' => $type, 'title' => $_type, 'cost' => ($this->handling_fee + $cost)); 344 } 345 } 346 } 347 if ($this->tax_class > 0) { 348 $this->quotes['tax'] = tep_get_tax_rate($this->tax_class, $order->delivery['country']['id'], $order->delivery['zone_id']); 349 } 350 $this->quotes['methods'] = $methods; 351 } else { 352 if ( $upsQuote != false ) { 353 $errmsg = $upsQuote; 354 } else { 355 $errmsg = MODULE_SHIPPING_UPSXML_RATES_TEXT_UNKNOWN_ERROR; 356 } 357 $errmsg .= '<br>' . MODULE_SHIPPING_UPSXML_RATES_TEXT_IF_YOU_PREFER . ' ' . STORE_NAME.' via <a href="mailto:'.STORE_OWNER_EMAIL_ADDRESS.'"><u>Email</U></a>.'; 358 $this->quotes = array('module' => $this->title, 'error' => $errmsg); 359 } 360 if (tep_not_null($this->icon)) { 361 $this->quotes['icon'] = tep_image($this->icon, $this->title); 362 } 363 return $this->quotes; 364 } 365 366 //************** 367 function check() { 368 if (!isset($this->_check)) { 369 $check_query = tep_db_query("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_SHIPPING_UPSXML_RATES_STATUS'"); 370 $this->_check = tep_db_num_rows($check_query); 371 } 372 return $this->_check; 373 } 374 375 //************** 376 function install() { 377 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable UPS Shipping', 'MODULE_SHIPPING_UPSXML_RATES_STATUS', 'True', 'Do you want to offer UPS shipping?', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())"); 378 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('UPS Rates Access Key', 'MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY', '', 'Enter the XML rates access key assigned to you by UPS.', '6', '1', now())"); 379 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('UPS Rates Username', 'MODULE_SHIPPING_UPSXML_RATES_USERNAME', '', 'Enter your UPS Services account username.', '6', '2', now())"); 380 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('UPS Rates Password', 'MODULE_SHIPPING_UPSXML_RATES_PASSWORD', '', 'Enter your UPS Services account password.', '6', '3', now())"); 381 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Pickup Method', 'MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD', 'Daily Pickup', 'How do you give packages to UPS (only used when origin is US)?', '6', '4', 'tep_cfg_select_option(array(\'Daily Pickup\', \'Customer Counter\', \'One Time Pickup\', \'On Call Air Pickup\', \'Letter Center\', \'Air Service Center\', \'Suggested Retail Rates (UPS Store)\'), ', now())"); 382 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Packaging Type', 'MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE', 'Package', 'What kind of packaging do you use?', '6', '5', 'tep_cfg_select_option(array(\'Package\', \'UPS Letter\', \'UPS Tube\', \'UPS Pak\', \'UPS Express Box\', \'UPS 25kg Box\', \'UPS 10kg box\'), ', now())"); 383 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Customer Classification Code', 'MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE', '01', '01 - If you are billing to a UPS account and have a daily UPS pickup, 03 - If you do not have a UPS account or you are billing to a UPS account but do not have a daily pickup, 04 - If you are shipping from a retail outlet (only used when origin is US)', '6', '6', 'tep_cfg_select_option(array(\'01\', \'03\', \'04\'), ', now())"); 384 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Shipping Origin', 'MODULE_SHIPPING_UPSXML_RATES_ORIGIN', 'US Origin', 'What origin point should be used (this setting affects only what UPS product names are shown to the user)', '6', '7', 'tep_cfg_select_option(array(\'US Origin\', \'Canada Origin\', \'European Union Origin\', \'Puerto Rico Origin\', \'Mexico Origin\', \'All other origins\'), ', now())"); 385 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Origin City', 'MODULE_SHIPPING_UPSXML_RATES_CITY', '', 'Enter the name of the origin city.', '6', '8', now())"); 386 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Origin State/Province', 'MODULE_SHIPPING_UPSXML_RATES_STATEPROV', '', 'Enter the two-letter code for your origin state/province.', '6', '9', now())"); 387 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Origin Country', 'MODULE_SHIPPING_UPSXML_RATES_COUNTRY', '', 'Enter the two-letter code for your origin country.', '6', '10', now())"); 388 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Origin Zip/Postal Code', 'MODULE_SHIPPING_UPSXML_RATES_POSTALCODE', '', 'Enter your origin zip/postalcode.', '6', '11', now())"); 389 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Test or Production Mode', 'MODULE_SHIPPING_UPSXML_RATES_MODE', 'Test', 'Use this module in Test or Production mode?', '6', '12', 'tep_cfg_select_option(array(\'Test\', \'Production\'), ', now())"); 390 // three configuration options were moved to shop Configuration -> Shipping/Packaging in v1.3.0 391 // those are (and were renamed to -> ): 392 // MODULE_SHIPPING_UPSXML_RATES_UNIT_WEIGHT (LBS/KG) -> SHIPPING_UNIT_WEIGHT 393 // MODULE_SHIPPING_UPSXML_RATES_UNIT_LENGTH (IN/CM) -> SHIPPING_UNIT_LENGTH 394 // MODULE_SHIPPING_UPSXML_DIMENSIONS_SUPPORT -> SHIPPING_DIMENSIONS_SUPPORT 395 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Quote Type', 'MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE', 'Commercial', 'Quote for Residential or Commercial Delivery', '6', '15', 'tep_cfg_select_option(array(\'Commercial\', \'Residential\'), ', now())"); 396 // next two keys added to be able to use negotiated rates (available from UPS since about July 2006) 397 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Negotiated rates', 'MODULE_SHIPPING_UPSXML_RATES_USE_NEGOTIATED_RATES', 'False', 'Do you receive discounted rates from UPS and want to use these for shipping quotes? <b>Note:</b> You need to enter your UPS account number below.', '6', '25', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())"); 398 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('UPS Account Number', 'MODULE_SHIPPING_UPSXML_RATES_UPS_ACCOUNT_NUMBER', '', 'Enter your UPS Account number when you have and want to use negotiated rates.', '6', '26', now())"); 399 // added for handling type selection 400 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Handling Type', 'MODULE_SHIPPING_UPSXML_HANDLING_TYPE', 'Flat Fee', 'Handling type for this shipping method.', '6', '16', 'tep_cfg_select_option(array(\'Flat Fee\', \'Percentage\'), ', now())"); 401 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Handling Fee', 'MODULE_SHIPPING_UPSXML_RATES_HANDLING', '0', 'Handling fee for this shipping method.', '6', '16', now())"); 402 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('UPS Currency Code', 'MODULE_SHIPPING_UPSXML_CURRENCY_CODE', '', 'Enter the 3 letter currency code for your country of origin. United States (USD)', '6', '2', now())"); 403 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable Insurance', 'MODULE_SHIPPING_UPSXML_INSURE', 'True', 'Do you want to insure packages shipped by UPS?', '6', '22', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())"); 404 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Tax Class', 'MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS', '0', 'Use the following tax class on the shipping fee.', '6', '17', 'tep_get_tax_class_title', 'tep_cfg_pull_down_tax_classes(', now())"); 405 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Shipping Zone', 'MODULE_SHIPPING_UPSXML_RATES_ZONE', '0', 'If a zone is selected, only enable this shipping method for that zone.', '6', '18', 'tep_get_zone_class_title', 'tep_cfg_pull_down_zone_classes(', now())"); 406 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Sort order of display.', 'MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER', '0', 'Sort order of display. Lowest is displayed first.', '6', '19', now())"); 407 // add key for disallowed shipping methods 408 tep_db_query("insert into " . TABLE_CONFIGURATION . " ( configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Disallowed Shipping Methods', 'MODULE_SHIPPING_UPSXML_TYPES', '', 'Select the UPS services <span style=\'color: red; font-weight: bold\'>not</span> to be offered.', '6', '20', 'get_multioption_upsxml', 'upsxml_cfg_select_multioption_indexed(array(\'US_01\', \'US_02\', \'US_03\', \'US_07\', \'US_54\', \'US_08\', \'CAN_01\', \'US_11\', \'US_12\', \'US_13\', \'US_14\', \'CAN_02\', \'US_59\', \'US_65\', \'CAN_14\', \'MEX_54\', \'EU_82\', \'EU_83\', \'EU_84\'), ', now())"); 409 // add key for shipping delay, changed the constant from SHIPPING_DAYS_DELAY in v1.3.0 410 tep_db_query("insert into " . TABLE_CONFIGURATION . " ( configuration_id, configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, last_modified, date_added, use_function, set_function) values ('', 'Shipping Delay', 'MODULE_SHIPPING_UPSXML_SHIPPING_DAYS_DELAY', '1', 'How many days from when an order is placed to when you ship it (Decimals are allowed). Arrival date estimations are based on this value.', '6', '21', NULL, now(), NULL, NULL)"); 411 // add key for enabling email error messages 412 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Email UPS errors', 'MODULE_SHIPPING_UPSXML_EMAIL_ERRORS', 'Yes', 'Do you want to receive UPS errors by email?', '6', '24', 'tep_cfg_select_option(array(\'Yes\', \'No\'), ', now())"); 413 // add key for time in transit view type 414 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Time in Transit View Type', 'MODULE_SHIPPING_UPSXML_RATES_TIME_IN_TRANSIT_VIEW', 'Not', 'If and how the module should display the time in transit to the customer.', '6', '16', 'tep_cfg_select_option(array(\'Not\',\'Raw\', \'Detailed\'), ', now())"); 415 tep_db_query("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Display Weight', 'MODULE_SHIPPING_UPSXML_WEIGHT1', 'True', 'Do you want to show number of packages and package weight?', '6', '27', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now())"); 416 } 417 418 //**************** 419 function remove() { 420 tep_db_query("delete from " . TABLE_CONFIGURATION . " where configuration_key in ('" . implode("', '", $this->keys()) . "')"); 421 } 422 423 //************* 424 function keys() { 425 return array('MODULE_SHIPPING_UPSXML_RATES_STATUS', 'MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY', 'MODULE_SHIPPING_UPSXML_RATES_USERNAME', 'MODULE_SHIPPING_UPSXML_RATES_PASSWORD', 'MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD', 'MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE', 'MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE', 'MODULE_SHIPPING_UPSXML_RATES_ORIGIN', 'MODULE_SHIPPING_UPSXML_RATES_CITY', 'MODULE_SHIPPING_UPSXML_RATES_STATEPROV', 'MODULE_SHIPPING_UPSXML_RATES_COUNTRY', 'MODULE_SHIPPING_UPSXML_RATES_POSTALCODE', 'MODULE_SHIPPING_UPSXML_RATES_MODE', 'MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE', 'MODULE_SHIPPING_UPSXML_RATES_USE_NEGOTIATED_RATES', 'MODULE_SHIPPING_UPSXML_RATES_UPS_ACCOUNT_NUMBER', 'MODULE_SHIPPING_UPSXML_HANDLING_TYPE', 'MODULE_SHIPPING_UPSXML_RATES_HANDLING', 'MODULE_SHIPPING_UPSXML_INSURE', 'MODULE_SHIPPING_UPSXML_CURRENCY_CODE', 'MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS', 'MODULE_SHIPPING_UPSXML_RATES_ZONE', 'MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER', 'MODULE_SHIPPING_UPSXML_TYPES', 'MODULE_SHIPPING_UPSXML_SHIPPING_DAYS_DELAY', 'MODULE_SHIPPING_UPSXML_EMAIL_ERRORS', 'MODULE_SHIPPING_UPSXML_RATES_TIME_IN_TRANSIT_VIEW', 'MODULE_SHIPPING_UPSXML_WEIGHT1'); 426 } 427 428 //*********************** 429 function _upsProduct($prod){ 430 $this->_upsProductCode = $prod; 431 } 432 433 //********************************************** 434 function _upsOrigin($city, $stateprov, $country, $postal){ 435 $this->_upsOriginCity = $city; 436 $this->_upsOriginStateProv = $stateprov; 437 $this->_upsOriginCountryCode = $country; 438 $postal = str_replace(' ', '', $postal); 439 if ($country == 'US') { 440 $this->_upsOriginPostalCode = substr($postal, 0, 5); 441 } else { 442 $this->_upsOriginPostalCode = $postal; 443 } 444 } 445 446 //********************************************** 447 function _upsDest($city, $stateprov, $country, $postal) { 448 $this->_upsDestCity = $city; 449 $this->_upsDestStateProv = $stateprov; 450 $this->_upsDestCountryCode = $country; 451 $postal = str_replace(' ', '', $postal); 452 if ($country == 'US') { 453 $this->_upsDestPostalCode = substr($postal, 0, 5); 454 $territories = array('AS','FM','GU','MH','MP','PR','PW','VI'); 455 if (in_array($this->_upsDestStateProv,$territories)) { 456 $this->_upsDestCountryCode = $stateprov; 457 } 458 } else if ($country == 'BR') { 459 $this->_upsDestPostalCode = substr($postal, 0, 5); 460 } else { 461 $this->_upsDestPostalCode = $postal; 462 } 463 } 464 465 //************************ 466 function _upsAction($action) { 467 // rate - Single Quote; shop - All Available Quotes 468 $this->_upsActionCode = $action; 469 } 470 471 //******************************************** 472 // default value of 100 added for insurance (100 shouldn't trigger costs for insurance) 473 function _addItem($length, $width, $height, $weight, $price = 0 ) { 474 // Add box or item to shipment list. Round weights to 1 decimal places. 475 if ((float)$weight < 1.0) { 476 $weight = 1; 477 } else { 478 $weight = round($weight, 1); 479 } 480 $index = $this->items_qty; 481 $this->item_length[$index] = ($length ? (string)$length : '0' ); 482 $this->item_width[$index] = ($width ? (string)$width : '0' ); 483 $this->item_height[$index] = ($height ? (string)$height : '0' ); 484 $this->item_weight[$index] = ($weight ? (string)$weight : '0' ); 485 $this->item_price[$index] = $price; 486 $this->items_qty++; 487 } 488 489 //********************* 490 function _upsGetQuote() { 491 492 // Create the access request 493 $accessRequestHeader = 494 "<?xml version=\"1.0\"?>\n". 495 "<AccessRequest xml:lang=\"en-US\">\n". 496 " <AccessLicenseNumber>". $this->access_key ."</AccessLicenseNumber>\n". 497 " <UserId>". $this->access_username ."</UserId>\n". 498 " <Password>". $this->access_password ."</Password>\n". 499 "</AccessRequest>\n"; 500 501 $ratingServiceSelectionRequestHeader = 502 "<?xml version=\"1.0\"?>\n". 503 "<RatingServiceSelectionRequest xml:lang=\"en-US\">\n". 504 " <Request>\n". 505 " <TransactionReference>\n". 506 " <CustomerContext>Rating and Service</CustomerContext>\n". 507 " <XpciVersion>". $this->xpci_version ."</XpciVersion>\n". 508 " </TransactionReference>\n". 509 " <RequestAction>Rate</RequestAction>\n". 510 " <RequestOption>shop</RequestOption>\n". 511 " </Request>\n"; 512 // according to UPS the CustomerClassification and PickupType containers should 513 // not be present when the origin country is non-US see: 514 // http://forums.oscommerce.com/index.php?s=&showtopic=49382&view=findpost&p=730947 515 if ($this->origin_country == 'US') { 516 $ratingServiceSelectionRequestHeader .= 517 " <PickupType>\n". 518 " <Code>". $this->pickup_methods[$this->pickup_method] ."</Code>\n". 519 " </PickupType>\n"; 520 " <CustomerClassification>\n". 521 " <Code>". $this->customer_classification ."</Code>\n". 522 " </CustomerClassification>\n"; 523 } 524 $ratingServiceSelectionRequestHeader .= 525 " <Shipment>\n". 526 " <Shipper>\n"; 527 if ($this->use_negotiated_rates == 'True') { 528 $ratingServiceSelectionRequestHeader .= 529 " <ShipperNumber>" . $this->access_account_number . "</ShipperNumber>\n"; 530 } 531 $ratingServiceSelectionRequestHeader .= 532 " <Address>\n". 533 " <City>". $this->_upsOriginCity ."</City>\n". 534 " <StateProvinceCode>". $this->_upsOriginStateProv ."</StateProvinceCode>\n". 535 " <CountryCode>". $this->_upsOriginCountryCode ."</CountryCode>\n". 536 " <PostalCode>". $this->_upsOriginPostalCode ."</PostalCode>\n". 537 " </Address>\n". 538 " </Shipper>\n". 539 " <ShipTo>\n". 540 " <Address>\n". 541 " <City>". $this->_upsDestCity ."</City>\n". 542 " <StateProvinceCode>". $this->_upsDestStateProv ."</StateProvinceCode>\n". 543 " <CountryCode>". $this->_upsDestCountryCode ."</CountryCode>\n". 544 " <PostalCode>". $this->_upsDestPostalCode ."</PostalCode>\n". 545 ($this->quote_type == "Residential" ? "<ResidentialAddressIndicator/>\n" : "") . 546 " </Address>\n". 547 " </ShipTo>\n"; 548 for ($i = 0; $i < $this->items_qty; $i++) { 549 550 $ratingServiceSelectionRequestPackageContent .= 551 " <Package>\n". 552 " <PackagingType>\n". 553 " <Code>". $this->package_types[$this->package_type] ."</Code>\n". 554 " </PackagingType>\n"; 555 if ($this->dimensions_support > 0 && ($this->item_length[$i] > 0 ) && ($this->item_width[$i] > 0 ) && ($this->item_height[$i] > 0)) { 556 557 $ratingServiceSelectionRequestPackageContent .= 558 " <Dimensions>\n". 559 " <UnitOfMeasurement>\n". 560 " <Code>". $this->unit_length ."</Code>\n". 561 " </UnitOfMeasurement>\n". 562 " <Length>". $this->item_length[$i] ."</Length>\n". 563 " <Width>". $this->item_width[$i] ."</Width>\n". 564 " <Height>". $this->item_height[$i] ."</Height>\n". 565 " </Dimensions>\n"; 566 } 567 568 $ratingServiceSelectionRequestPackageContent .= 569 " <PackageWeight>\n". 570 " <UnitOfMeasurement>\n". 571 " <Code>". $this->unit_weight ."</Code>\n". 572 " </UnitOfMeasurement>\n". 573 " <Weight>". $this->item_weight[$i] ."</Weight>\n". 574 " </PackageWeight>\n". 575 " <PackageServiceOptions>\n". 576 //" <COD>\n". 577 //" <CODFundsCode>0</CODFundsCode>\n". 578 //" <CODCode>3</CODCode>\n". 579 //" <CODAmount>\n". 580 //" <CurrencyCode>USD</CurrencyCode>\n". 581 //" <MonetaryValue>1000</MonetaryValue>\n". 582 //" </CODAmount>\n". 583 //" </COD>\n". 584 " <InsuredValue>\n". 585 " <CurrencyCode>".MODULE_SHIPPING_UPSXML_CURRENCY_CODE."</CurrencyCode>\n". 586 " <MonetaryValue>".$this->item_price[$i]."</MonetaryValue>\n". 587 " </InsuredValue>\n". 588 " </PackageServiceOptions>\n". 589 " </Package>\n"; 590 } 591 592 $ratingServiceSelectionRequestFooter = ''; 593 //" <ShipmentServiceOptions/>\n". 594 if ($this->use_negotiated_rates == 'True') { 595 $ratingServiceSelectionRequestFooter .= 596 " <RateInformation>\n". 597 " <NegotiatedRatesIndicator/>\n". 598 " </RateInformation>\n"; 599 } 600 $ratingServiceSelectionRequestFooter .= 601 " </Shipment>\n"; 602 // according to UPS the CustomerClassification and PickupType containers should 603 // not be present when the origin country is non-US see: 604 // http://forums.oscommerce.com/index.php?s=&showtopic=49382&view=findpost&p=730947 605 if ($this->origin_country == 'US') { 606 $ratingServiceSelectionRequestFooter .= 607 " <CustomerClassification>\n". 608 " <Code>". $this->customer_classification ."</Code>\n". 609 " </CustomerClassification>\n"; 610 } 611 $ratingServiceSelectionRequestFooter .= 612 "</RatingServiceSelectionRequest>\n"; 613 614 $xmlRequest = $accessRequestHeader . 615 $ratingServiceSelectionRequestHeader . 616 $ratingServiceSelectionRequestPackageContent . 617 $ratingServiceSelectionRequestFooter; 618 619 //post request $strXML; 620 $xmlResult = $this->_post($this->protocol, $this->host, $this->port, $this->path, $this->version, $this->timeout, $xmlRequest); 621 // BOF testing with a response from UPS saved as a text file 622 // needs commenting out the line above: $xmlResult = $this->_post($this->protocol, etcetera 623 /* $filename = '/srv/www/htdocs/catalog/includes/modules/shipping/example_response.xml'; 624 $fp = fopen($filename, "r") or die("couldn't open file"); 625 $xmlResult = ""; 626 while (! feof($fp)) { 627 $xmlResult .= fgets($fp, 1024); 628 } 629 // EOF testing with a text file */ 630 return $this->_parseResult($xmlResult); 631 } 632 633 //****************************************************************** 634 function _post($protocol, $host, $port, $path, $version, $timeout, $xmlRequest) { 635 $url = $protocol."://".$host.":".$port.$path; 636 if ($this->logfile) { 637 error_log("------------------------------------------\n", 3, $this->logfile); 638 error_log("DATE AND TIME: ".date('Y-m-d H:i:s')."\n", 3, $this->logfile); 639 error_log("UPS URL: " . $url . "\n", 3, $this->logfile); 640 } 641 if (function_exists('exec') && $this->use_exec == '1' ) { 642 exec('which curl', $curl_output); 643 if ($curl_output) { 644 $curl_path = $curl_output[0]; 645 } else { 646 $curl_path = 'curl'; // change this if necessary 647 } 648 if ($this->logfile) { 649 error_log("UPS REQUEST using exec(): " . $xmlRequest . "\n", 3, $this->logfile); 650 } 651 // add option -k to the statement: $command = "".$curl_path." -k -d \"". etcetera if you get 652 // curl error 60: error setting certificate verify locations 653 // using addslashes was the only way to avoid UPS returning the 1001 error: The XML document is not well formed 654 $command = "".$curl_path." -d \"".addslashes($xmlRequest)."\" ".$url.""; 655 exec($command, $xmlResponse); 656 if ( empty($xmlResponse) && $this->logfile) { // using exec no curl errors can be retrieved 657 error_log("Error from cURL using exec() since there is no \$xmlResponse\n", 3, $this->logfile); 658 } 659 if ($this->logfile) { 660 error_log("UPS RESPONSE using exec(): " . $xmlResponse[0] . "\n", 3, $this->logfile); 661 } 662 } elseif ($this->use_exec == '1') { // if NOT (function_exists('exec') && $this->use_exec == '1' 663 if ($this->logfile) { 664 error_log("Sorry, exec() cannot be called\n", 3, $this->logfile); 665 } 666 } else { // default behavior: cURL is assumed to be compiled in PHP 667 $ch = curl_init(); 668 curl_setopt($ch, CURLOPT_URL, $url); 669 // uncomment the next line if you get curl error 60: error setting certificate verify locations 670 // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); 671 // uncommenting the next line is most likely not necessary in case of error 60 672 // curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); 673 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 674 curl_setopt($ch, CURLOPT_HEADER, 0); 675 curl_setopt($ch, CURLOPT_POST, 1); 676 curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlRequest); 677 curl_setopt($ch, CURLOPT_TIMEOUT, (int)$timeout); 678 679 if ($this->logfile) { 680 error_log("UPS REQUEST: " . $xmlRequest . "\n", 3, $this->logfile); 681 } 682 $xmlResponse = curl_exec ($ch); 683 if (curl_errno($ch) && $this->logfile) { 684 $error_from_curl = sprintf('Error [%d]: %s', curl_errno($ch), curl_error($ch)); 685 error_log("Error from cURL: " . $error_from_curl . "\n", 3, $this->logfile); 686 } 687 // send email if enabled in the admin section 688 if (curl_errno($ch) && $this->email_errors) { 689 $error_from_curl = sprintf('Error [%d]: %s', curl_errno($ch), curl_error($ch)); 690 error_log("Error from cURL: " . $error_from_curl . " experienced by customer with id " . $_SESSION['customer_id'] . " on " . date('Y-m-d H:i:s'), 1, STORE_OWNER_EMAIL_ADDRESS); 691 } 692 // log errors to file ups_error.log when set 693 if (curl_errno($ch) && $this->ups_error_file) { 694 $error_from_curl = sprintf('Error [%d]: %s', curl_errno($ch), curl_error($ch)); 695 error_log(date('Y-m-d H:i:s')."\tcURL\t" . $error_from_curl . "\t" . $_SESSION['customer_id']."\n", 3, $this->ups_error_file); 696 } 697 if ($this->logfile) { 698 error_log("UPS RESPONSE: " . $xmlResponse . "\n", 3, $this->logfile); 699 } 700 curl_close ($ch); 701 } 702 703 if(!$xmlResponse || strstr(strtolower(substr($xmlResponse, 0, 120)), "bad request")) { 704 /* Sometimes the UPS server responds with an HTML message (differing depending on whether the test server 705 or the production server is used) but both have in the title tag "Bad request". 706 Parsing this response will result in a fatal error: 707 Call to a member function on a non-object in /blabla/includes/classes/xmldocument.php on line 57 708 It only results in not showing Estimated Delivery Dates to the customer so avoiding the fatal error should do. 709 */ 710 $xmlResponse = "<?xml version=\"1.0\"?>\n". 711 "<RatingServiceSelectionResponse>\n". 712 " <Response>\n". 713 " <TransactionReference>\n". 714 " <CustomerContext>Rating and Service</CustomerContext>\n". 715 " <XpciVersion>1.0001</XpciVersion>\n". 716 " </TransactionReference>\n". 717 " <ResponseStatusCode>0</ResponseStatusCode>\n". 718 " <ResponseStatusDescription>". MODULE_SHIPPING_UPSXML_RATES_TEXT_COMM_UNKNOWN_ERROR ."</ResponseStatusDescription>\n". 719 " </Response>\n". 720 "</RatingServiceSelectionResponse>\n"; 721 return $xmlResponse; 722 } 723 if ($this->use_exec == '1') { 724 return $xmlResponse[0]; // $xmlResponse is an array in this case 725 } else { 726 return $xmlResponse; 727 } 728 } 729 730 //***************************** 731 function _parseResult($xmlResult) { 732 // Parse XML message returned by the UPS post server. 733 $doc = XML_unserialize ($xmlResult); 734 735 // Get version. Must be xpci version 1.0001 or this might not work. 736 $responseVersion = $doc['RatingServiceSelectionResponse']['Response']['TransactionReference']['XpciVersion']; 737 if ($this->xpci_version != $responseVersion) { 738 $message = MODULE_SHIPPING_UPSXML_RATES_TEXT_COMM_VERSION_ERROR; 739 return $message; 740 } 741 // Get response code: 1 = SUCCESS, 0 = FAIL 742 $responseStatusCode = $doc['RatingServiceSelectionResponse']['Response']['ResponseStatusCode']; 743 if ($responseStatusCode != '1') { 744 $errorMsg = $doc['RatingServiceSelectionResponse']['Response']['Error']['ErrorCode']; 745 $errorMsg .= ": "; 746 $errorMsg .= $doc['RatingServiceSelectionResponse']['Response']['Error']['ErrorDescription']; 747 // send email if enabled in the admin section 748 if ($this->email_errors) { 749 error_log("UPSXML Rates Error: " . $errorMsg . " experienced by customer with id " . $_SESSION['customer_id'] . " on " . date('Y-m-d H:i:s'), 1, STORE_OWNER_EMAIL_ADDRESS); 750 } 751 // log errors to file ups_error.log when set 752 if ($this->ups_error_file) { 753 error_log(date('Y-m-d H:i:s')."\tRates\t" . $errorMsg . "\t" . $_SESSION['customer_id']."\n", 3, $this->ups_error_file); 754 } 755 return $errorMsg; 756 } 757 758 $ratedShipments = $doc['RatingServiceSelectionResponse']['RatedShipment']; 759 760 $aryProducts = false; 761 if (isset($doc['RatingServiceSelectionResponse']['RatedShipment'][0])) { // more than 1 rate 762 for ($i = 0; $i < count($ratedShipments); $i++) { 763 $serviceCode = $ratedShipments[$i]['Service']['Code']; 764 if ($this->use_negotiated_rates == 'True' && isset($ratedShipments[$i]['NegotiatedRates']['NetSummaryCharges']['GrandTotal']['MonetaryValue'])) { 765 $totalCharge = $ratedShipments[$i]['NegotiatedRates']['NetSummaryCharges']['GrandTotal']['MonetaryValue']; 766 } else { 767 // either a negotiated rate was not given or the shipper does not get/wants any 768 $totalCharge = $ratedShipments[$i]['TotalCharges']['MonetaryValue']; 769 } 770 if (!($serviceCode && $totalCharge)) { 771 continue; 772 } 773 $ratedPackages = $ratedShipments[0]['RatedPackage']; // only do this once for the first service given 774 if (isset($ratedShipments[0]['RatedPackage'][0])) { // multidimensional array of packages 775 $this->boxCount = count($ratedPackages); 776 } else { 777 $this->boxCount = 1; // if there is only one package count($ratedPackages) returns 778 // the number of fields in the array like TransportationCharges and BillingWeight 779 } 780 $title = ''; 781 $title = $this->service_codes[$this->origin][$serviceCode]; 782 $aryProducts[$i] = array($title => $totalCharge); 783 } // end for ($i = 0; $i < count($ratedShipments); $i++) 784 } elseif (isset($doc['RatingServiceSelectionResponse']['RatedShipment'])) { // only 1 rate 785 $serviceCode = $ratedShipments['Service']['Code']; 786 if ($this->use_negotiated_rates == 'True' && isset($ratedShipments['NegotiatedRates']['NetSummaryCharges']['GrandTotal']['MonetaryValue'])) { 787 $totalCharge = $ratedShipments['NegotiatedRates']['NetSummaryCharges']['GrandTotal']['MonetaryValue']; 788 } else { 789 // either a negotiated rate was not given or the shipper does not get/wants any 790 $totalCharge = $ratedShipments['TotalCharges']['MonetaryValue']; 791 } 792 if (!($serviceCode && $totalCharge)) { 793 return $aryProducts; // is false 794 } 795 $ratedPackages = $ratedShipments['RatedPackage']; // only do this once for the first service given 796 if (isset($ratedShipments['RatedPackage'][0])) { // multidimensional array of packages 797 $this->boxCount = count($ratedPackages); 798 } else { 799 $this->boxCount = 1; // if there is only one package count($ratedPackages) returns 800 // the number of fields in the array like TransportationCharges and BillingWeight 801 } 802 $title = ''; 803 $title = $this->service_codes[$this->origin][$serviceCode]; 804 $aryProducts[] = array($title => $totalCharge); 805 } 806 return $aryProducts; 807 } 808 809 // BOF Time In Transit 810 811 //******************** 812 function _upsGetTimeServices() { 813 814 if (defined('MODULE_SHIPPING_UPSXML_SHIPPING_DAYS_DELAY')) { 815 $shipdate = date("Ymd", $this->today_unix_time + (86400*MODULE_SHIPPING_UPSXML_SHIPPING_DAYS_DELAY)); 816 $day_of_the_week = date ("w", $this->today_unix_time + (86400*MODULE_SHIPPING_UPSXML_SHIPPING_DAYS_DELAY) ) ; 817 if ($day_of_the_week == "0" || $day_of_the_week == "7") { // order supposed to leave on Sunday 818 $shipdate = date("Ymd", $this->today_unix_time + (86400*MODULE_SHIPPING_UPSXML_SHIPPING_DAYS_DELAY) + 86400); 819 } elseif ($day_of_the_week == "6") { // order supposed to leave on Saturday 820 $shipdate = date("Ymd", $this->today_unix_time + (86400*MODULE_SHIPPING_UPSXML_SHIPPING_DAYS_DELAY) + 172800); 821 } 822 } else { 823 $shipdate = $this->today; 824 } 825 826 // Create the access request 827 $accessRequestHeader = 828 "<?xml version=\"1.0\"?>\n". 829 "<AccessRequest xml:lang=\"en-US\">\n". 830 " <AccessLicenseNumber>". $this->access_key ."</AccessLicenseNumber>\n". 831 " <UserId>". $this->access_username ."</UserId>\n". 832 " <Password>". $this->access_password ."</Password>\n". 833 "</AccessRequest>\n"; 834 835 $timeintransitSelectionRequestHeader = 836 "<?xml version=\"1.0\"?>\n". 837 "<TimeInTransitRequest xml:lang=\"en-US\">\n". 838 " <Request>\n". 839 " <TransactionReference>\n". 840 " <CustomerContext>Time in Transit</CustomerContext>\n". 841 " <XpciVersion>". $this->transitxpci_version ."</XpciVersion>\n". 842 " </TransactionReference>\n". 843 " <RequestAction>TimeInTransit</RequestAction>\n". 844 " </Request>\n". 845 " <TransitFrom>\n". 846 " <AddressArtifactFormat>\n". 847 " <PoliticalDivision2>". $this->origin_city ."</PoliticalDivision2>\n". 848 " <PoliticalDivision1>". $this->origin_stateprov ."</PoliticalDivision1>\n". 849 " <CountryCode>". $this->_upsOriginCountryCode ."</CountryCode>\n". 850 " <PostcodePrimaryLow>". $this->origin_postalcode ."</PostcodePrimaryLow>\n". 851 " </AddressArtifactFormat>\n". 852 " </TransitFrom>\n". 853 " <TransitTo>\n". 854 " <AddressArtifactFormat>\n". 855 " <PoliticalDivision2>". $this->_upsDestCity ."</PoliticalDivision2>\n". 856 " <PoliticalDivision1>". $this->_upsDestStateProv ."</PoliticalDivision1>\n". 857 " <CountryCode>". $this->_upsDestCountryCode ."</CountryCode>\n". 858 " <PostcodePrimaryLow>". $this->_upsDestPostalCode ."</PostcodePrimaryLow>\n". 859 " <PostcodePrimaryHigh>". $this->_upsDestPostalCode ."</PostcodePrimaryHigh>\n". 860 " </AddressArtifactFormat>\n". 861 " </TransitTo>\n". 862 " <ShipmentWeight>\n". 863 " <UnitOfMeasurement>\n". 864 " <Code>" . $this->unit_weight . "</Code>\n". 865 " </UnitOfMeasurement>\n". 866 " <Weight>" . $this->weight_for_timeintransit . "</Weight>\n". 867 " </ShipmentWeight>\n". 868 " <InvoiceLineTotal>\n". 869 " <CurrencyCode>" . MODULE_SHIPPING_UPSXML_CURRENCY_CODE . "</CurrencyCode>\n". 870 " <MonetaryValue>" . $this->pkgvalue . "</MonetaryValue>\n". 871 " </InvoiceLineTotal>\n". 872 " <PickupDate>" . $shipdate . "</PickupDate>\n". 873 "</TimeInTransitRequest>\n"; 874 875 $xmlTransitRequest = $accessRequestHeader . 876 $timeintransitSelectionRequestHeader; 877 878 //post request $strXML; 879 $xmlTransitResult = $this->_post($this->protocol, $this->host, $this->port, $this->transitpath, $this->transitversion, $this->timeout, $xmlTransitRequest); 880 return $this->_transitparseResult($xmlTransitResult); 881 } 882 883 //*************************************** 884 885 // GM 11-15-2004: modified to return array with time for each service, as 886 // opposed to single transit time for hardcoded "GND" code 887 888 function _transitparseResult($xmlTransitResult) { 889 $transitTime = array(); 890 891 // Parse XML message returned by the UPS post server. 892 $doc = XML_unserialize ($xmlTransitResult); 893 // Get version. Must be xpci version 1.0001 or this might not work. 894 // 1.0001 and 1.0002 seem to be very similar, forget about this for the moment 895 /* $responseVersion = $doc['TimeInTransitResponse']['Response']['TransactionReference']['XpciVersion']; 896 if ($this->transitxpci_version != $responseVersion) { 897 $message = MODULE_SHIPPING_UPSXML_RATES_TEXT_COMM_VERSION_ERROR; 898 return $message; 899 } */ 900 // Get response code. 1 = SUCCESS, 0 = FAIL 901 $responseStatusCode = $doc['TimeInTransitResponse']['Response']['ResponseStatusCode']; 902 if ($responseStatusCode != '1') { 903 $errorMsg = $doc['TimeInTransitResponse']['Response']['Error']['ErrorCode']; 904 $errorMsg .= ": "; 905 $errorMsg .= $doc['TimeInTransitResponse']['Response']['Error']['ErrorDescription']; 906 // send email if enabled in the admin section 907 if ($this->email_errors) { 908 error_log("UPSXML TimeInTransit Error: " . $errorMsg . " experienced by customer with id " . $_SESSION['customer_id'] . " on " . date('Y-m-d H:i:s'), 1, STORE_OWNER_EMAIL_ADDRESS); 909 } 910 // log errors to file ups_error.log when set 911 if ($this->ups_error_file) { 912 error_log(date('Y-m-d H:i:s')."\tTimeInTransit\t" . $errorMsg . "\t" . $_SESSION['customer_id'] ."\n", 3, $this->ups_error_file); 913 } 914 // return $errorMsg; 915 return false; 916 } 917 918 if (isset($doc['TimeInTransitResponse']['TransitResponse']['ServiceSummary'][0])) { // more than one EDD 919 foreach ($doc['TimeInTransitResponse']['TransitResponse']['ServiceSummary'] as $key_index => $service_array) { 920 // index by description because that's all we can relate back to the service 921 // with (though it can probably return the code as well but they are very 922 // different from those used by the Rates Service and there is a lot of 923 // duplication so pretty useless) 924 $serviceDesc = $service_array['Service']['Description']; 925 // hack to get EDD for UPS Saver recognized (Time in Transit uses UPS Worldwide Saver 926 // but the service in Rates and Services is called UPS Saver) 927 if ($serviceDesc == "UPS Worldwide Saver") { 928 $serviceDesc = "UPS Saver"; 929 } 930 // only date is used so why bother with days and guaranteed? 931 // $transitTime[$serviceDesc]["days"] = $serviceSummary[$s]->getValueByPath("EstimatedArrival/BusinessTransitDays"); 932 $transitTime[$serviceDesc]['date'] = $service_array['EstimatedArrival']['Date']; 933 // $transitTime[$serviceDesc]["guaranteed"] = $serviceSummary[$s]->getValueByPath("Guaranteed/Code"); 934 } // end foreach ($doc['TimeInTransitResponse']['ServiceSummary'] etc. 935 } elseif (isset($doc['TimeInTransitResponse']['TransitResponse']['ServiceSummary'])) { // only one EDD 936 $serviceDesc = $doc['TimeInTransitResponse']['TransitResponse']['ServiceSummary']['Service']['Description']; 937 $transitTime[$serviceDesc]['date'] = $doc['TimeInTransitResponse']['TransitResponse']['ServiceSummary']['EstimatedArrival']['Date']; 938 } else { 939 $errorMsg = MODULE_SHIPPING_UPSXML_TIME_IN_TRANSIT_TEXT_NO_RATES; 940 if ($this->ups_error_file) { 941 error_log(date('Y-m-d H:i:s')."\tTimeInTransit\t" . $errorMsg . "\t" . $_SESSION['customer_id'] ."\n", 3, $this->ups_error_file); 942 } 943 return false; 944 } 945 if ($this->logfile) { 946 error_log("------------------------------------------\n", 3, $this->logfile); 947 foreach($transitTime as $desc => $time) { 948 error_log("Business Transit: " . $desc ." = ". $time["date"] . "\n", 3, $this->logfile); 949 } 950 } 951 return $transitTime; 952 } 953 954 //EOF Time In Transit 955 // *************************** 956 function exclude_choices($type) { 957 // Used for exclusion of UPS shipping options, disallowed types are read from db (stored as 958 // short defines). The short defines are not used as such, to avoid collisions 959 // with other shipping modules, they are prefixed with UPSXML_ 960 // These defines are found in the upsxml language file (UPSXML_US_01, UPSXML_CAN_14 etc.) 961 $disallowed_types = explode(",", MODULE_SHIPPING_UPSXML_TYPES); 962 if (strstr($type, "UPS")) { 963 // this will chop off "UPS" from the beginning of the line - typically something like UPS Next Day Air (1 Business Days) 964 $type_minus_ups = explode("UPS", $type ); 965 $type_root = trim($type_minus_ups[1]); 966 } // end if (strstr($type, "UPS"): 967 else { // service description does not contain UPS (unlikely) 968 $type_root = trim($type); 969 } 970 for ($za = 0; $za < count ($disallowed_types); $za++ ) { 971 // when no disallowed types are present, --none-- is in the db but causes an error because --none-- is 972 // not added as a define 973 if ($disallowed_types[$za] == '--none--' ) continue; 974 if ($type_root == constant('UPSXML_' . trim($disallowed_types[$za]))) { 975 return true; 976 } // end if ($type_root == constant(trim($disallowed_types[$za]))). 977 } 978 // if the type is not disallowed: 979 return false; 980 } 981 // Next function used for sorting the shipping quotes on rate: low to high is default. 982 function rate_sort_func ($a, $b) { 983 984 $av = array_values($a); 985 $av = $av[0]; 986 $bv = array_values($b); 987 $bv = $bv[0]; 988 989 // return ($av == $bv) ? 0 : (($av < $bv) ? 1 : -1); // for having the high rates first 990 return ($av == $bv) ? 0 : (($av > $bv) ? 1 : -1); // low rates first 991 992 } 993 } // end class upsxml 994 // Next two functions are used only in the admin for disallowed shipping options. 995 // The (short) constants like US_12, CAN_14 are stored in the database 996 // to stay below 255 characters. The defines themselves are found in the upsxml 997 // language file prefixed with UPSXML_ to avoid collisions with other shipping modules. 998 // They can be moved to admin/includes/function/general.php if you like but don't forget 999 // to remove them from this file in future updates or you will get an error in the admin 1000 // about re-declaring functions 1001 function get_multioption_upsxml($values) { 1002 if (tep_not_null($values)) { 1003 $values_array = explode(',', $values); 1004 foreach ($values_array as $key => $_method) { 1005 if ($_method == '--none--') { 1006 $method = $_method; 1007 } else { 1008 $method = constant('UPSXML_' . trim($_method)); 1009 } 1010 $readable_values_array[] = $method; 1011 } 1012 $readable_values = implode(', ', $readable_values_array); 1013 return $readable_values; 1014 } else { 1015 return ''; 1016 } 1017 } 1018 1019 function upsxml_cfg_select_multioption_indexed($select_array, $key_value, $key = '') { 1020 for ($i=0; $i<sizeof($select_array); $i++) { 1021 $name = (($key) ? 'configuration[' . $key . '][]' : 'configuration_value'); 1022 $string .= '<br><input type="checkbox" name="' . $name . '" value="' . $select_array[$i] . '"'; 1023 $key_values = explode( ", ", $key_value); 1024 if ( in_array($select_array[$i], $key_values) ) $string .= ' CHECKED'; 1025 $string .= '> ' . constant('UPSXML_' . trim($select_array[$i])); 1026 } 1027 $string .= '<input type="hidden" name="' . $name . '" value="--none--">'; 1028 return $string; 1029 } 1030 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Jan 1 13:43:16 2010 | Cross-referenced by PHPXref 0.7 |