Overview

Packages

  • currencysymbol
  • MAbout
  • Mage
    • Admin
    • Adminhtml
    • AdminNotification
    • Api
    • Api2
    • Authorizenet
    • Backup
    • Bundle
    • Captcha
    • Catalog
    • CatalogIndex
    • CatalogInventory
    • CatalogRule
    • CatalogSearch
    • Centinel
    • Checkout
    • Cms
    • Compiler
    • Connect
    • Contacts
    • Core
    • Cron
    • CurrencySymbol
    • Customer
    • Dataflow
    • Directory
    • DirtectPost
    • Downloadable
    • Eav
    • GiftMessage
    • GoogleAnalytics
    • GoogleBase
    • GoogleCheckout
    • ImportExport
    • Index
    • Install
    • Log
    • Media
    • Newsletter
    • Oauth
    • Page
    • PageCache
    • Paygate
    • Payment
    • Paypal
    • PaypalUk
    • Persistent
    • Poll
    • ProductAlert
    • Rating
    • Reports
    • Review
    • Rss
    • Rule
    • Sales
    • SalesRule
    • Sedfriend
    • Sendfriend
    • Shipping
    • Sitemap
    • Tag
    • Tax
    • Usa
    • Weee
    • Widget
    • Wishlist
    • XmlConnect
  • None
  • Phoenix
    • Moneybookers
  • PHP
  • Zend
    • Date
    • Mime
    • XmlRpc

Classes

  • Mage_Usa_Block_Adminhtml_Dhl_Unitofmeasure
  • Mage_Usa_Helper_Data
  • Mage_Usa_Model_Shipping_Carrier_Abstract
  • Mage_Usa_Model_Shipping_Carrier_Abstract_Source_Mode
  • Mage_Usa_Model_Shipping_Carrier_Abstract_Source_Requesttype
  • Mage_Usa_Model_Shipping_Carrier_Dhl
  • Mage_Usa_Model_Shipping_Carrier_Dhl_International
  • Mage_Usa_Model_Shipping_Carrier_Dhl_International_Source_Contenttype
  • Mage_Usa_Model_Shipping_Carrier_Dhl_International_Source_Method_Abstract
  • Mage_Usa_Model_Shipping_Carrier_Dhl_International_Source_Method_Doc
  • Mage_Usa_Model_Shipping_Carrier_Dhl_International_Source_Method_Freedoc
  • Mage_Usa_Model_Shipping_Carrier_Dhl_International_Source_Method_Freenondoc
  • Mage_Usa_Model_Shipping_Carrier_Dhl_International_Source_Method_Nondoc
  • Mage_Usa_Model_Shipping_Carrier_Dhl_International_Source_Method_Size
  • Mage_Usa_Model_Shipping_Carrier_Dhl_International_Source_Method_Unitofmeasure
  • Mage_Usa_Model_Shipping_Carrier_Dhl_Label_Pdf
  • Mage_Usa_Model_Shipping_Carrier_Dhl_Label_Pdf_Page
  • Mage_Usa_Model_Shipping_Carrier_Dhl_Label_Pdf_PageBuilder
  • Mage_Usa_Model_Shipping_Carrier_Dhl_Source_Dutypaymenttype
  • Mage_Usa_Model_Shipping_Carrier_Dhl_Source_Freemethod
  • Mage_Usa_Model_Shipping_Carrier_Dhl_Source_Method
  • Mage_Usa_Model_Shipping_Carrier_Dhl_Source_Protection_Rounding
  • Mage_Usa_Model_Shipping_Carrier_Dhl_Source_Protection_Value
  • Mage_Usa_Model_Shipping_Carrier_Dhl_Source_Shipmenttype
  • Mage_Usa_Model_Shipping_Carrier_Fedex
  • Mage_Usa_Model_Shipping_Carrier_Fedex_Source_Dropoff
  • Mage_Usa_Model_Shipping_Carrier_Fedex_Source_Freemethod
  • Mage_Usa_Model_Shipping_Carrier_Fedex_Source_Method
  • Mage_Usa_Model_Shipping_Carrier_Fedex_Source_Packaging
  • Mage_Usa_Model_Shipping_Carrier_Ups
  • Mage_Usa_Model_Shipping_Carrier_Ups_Source_Container
  • Mage_Usa_Model_Shipping_Carrier_Ups_Source_DestType
  • Mage_Usa_Model_Shipping_Carrier_Ups_Source_Freemethod
  • Mage_Usa_Model_Shipping_Carrier_Ups_Source_Method
  • Mage_Usa_Model_Shipping_Carrier_Ups_Source_OriginShipment
  • Mage_Usa_Model_Shipping_Carrier_Ups_Source_Pickup
  • Mage_Usa_Model_Shipping_Carrier_Ups_Source_Type
  • Mage_Usa_Model_Shipping_Carrier_Ups_Source_Unitofmeasure
  • Mage_Usa_Model_Shipping_Carrier_Usps
  • Mage_Usa_Model_Shipping_Carrier_Usps_Source_Container
  • Mage_Usa_Model_Shipping_Carrier_Usps_Source_Freemethod
  • Mage_Usa_Model_Shipping_Carrier_Usps_Source_Machinable
  • Mage_Usa_Model_Shipping_Carrier_Usps_Source_Method
  • Mage_Usa_Model_Shipping_Carrier_Usps_Source_Size
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * Magento
   4:  *
   5:  * NOTICE OF LICENSE
   6:  *
   7:  * This source file is subject to the Open Software License (OSL 3.0)
   8:  * that is bundled with this package in the file LICENSE.txt.
   9:  * It is also available through the world-wide-web at this URL:
  10:  * http://opensource.org/licenses/osl-3.0.php
  11:  * If you did not receive a copy of the license and are unable to
  12:  * obtain it through the world-wide-web, please send an email
  13:  * to license@magentocommerce.com so we can send you a copy immediately.
  14:  *
  15:  * DISCLAIMER
  16:  *
  17:  * Do not edit or add to this file if you wish to upgrade Magento to newer
  18:  * versions in the future. If you wish to customize Magento for your
  19:  * needs please refer to http://www.magentocommerce.com for more information.
  20:  *
  21:  * @category    Mage
  22:  * @package     Mage_Usa
  23:  * @copyright   Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
  24:  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
  25:  */
  26: 
  27: /**
  28:  * Fedex shipping implementation
  29:  *
  30:  * @category   Mage
  31:  * @package    Mage_Usa
  32:  * @author     Magento Core Team <core@magentocommerce.com>
  33:  */
  34: class Mage_Usa_Model_Shipping_Carrier_Fedex
  35:     extends Mage_Usa_Model_Shipping_Carrier_Abstract
  36:     implements Mage_Shipping_Model_Carrier_Interface
  37: {
  38: 
  39:     /**
  40:      * Code of the carrier
  41:      *
  42:      * @var string
  43:      */
  44:     const CODE = 'fedex';
  45: 
  46:     /**
  47:      * Purpose of rate request
  48:      *
  49:      * @var string
  50:      */
  51:     const RATE_REQUEST_GENERAL = 'general';
  52: 
  53:     /**
  54:      * Purpose of rate request
  55:      *
  56:      * @var string
  57:      */
  58:     const RATE_REQUEST_SMARTPOST = 'SMART_POST';
  59: 
  60:     /**
  61:      * Code of the carrier
  62:      *
  63:      * @var string
  64:      */
  65:     protected $_code = self::CODE;
  66: 
  67:     /**
  68:      * Rate request data
  69:      *
  70:      * @var Mage_Shipping_Model_Rate_Request|null
  71:      */
  72:     protected $_request = null;
  73: 
  74:     /**
  75:      * Raw rate request data
  76:      *
  77:      * @var Varien_Object|null
  78:      */
  79:     protected $_rawRequest = null;
  80: 
  81:     /**
  82:      * Rate result data
  83:      *
  84:      * @var Mage_Shipping_Model_Rate_Result|null
  85:      */
  86:     protected $_result = null;
  87: 
  88:     /**
  89:      * Path to wsdl file of rate service
  90:      *
  91:      * @var string
  92:      */
  93:     protected $_rateServiceWsdl;
  94: 
  95:     /**
  96:      * Path to wsdl file of ship service
  97:      *
  98:      * @var string
  99:      */
 100:     protected $_shipServiceWsdl = null;
 101: 
 102:     /**
 103:      * Path to wsdl file of track service
 104:      *
 105:      * @var string
 106:      */
 107:     protected $_trackServiceWsdl = null;
 108: 
 109:     /**
 110:      * Container types that could be customized for FedEx carrier
 111:      *
 112:      * @var array
 113:      */
 114:     protected $_customizableContainerTypes = array('YOUR_PACKAGING');
 115: 
 116:     public function __construct()
 117:     {
 118:         parent::__construct();
 119:         $wsdlBasePath = Mage::getModuleDir('etc', 'Mage_Usa')  . DS . 'wsdl' . DS . 'FedEx' . DS;
 120:         $this->_shipServiceWsdl = $wsdlBasePath . 'ShipService_v10.wsdl';
 121:         $this->_rateServiceWsdl = $wsdlBasePath . 'RateService_v10.wsdl';
 122:         $this->_trackServiceWsdl = $wsdlBasePath . 'TrackService_v5.wsdl';
 123:     }
 124: 
 125:     /**
 126:      * Create soap client with selected wsdl
 127:      *
 128:      * @param string $wsdl
 129:      * @param bool|int $trace
 130:      * @return SoapClient
 131:      */
 132:     protected function _createSoapClient($wsdl, $trace = false)
 133:     {
 134:         $client = new SoapClient($wsdl, array('trace' => $trace));
 135:         $client->__setLocation($this->getConfigFlag('sandbox_mode')
 136:             ? 'https://wsbeta.fedex.com:443/web-services/rate'
 137:             : 'https://ws.fedex.com:443/web-services/rate'
 138:         );
 139: 
 140:         return $client;
 141:     }
 142: 
 143:     /**
 144:      * Create rate soap client
 145:      *
 146:      * @return SoapClient
 147:      */
 148:     protected function _createRateSoapClient()
 149:     {
 150:         return $this->_createSoapClient($this->_rateServiceWsdl);
 151:     }
 152: 
 153:     /**
 154:      * Create ship soap client
 155:      *
 156:      * @return SoapClient
 157:      */
 158:     protected function _createShipSoapClient()
 159:     {
 160:         return $this->_createSoapClient($this->_shipServiceWsdl, 1);
 161:     }
 162: 
 163:     /**
 164:      * Create track soap client
 165:      *
 166:      * @return SoapClient
 167:      */
 168:     protected function _createTrackSoapClient()
 169:     {
 170:         return $this->_createSoapClient($this->_trackServiceWsdl, 1);
 171:     }
 172: 
 173:     /**
 174:      * Collect and get rates
 175:      *
 176:      * @param Mage_Shipping_Model_Rate_Request $request
 177:      * @return Mage_Shipping_Model_Rate_Result|bool|null
 178:      */
 179:     public function collectRates(Mage_Shipping_Model_Rate_Request $request)
 180:     {
 181:         if (!$this->getConfigFlag($this->_activeFlag)) {
 182:             return false;
 183:         }
 184:         $this->setRequest($request);
 185: 
 186:         $this->_getQuotes();
 187: 
 188:         $this->_updateFreeMethodQuote($request);
 189: 
 190:         return $this->getResult();
 191:     }
 192: 
 193:     /**
 194:      * Prepare and set request to this instance
 195:      *
 196:      * @param Mage_Shipping_Model_Rate_Request $request
 197:      * @return Mage_Usa_Model_Shipping_Carrier_Fedex
 198:      */
 199:     public function setRequest(Mage_Shipping_Model_Rate_Request $request)
 200:     {
 201:         $this->_request = $request;
 202: 
 203:         $r = new Varien_Object();
 204: 
 205:         if ($request->getLimitMethod()) {
 206:             $r->setService($request->getLimitMethod());
 207:         }
 208: 
 209:         if ($request->getFedexAccount()) {
 210:             $account = $request->getFedexAccount();
 211:         } else {
 212:             $account = $this->getConfigData('account');
 213:         }
 214:         $r->setAccount($account);
 215: 
 216:         if ($request->getFedexDropoff()) {
 217:             $dropoff = $request->getFedexDropoff();
 218:         } else {
 219:             $dropoff = $this->getConfigData('dropoff');
 220:         }
 221:         $r->setDropoffType($dropoff);
 222: 
 223:         if ($request->getFedexPackaging()) {
 224:             $packaging = $request->getFedexPackaging();
 225:         } else {
 226:             $packaging = $this->getConfigData('packaging');
 227:         }
 228:         $r->setPackaging($packaging);
 229: 
 230:         if ($request->getOrigCountry()) {
 231:             $origCountry = $request->getOrigCountry();
 232:         } else {
 233:             $origCountry = Mage::getStoreConfig(
 234:                 Mage_Shipping_Model_Shipping::XML_PATH_STORE_COUNTRY_ID,
 235:                 $request->getStoreId()
 236:             );
 237:         }
 238:         $r->setOrigCountry(Mage::getModel('directory/country')->load($origCountry)->getIso2Code());
 239: 
 240:         if ($request->getOrigPostcode()) {
 241:             $r->setOrigPostal($request->getOrigPostcode());
 242:         } else {
 243:             $r->setOrigPostal(Mage::getStoreConfig(
 244:                 Mage_Shipping_Model_Shipping::XML_PATH_STORE_ZIP,
 245:                 $request->getStoreId()
 246:             ));
 247:         }
 248: 
 249:         if ($request->getDestCountryId()) {
 250:             $destCountry = $request->getDestCountryId();
 251:         } else {
 252:             $destCountry = self::USA_COUNTRY_ID;
 253:         }
 254:         $r->setDestCountry(Mage::getModel('directory/country')->load($destCountry)->getIso2Code());
 255: 
 256:         if ($request->getDestPostcode()) {
 257:             $r->setDestPostal($request->getDestPostcode());
 258:         } else {
 259: 
 260:         }
 261: 
 262:         $weight = $this->getTotalNumOfBoxes($request->getPackageWeight());
 263:         $r->setWeight($weight);
 264:         if ($request->getFreeMethodWeight()!= $request->getPackageWeight()) {
 265:             $r->setFreeMethodWeight($request->getFreeMethodWeight());
 266:         }
 267: 
 268:         $r->setValue($request->getPackagePhysicalValue());
 269:         $r->setValueWithDiscount($request->getPackageValueWithDiscount());
 270: 
 271:         $r->setMeterNumber($this->getConfigData('meter_number'));
 272:         $r->setKey($this->getConfigData('key'));
 273:         $r->setPassword($this->getConfigData('password'));
 274: 
 275:         $r->setIsReturn($request->getIsReturn());
 276: 
 277:         $r->setBaseSubtotalInclTax($request->getBaseSubtotalInclTax());
 278: 
 279:         $this->_rawRequest = $r;
 280: 
 281:         return $this;
 282:     }
 283: 
 284:     /**
 285:      * Get result of request
 286:      *
 287:      * @return mixed
 288:      */
 289:     public function getResult()
 290:     {
 291:        return $this->_result;
 292:     }
 293: 
 294:     /**
 295:      * Get version of rates request
 296:      *
 297:      * @return array
 298:      */
 299:     public function getVersionInfo()
 300:     {
 301:         return array(
 302:             'ServiceId'    => 'crs',
 303:             'Major'        => '10',
 304:             'Intermediate' => '0',
 305:             'Minor'        => '0'
 306:         );
 307:     }
 308: 
 309:     /**
 310:      * Forming request for rate estimation depending to the purpose
 311:      *
 312:      * @param string $purpose
 313:      * @return array
 314:      */
 315:     protected function _formRateRequest($purpose)
 316:     {
 317:         $r = $this->_rawRequest;
 318:         $ratesRequest = array(
 319:             'WebAuthenticationDetail' => array(
 320:                 'UserCredential' => array(
 321:                     'Key'      => $r->getKey(),
 322:                     'Password' => $r->getPassword()
 323:                 )
 324:             ),
 325:             'ClientDetail' => array(
 326:                 'AccountNumber' => $r->getAccount(),
 327:                 'MeterNumber'   => $r->getMeterNumber()
 328:             ),
 329:             'Version' => $this->getVersionInfo(),
 330:             'RequestedShipment' => array(
 331:                 'DropoffType'   => $r->getDropoffType(),
 332:                 'ShipTimestamp' => date('c'),
 333:                 'PackagingType' => $r->getPackaging(),
 334:                 'TotalInsuredValue' => array(
 335:                     'Amount'  => $r->getValue(),
 336:                     'Currency' => $this->getCurrencyCode()
 337:                 ),
 338:                 'Shipper' => array(
 339:                     'Address' => array(
 340:                         'PostalCode'  => $r->getOrigPostal(),
 341:                         'CountryCode' => $r->getOrigCountry()
 342:                     )
 343:                 ),
 344:                 'Recipient' => array(
 345:                     'Address' => array(
 346:                         'PostalCode'  => $r->getDestPostal(),
 347:                         'CountryCode' => $r->getDestCountry(),
 348:                         'Residential' => (bool)$this->getConfigData('residence_delivery')
 349:                     )
 350:                 ),
 351:                 'ShippingChargesPayment' => array(
 352:                     'PaymentType' => 'SENDER',
 353:                     'Payor' => array(
 354:                         'AccountNumber' => $r->getAccount(),
 355:                         'CountryCode'   => $r->getOrigCountry()
 356:                     )
 357:                 ),
 358:                 'CustomsClearanceDetail' => array(
 359:                     'CustomsValue' => array(
 360:                         'Amount' => $r->getValue(),
 361:                         'Currency' => $this->getCurrencyCode()
 362:                     )
 363:                 ),
 364:                 'RateRequestTypes' => 'LIST',
 365:                 'PackageCount'     => '1',
 366:                 'PackageDetail'    => 'INDIVIDUAL_PACKAGES',
 367:                 'RequestedPackageLineItems' => array(
 368:                     '0' => array(
 369:                         'Weight' => array(
 370:                             'Value' => (float)$r->getWeight(),
 371:                             'Units' => 'LB'
 372:                         ),
 373:                         'GroupPackageCount' => 1,
 374:                     )
 375:                 )
 376:             )
 377:         );
 378: 
 379:         if ($purpose == self::RATE_REQUEST_GENERAL) {
 380:             $ratesRequest['RequestedShipment']['RequestedPackageLineItems'][0]['InsuredValue'] = array(
 381:                 'Amount'  => $r->getValue(),
 382:                 'Currency' => $this->getCurrencyCode()
 383:             );
 384:         } else if ($purpose == self::RATE_REQUEST_SMARTPOST) {
 385:             $ratesRequest['RequestedShipment']['ServiceType'] = self::RATE_REQUEST_SMARTPOST;
 386:             $ratesRequest['RequestedShipment']['SmartPostDetail'] = array(
 387:                 'Indicia' => ((float)$r->getWeight() >= 1) ? 'PARCEL_SELECT' : 'PRESORTED_STANDARD',
 388:                 'HubId' => $this->getConfigData('smartpost_hubid')
 389:             );
 390:         }
 391: 
 392:         return $ratesRequest;
 393:     }
 394: 
 395:     /**
 396:      * Makes remote request to the carrier and returns a response
 397:      *
 398:      * @param string $purpose
 399:      * @return mixed
 400:      */
 401:     protected function _doRatesRequest($purpose)
 402:     {
 403:         $ratesRequest = $this->_formRateRequest($purpose);
 404:         $requestString = serialize($ratesRequest);
 405:         $response = $this->_getCachedQuotes($requestString);
 406:         $debugData = array('request' => $ratesRequest);
 407:         if ($response === null) {
 408:             try {
 409:                 $client = $this->_createRateSoapClient();
 410:                 $response = $client->getRates($ratesRequest);
 411:                 $this->_setCachedQuotes($requestString, serialize($response));
 412:                 $debugData['result'] = $response;
 413:             } catch (Exception $e) {
 414:                 $debugData['result'] = array('error' => $e->getMessage(), 'code' => $e->getCode());
 415:                 Mage::logException($e);
 416:             }
 417:         } else {
 418:             $response = unserialize($response);
 419:             $debugData['result'] = $response;
 420:         }
 421:         $this->_debug($debugData);
 422:         return $response;
 423:     }
 424: 
 425:     /**
 426:      * Do remote request for and handle errors
 427:      *
 428:      * @return Mage_Shipping_Model_Rate_Result
 429:      */
 430:     protected function _getQuotes()
 431:     {
 432:         $this->_result = Mage::getModel('shipping/rate_result');
 433:         // make separate request for Smart Post method
 434:         $allowedMethods = explode(',', $this->getConfigData('allowed_methods'));
 435:         if (in_array(self::RATE_REQUEST_SMARTPOST, $allowedMethods)) {
 436:             $response = $this->_doRatesRequest(self::RATE_REQUEST_SMARTPOST);
 437:             $preparedSmartpost = $this->_prepareRateResponse($response);
 438:             if (!$preparedSmartpost->getError()) {
 439:                 $this->_result->append($preparedSmartpost);
 440:             }
 441:         }
 442:         // make general request for all methods
 443:         $response = $this->_doRatesRequest(self::RATE_REQUEST_GENERAL);
 444:         $preparedGeneral = $this->_prepareRateResponse($response);
 445:         if (!$preparedGeneral->getError() || ($this->_result->getError() && $preparedGeneral->getError())) {
 446:             $this->_result->append($preparedGeneral);
 447:         }
 448:         return $this->_result;
 449:     }
 450: 
 451:     /**
 452:      * Prepare shipping rate result based on response
 453:      *
 454:      * @param mixed $response
 455:      * @return Mage_Shipping_Model_Rate_Result
 456:      */
 457:     protected function _prepareRateResponse($response)
 458:     {
 459:         $costArr = array();
 460:         $priceArr = array();
 461:         $errorTitle = 'Unable to retrieve tracking';
 462: 
 463:         if (is_object($response)) {
 464:             if ($response->HighestSeverity == 'FAILURE' || $response->HighestSeverity == 'ERROR') {
 465:                 $errorTitle = (string)$response->Notifications->Message;
 466:             } elseif (isset($response->RateReplyDetails)) {
 467:                 $allowedMethods = explode(",", $this->getConfigData('allowed_methods'));
 468: 
 469:                 if (is_array($response->RateReplyDetails)) {
 470:                     foreach ($response->RateReplyDetails as $rate) {
 471:                         $serviceName = (string)$rate->ServiceType;
 472:                         if (in_array($serviceName, $allowedMethods)) {
 473:                             $amount = $this->_getRateAmountOriginBased($rate);
 474:                             $costArr[$serviceName]  = $amount;
 475:                             $priceArr[$serviceName] = $this->getMethodPrice($amount, $serviceName);
 476:                         }
 477:                     }
 478:                     asort($priceArr);
 479:                 } else {
 480:                     $rate = $response->RateReplyDetails;
 481:                     $serviceName = (string)$rate->ServiceType;
 482:                     if (in_array($serviceName, $allowedMethods)) {
 483:                         $amount = $this->_getRateAmountOriginBased($rate);
 484:                         $costArr[$serviceName]  = $amount;
 485:                         $priceArr[$serviceName] = $this->getMethodPrice($amount, $serviceName);
 486:                     }
 487:                 }
 488:             }
 489:         }
 490: 
 491:         $result = Mage::getModel('shipping/rate_result');
 492:         if (empty($priceArr)) {
 493:             $error = Mage::getModel('shipping/rate_result_error');
 494:             $error->setCarrier($this->_code);
 495:             $error->setCarrierTitle($this->getConfigData('title'));
 496:             $error->setErrorMessage($errorTitle);
 497:             $error->setErrorMessage($this->getConfigData('specificerrmsg'));
 498:             $result->append($error);
 499:         } else {
 500:             foreach ($priceArr as $method=>$price) {
 501:                 $rate = Mage::getModel('shipping/rate_result_method');
 502:                 $rate->setCarrier($this->_code);
 503:                 $rate->setCarrierTitle($this->getConfigData('title'));
 504:                 $rate->setMethod($method);
 505:                 $rate->setMethodTitle($this->getCode('method', $method));
 506:                 $rate->setCost($costArr[$method]);
 507:                 $rate->setPrice($price);
 508:                 $result->append($rate);
 509:             }
 510:         }
 511:         return $result;
 512:     }
 513: 
 514:     /**
 515:      * Get origin based amount form response of rate estimation
 516:      *
 517:      * @param stdClass $rate
 518:      * @return null|float
 519:      */
 520:     protected function _getRateAmountOriginBased($rate)
 521:     {
 522:         $amount = null;
 523:         $rateTypeAmounts = array();
 524: 
 525:         if (is_object($rate)) {
 526:             // The "RATED..." rates are expressed in the currency of the origin country
 527:             foreach ($rate->RatedShipmentDetails as $ratedShipmentDetail) {
 528:                 $netAmount = (string)$ratedShipmentDetail->ShipmentRateDetail->TotalNetCharge->Amount;
 529:                 $rateType = (string)$ratedShipmentDetail->ShipmentRateDetail->RateType;
 530:                 $rateTypeAmounts[$rateType] = $netAmount;
 531:             }
 532: 
 533:             // Order is important
 534:             foreach (array('RATED_ACCOUNT_SHIPMENT', 'RATED_LIST_SHIPMENT', 'RATED_LIST_PACKAGE') as $rateType) {
 535:                 if (!empty($rateTypeAmounts[$rateType])) {
 536:                     $amount = $rateTypeAmounts[$rateType];
 537:                     break;
 538:                 }
 539:             }
 540: 
 541:             if (is_null($amount)) {
 542:                 $amount = (string)$rate->RatedShipmentDetails[0]->ShipmentRateDetail->TotalNetCharge->Amount;
 543:             }
 544:         }
 545: 
 546:         return $amount;
 547:     }
 548: 
 549:     /**
 550:      * Set free method request
 551:      *
 552:      * @param  $freeMethod
 553:      * @return void
 554:      */
 555:     protected function _setFreeMethodRequest($freeMethod)
 556:     {
 557:         $r = $this->_rawRequest;
 558:         $weight = $this->getTotalNumOfBoxes($r->getFreeMethodWeight());
 559:         $r->setWeight($weight);
 560:         $r->setService($freeMethod);
 561:     }
 562: 
 563:     /**
 564:      * Get xml quotes
 565:      *
 566:      * @return Mage_Shipping_Model_Rate_Result
 567:      */
 568:     protected function _getXmlQuotes()
 569:     {
 570:         $r = $this->_rawRequest;
 571:         $xml = new SimpleXMLElement('<?xml version = "1.0" encoding = "UTF-8"?><FDXRateAvailableServicesRequest/>');
 572: 
 573:         $xml->addAttribute('xmlns:api', 'http://www.fedex.com/fsmapi');
 574:         $xml->addAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
 575:         $xml->addAttribute('xsi:noNamespaceSchemaLocation', 'FDXRateAvailableServicesRequest.xsd');
 576: 
 577:         $requestHeader = $xml->addChild('RequestHeader');
 578:         $requestHeader->addChild('AccountNumber', $r->getAccount());
 579:         $requestHeader->addChild('MeterNumber', '0');
 580: 
 581:         $xml->addChild('ShipDate', date('Y-m-d'));
 582:         $xml->addChild('DropoffType', $r->getDropoffType());
 583:         if ($r->hasService()) {
 584:             $xml->addChild('Service', $r->getService());
 585:         }
 586:         $xml->addChild('Packaging', $r->getPackaging());
 587:         $xml->addChild('WeightUnits', 'LBS');
 588:         $xml->addChild('Weight', $r->getWeight());
 589: 
 590:         $originAddress = $xml->addChild('OriginAddress');
 591:         $originAddress->addChild('PostalCode', $r->getOrigPostal());
 592:         $originAddress->addChild('CountryCode', $r->getOrigCountry());
 593: 
 594:         $destinationAddress = $xml->addChild('DestinationAddress');
 595:         $destinationAddress->addChild('PostalCode', $r->getDestPostal());
 596:         $destinationAddress->addChild('CountryCode', $r->getDestCountry());
 597: 
 598:         $payment = $xml->addChild('Payment');
 599:         $payment->addChild('PayorType', 'SENDER');
 600: 
 601:         $declaredValue = $xml->addChild('DeclaredValue');
 602:         $declaredValue->addChild('Value', $r->getValue());
 603:         $declaredValue->addChild('CurrencyCode', $this->getCurrencyCode());
 604: 
 605:         if ($this->getConfigData('residence_delivery')) {
 606:             $specialServices = $xml->addChild('SpecialServices');
 607:             $specialServices->addChild('ResidentialDelivery', 'true');
 608:         }
 609: 
 610:         $xml->addChild('PackageCount', '1');
 611: 
 612:         $request = $xml->asXML();
 613: 
 614:         $responseBody = $this->_getCachedQuotes($request);
 615:         if ($responseBody === null) {
 616:             $debugData = array('request' => $request);
 617:             try {
 618:                 $url = $this->getConfigData('gateway_url');
 619:                 if (!$url) {
 620:                     $url = $this->_defaultGatewayUrl;
 621:                 }
 622:                 $ch = curl_init();
 623:                 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
 624:                 curl_setopt($ch, CURLOPT_URL, $url);
 625:                 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
 626:                 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
 627:                 curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
 628:                 $responseBody = curl_exec($ch);
 629:                 curl_close ($ch);
 630: 
 631:                 $debugData['result'] = $responseBody;
 632:                 $this->_setCachedQuotes($request, $responseBody);
 633:             }
 634:             catch (Exception $e) {
 635:                 $debugData['result'] = array('error' => $e->getMessage(), 'code' => $e->getCode());
 636:                 $responseBody = '';
 637:             }
 638:             $this->_debug($debugData);
 639:         }
 640:         return $this->_parseXmlResponse($responseBody);
 641:     }
 642: 
 643:     /**
 644:      * Prepare shipping rate result based on response
 645:      *
 646:      * @param mixed $response
 647:      * @return Mage_Shipping_Model_Rate_Result
 648:      */
 649:     protected function _parseXmlResponse($response)
 650:     {
 651:         $costArr = array();
 652:         $priceArr = array();
 653: 
 654:         if (strlen(trim($response))>0) {
 655:            if ($xml = $this->_parseXml($response)) {
 656: 
 657:                if (is_object($xml->Error) && is_object($xml->Error->Message)) {
 658:                    $errorTitle = (string)$xml->Error->Message;
 659:                } elseif (is_object($xml->SoftError) && is_object($xml->SoftError->Message)) {
 660:                    $errorTitle = (string)$xml->SoftError->Message;
 661:                } else {
 662:                    $errorTitle = 'Unknown error';
 663:                }
 664: 
 665:                $allowedMethods = explode(",", $this->getConfigData('allowed_methods'));
 666: 
 667:                foreach ($xml->Entry as $entry) {
 668:                    if (in_array((string)$entry->Service, $allowedMethods)) {
 669:                        $costArr[(string)$entry->Service] =
 670:                            (string)$entry->EstimatedCharges->DiscountedCharges->NetCharge;
 671:                        $priceArr[(string)$entry->Service] = $this->getMethodPrice(
 672:                            (string)$entry->EstimatedCharges->DiscountedCharges->NetCharge,
 673:                            (string)$entry->Service
 674:                        );
 675:                    }
 676:                }
 677: 
 678:                asort($priceArr);
 679: 
 680:            } else {
 681:                $errorTitle = 'Response is in the wrong format.';
 682:            }
 683:         } else {
 684:             $errorTitle = 'Unable to retrieve tracking';
 685:         }
 686: 
 687:         $result = Mage::getModel('shipping/rate_result');
 688:         if (empty($priceArr)) {
 689:             $error = Mage::getModel('shipping/rate_result_error');
 690:             $error->setCarrier('fedex');
 691:             $error->setCarrierTitle($this->getConfigData('title'));
 692:             $error->setErrorMessage($this->getConfigData('specificerrmsg'));
 693:             $result->append($error);
 694:         } else {
 695:             foreach ($priceArr as $method=>$price) {
 696:                 $rate = Mage::getModel('shipping/rate_result_method');
 697:                 $rate->setCarrier('fedex');
 698:                 $rate->setCarrierTitle($this->getConfigData('title'));
 699:                 $rate->setMethod($method);
 700:                 $rate->setMethodTitle($this->getCode('method', $method));
 701:                 $rate->setCost($costArr[$method]);
 702:                 $rate->setPrice($price);
 703:                 $result->append($rate);
 704:             }
 705:         }
 706:         return $result;
 707:     }
 708: 
 709:     /**
 710:      * Parse XML string and return XML document object or false
 711:      *
 712:      * @param string $xmlContent
 713:      * @return SimpleXMLElement|bool
 714:      */
 715:     protected function _parseXml($xmlContent)
 716:     {
 717:         try {
 718:             try {
 719:                 return simplexml_load_string($xmlContent);
 720:             } catch (Exception $e) {
 721:                 throw new Exception(Mage::helper('usa')->__('Failed to parse xml document: %s', $xmlContent));
 722:             }
 723:         } catch (Exception $e) {
 724:             Mage::logException($e);
 725:             return false;
 726:         }
 727:     }
 728: 
 729:     /**
 730:      * Get configuration data of carrier
 731:      *
 732:      * @param string $type
 733:      * @param string $code
 734:      * @return array|bool
 735:      */
 736:     public function getCode($type, $code='')
 737:     {
 738:         $codes = array(
 739:             'method' => array(
 740:                 'EUROPE_FIRST_INTERNATIONAL_PRIORITY' => Mage::helper('usa')->__('Europe First Priority'),
 741:                 'FEDEX_1_DAY_FREIGHT'                 => Mage::helper('usa')->__('1 Day Freight'),
 742:                 'FEDEX_2_DAY_FREIGHT'                 => Mage::helper('usa')->__('2 Day Freight'),
 743:                 'FEDEX_2_DAY'                         => Mage::helper('usa')->__('2 Day'),
 744:                 'FEDEX_2_DAY_AM'                      => Mage::helper('usa')->__('2 Day AM'),
 745:                 'FEDEX_3_DAY_FREIGHT'                 => Mage::helper('usa')->__('3 Day Freight'),
 746:                 'FEDEX_EXPRESS_SAVER'                 => Mage::helper('usa')->__('Express Saver'),
 747:                 'FEDEX_GROUND'                        => Mage::helper('usa')->__('Ground'),
 748:                 'FIRST_OVERNIGHT'                     => Mage::helper('usa')->__('First Overnight'),
 749:                 'GROUND_HOME_DELIVERY'                => Mage::helper('usa')->__('Home Delivery'),
 750:                 'INTERNATIONAL_ECONOMY'               => Mage::helper('usa')->__('International Economy'),
 751:                 'INTERNATIONAL_ECONOMY_FREIGHT'       => Mage::helper('usa')->__('Intl Economy Freight'),
 752:                 'INTERNATIONAL_FIRST'                 => Mage::helper('usa')->__('International First'),
 753:                 'INTERNATIONAL_GROUND'                => Mage::helper('usa')->__('International Ground'),
 754:                 'INTERNATIONAL_PRIORITY'              => Mage::helper('usa')->__('International Priority'),
 755:                 'INTERNATIONAL_PRIORITY_FREIGHT'      => Mage::helper('usa')->__('Intl Priority Freight'),
 756:                 'PRIORITY_OVERNIGHT'                  => Mage::helper('usa')->__('Priority Overnight'),
 757:                 'SMART_POST'                          => Mage::helper('usa')->__('Smart Post'),
 758:                 'STANDARD_OVERNIGHT'                  => Mage::helper('usa')->__('Standard Overnight'),
 759:                 'FEDEX_FREIGHT'                       => Mage::helper('usa')->__('Freight'),
 760:                 'FEDEX_NATIONAL_FREIGHT'              => Mage::helper('usa')->__('National Freight'),
 761:             ),
 762:             'dropoff' => array(
 763:                 'REGULAR_PICKUP'          => Mage::helper('usa')->__('Regular Pickup'),
 764:                 'REQUEST_COURIER'         => Mage::helper('usa')->__('Request Courier'),
 765:                 'DROP_BOX'                => Mage::helper('usa')->__('Drop Box'),
 766:                 'BUSINESS_SERVICE_CENTER' => Mage::helper('usa')->__('Business Service Center'),
 767:                 'STATION'                 => Mage::helper('usa')->__('Station')
 768:             ),
 769:             'packaging' => array(
 770:                 'FEDEX_ENVELOPE' => Mage::helper('usa')->__('FedEx Envelope'),
 771:                 'FEDEX_PAK'      => Mage::helper('usa')->__('FedEx Pak'),
 772:                 'FEDEX_BOX'      => Mage::helper('usa')->__('FedEx Box'),
 773:                 'FEDEX_TUBE'     => Mage::helper('usa')->__('FedEx Tube'),
 774:                 'FEDEX_10KG_BOX' => Mage::helper('usa')->__('FedEx 10kg Box'),
 775:                 'FEDEX_25KG_BOX' => Mage::helper('usa')->__('FedEx 25kg Box'),
 776:                 'YOUR_PACKAGING' => Mage::helper('usa')->__('Your Packaging')
 777:             ),
 778:             'containers_filter' => array(
 779:                 array(
 780:                     'containers' => array('FEDEX_ENVELOPE', 'FEDEX_PAK'),
 781:                     'filters'    => array(
 782:                         'within_us' => array(
 783:                             'method' => array(
 784:                                 'FEDEX_EXPRESS_SAVER',
 785:                                 'FEDEX_2_DAY',
 786:                                 'FEDEX_2_DAY_AM',
 787:                                 'STANDARD_OVERNIGHT',
 788:                                 'PRIORITY_OVERNIGHT',
 789:                                 'FIRST_OVERNIGHT',
 790:                             )
 791:                         ),
 792:                         'from_us' => array(
 793:                             'method' => array(
 794:                                 'INTERNATIONAL_FIRST',
 795:                                 'INTERNATIONAL_ECONOMY',
 796:                                 'INTERNATIONAL_PRIORITY',
 797:                             )
 798:                         )
 799:                     )
 800:                 ),
 801:                 array(
 802:                     'containers' => array('FEDEX_BOX', 'FEDEX_TUBE'),
 803:                     'filters'    => array(
 804:                         'within_us' => array(
 805:                             'method' => array(
 806:                                 'FEDEX_2_DAY',
 807:                                 'FEDEX_2_DAY_AM',
 808:                                 'STANDARD_OVERNIGHT',
 809:                                 'PRIORITY_OVERNIGHT',
 810:                                 'FIRST_OVERNIGHT',
 811:                                 'FEDEX_FREIGHT',
 812:                                 'FEDEX_1_DAY_FREIGHT',
 813:                                 'FEDEX_2_DAY_FREIGHT',
 814:                                 'FEDEX_3_DAY_FREIGHT',
 815:                                 'FEDEX_NATIONAL_FREIGHT',
 816:                             )
 817:                         ),
 818:                         'from_us' => array(
 819:                             'method' => array(
 820:                                 'INTERNATIONAL_FIRST',
 821:                                 'INTERNATIONAL_ECONOMY',
 822:                                 'INTERNATIONAL_PRIORITY',
 823:                             )
 824:                         )
 825:                     )
 826:                 ),
 827:                 array(
 828:                     'containers' => array('FEDEX_10KG_BOX', 'FEDEX_25KG_BOX'),
 829:                     'filters'    => array(
 830:                         'within_us' => array(),
 831:                         'from_us' => array('method' => array('INTERNATIONAL_PRIORITY'))
 832:                     )
 833:                 ),
 834:                 array(
 835:                     'containers' => array('YOUR_PACKAGING'),
 836:                     'filters'    => array(
 837:                         'within_us' => array(
 838:                             'method' =>array(
 839:                                 'FEDEX_GROUND',
 840:                                 'GROUND_HOME_DELIVERY',
 841:                                 'SMART_POST',
 842:                                 'FEDEX_EXPRESS_SAVER',
 843:                                 'FEDEX_2_DAY',
 844:                                 'FEDEX_2_DAY_AM',
 845:                                 'STANDARD_OVERNIGHT',
 846:                                 'PRIORITY_OVERNIGHT',
 847:                                 'FIRST_OVERNIGHT',
 848:                                 'FEDEX_FREIGHT',
 849:                                 'FEDEX_1_DAY_FREIGHT',
 850:                                 'FEDEX_2_DAY_FREIGHT',
 851:                                 'FEDEX_3_DAY_FREIGHT',
 852:                                 'FEDEX_NATIONAL_FREIGHT',
 853:                             )
 854:                         ),
 855:                         'from_us' => array(
 856:                             'method' =>array(
 857:                                 'INTERNATIONAL_FIRST',
 858:                                 'INTERNATIONAL_ECONOMY',
 859:                                 'INTERNATIONAL_PRIORITY',
 860:                                 'INTERNATIONAL_GROUND',
 861:                                 'FEDEX_FREIGHT',
 862:                                 'FEDEX_1_DAY_FREIGHT',
 863:                                 'FEDEX_2_DAY_FREIGHT',
 864:                                 'FEDEX_3_DAY_FREIGHT',
 865:                                 'FEDEX_NATIONAL_FREIGHT',
 866:                                 'INTERNATIONAL_ECONOMY_FREIGHT',
 867:                                 'INTERNATIONAL_PRIORITY_FREIGHT',
 868:                             )
 869:                         )
 870:                     )
 871:                 )
 872:             ),
 873: 
 874:             'delivery_confirmation_types' => array(
 875:                 'NO_SIGNATURE_REQUIRED' => Mage::helper('usa')->__('Not Required'),
 876:                 'ADULT'                 => Mage::helper('usa')->__('Adult'),
 877:                 'DIRECT'                => Mage::helper('usa')->__('Direct'),
 878:                 'INDIRECT'              => Mage::helper('usa')->__('Indirect'),
 879:             ),
 880:         );
 881: 
 882:         if (!isset($codes[$type])) {
 883:             return false;
 884:         } elseif ('' === $code) {
 885:             return $codes[$type];
 886:         }
 887: 
 888:         if (!isset($codes[$type][$code])) {
 889:             return false;
 890:         } else {
 891:             return $codes[$type][$code];
 892:         }
 893:     }
 894: 
 895:     /**
 896:      *  Return FeDex currency ISO code by Magento Base Currency Code
 897:      *
 898:      *  @return string 3-digit currency code
 899:      */
 900:     public function getCurrencyCode ()
 901:     {
 902:         $codes = array(
 903:             'DOP' => 'RDD', // Dominican Peso
 904:             'XCD' => 'ECD', // Caribbean Dollars
 905:             'ARS' => 'ARN', // Argentina Peso
 906:             'SGD' => 'SID', // Singapore Dollars
 907:             'KRW' => 'WON', // South Korea Won
 908:             'JMD' => 'JAD', // Jamaican Dollars
 909:             'CHF' => 'SFR', // Swiss Francs
 910:             'JPY' => 'JYE', // Japanese Yen
 911:             'KWD' => 'KUD', // Kuwaiti Dinars
 912:             'GBP' => 'UKL', // British Pounds
 913:             'AED' => 'DHS', // UAE Dirhams
 914:             'MXN' => 'NMP', // Mexican Pesos
 915:             'UYU' => 'UYP', // Uruguay New Pesos
 916:             'CLP' => 'CHP', // Chilean Pesos
 917:             'TWD' => 'NTD', // New Taiwan Dollars
 918:         );
 919:         $currencyCode = Mage::app()->getStore()->getBaseCurrencyCode();
 920:         return isset($codes[$currencyCode]) ? $codes[$currencyCode] : $currencyCode;
 921:     }
 922: 
 923:     /**
 924:      * Get tracking
 925:      *
 926:      * @param mixed $trackings
 927:      * @return mixed
 928:      */
 929:     public function getTracking($trackings)
 930:     {
 931:         $this->setTrackingReqeust();
 932: 
 933:         if (!is_array($trackings)) {
 934:             $trackings=array($trackings);
 935:         }
 936: 
 937:         foreach($trackings as $tracking){
 938:             $this->_getXMLTracking($tracking);
 939:         }
 940: 
 941:         return $this->_result;
 942:     }
 943: 
 944:     /**
 945:      * Set tracking request
 946:      *
 947:      * @return void
 948:      */
 949:     protected function setTrackingReqeust()
 950:     {
 951:         $r = new Varien_Object();
 952: 
 953:         $account = $this->getConfigData('account');
 954:         $r->setAccount($account);
 955: 
 956:         $this->_rawTrackingRequest = $r;
 957:     }
 958: 
 959:     /**
 960:      * Send request for tracking
 961:      *
 962:      * @param array $tracking
 963:      * @return void
 964:      */
 965:     protected function _getXMLTracking($tracking)
 966:     {
 967:         $trackRequest = array(
 968:             'WebAuthenticationDetail' => array(
 969:                 'UserCredential' => array(
 970:                     'Key'      => $this->getConfigData('key'),
 971:                     'Password' => $this->getConfigData('password')
 972:                 )
 973:             ),
 974:             'ClientDetail' => array(
 975:                 'AccountNumber' => $this->getConfigData('account'),
 976:                 'MeterNumber'   => $this->getConfigData('meter_number')
 977:             ),
 978:             'Version' => array(
 979:                 'ServiceId'    => 'trck',
 980:                 'Major'        => '5',
 981:                 'Intermediate' => '0',
 982:                 'Minor'        => '0'
 983:             ),
 984:             'PackageIdentifier' => array(
 985:                 'Type'  => 'TRACKING_NUMBER_OR_DOORTAG',
 986:                 'Value' => $tracking,
 987:             ),
 988:             /*
 989:              * 0 = summary data, one signle scan structure with the most recent scan
 990:              * 1 = multiple sacn activity for each package
 991:              */
 992:             'IncludeDetailedScans' => 1,
 993:         );
 994:         $requestString = serialize($trackRequest);
 995:         $response = $this->_getCachedQuotes($requestString);
 996:         $debugData = array('request' => $trackRequest);
 997:         if ($response === null) {
 998:             try {
 999:                 $client = $this->_createTrackSoapClient();
1000:                 $response = $client->track($trackRequest);
1001:                 $this->_setCachedQuotes($requestString, serialize($response));
1002:                 $debugData['result'] = $response;
1003:             } catch (Exception $e) {
1004:                 $debugData['result'] = array('error' => $e->getMessage(), 'code' => $e->getCode());
1005:                 Mage::logException($e);
1006:             }
1007:         } else {
1008:             $response = unserialize($response);
1009:             $debugData['result'] = $response;
1010:         }
1011:         $this->_debug($debugData);
1012: 
1013:         $this->_parseTrackingResponse($tracking, $response);
1014:     }
1015: 
1016:     /**
1017:      * Parse tracking response
1018:      *
1019:      * @param array $trackingValue
1020:      * @param stdClass $response
1021:      */
1022:     protected function _parseTrackingResponse($trackingValue, $response)
1023:     {
1024:         if (is_object($response)) {
1025:             if ($response->HighestSeverity == 'FAILURE' || $response->HighestSeverity == 'ERROR') {
1026:                 $errorTitle = (string)$response->Notifications->Message;
1027:             } elseif (isset($response->TrackDetails)) {
1028:                 $trackInfo = $response->TrackDetails;
1029:                 $resultArray['status'] = (string)$trackInfo->StatusDescription;
1030:                 $resultArray['service'] = (string)$trackInfo->ServiceInfo;
1031:                 $timestamp = isset($trackInfo->EstimatedDeliveryTimestamp) ?
1032:                     $trackInfo->EstimatedDeliveryTimestamp : $trackInfo->ActualDeliveryTimestamp;
1033:                 $timestamp = strtotime((string)$timestamp);
1034:                 if ($timestamp) {
1035:                     $resultArray['deliverydate'] = date('Y-m-d', $timestamp);
1036:                     $resultArray['deliverytime'] = date('H:i:s', $timestamp);
1037:                 }
1038: 
1039:                 $deliveryLocation = isset($trackInfo->EstimatedDeliveryAddress) ?
1040:                     $trackInfo->EstimatedDeliveryAddress : $trackInfo->ActualDeliveryAddress;
1041:                 $deliveryLocationArray = array();
1042:                 if (isset($deliveryLocation->City)) {
1043:                     $deliveryLocationArray[] = (string)$deliveryLocation->City;
1044:                 }
1045:                 if (isset($deliveryLocation->StateOrProvinceCode)) {
1046:                     $deliveryLocationArray[] = (string)$deliveryLocation->StateOrProvinceCode;
1047:                 }
1048:                 if (isset($deliveryLocation->CountryCode)) {
1049:                     $deliveryLocationArray[] = (string)$deliveryLocation->CountryCode;
1050:                 }
1051:                 if ($deliveryLocationArray) {
1052:                     $resultArray['deliverylocation'] = implode(', ', $deliveryLocationArray);
1053:                 }
1054: 
1055:                 $resultArray['signedby'] = (string)$trackInfo->DeliverySignatureName;
1056:                 $resultArray['shippeddate'] = date('Y-m-d', (int)$trackInfo->ShipTimestamp);
1057:                 if (isset($trackInfo->PackageWeight) && isset($trackInfo->Units)) {
1058:                     $weight = (string)$trackInfo->PackageWeight;
1059:                     $unit = (string)$trackInfo->Units;
1060:                     $resultArray['weight'] = "{$weight} {$unit}";
1061:                 }
1062: 
1063:                 $packageProgress = array();
1064:                 if (isset($trackInfo->Events)) {
1065:                     $events = $trackInfo->Events;
1066:                     if (isset($events->Address)) {
1067:                         $events = array($events);
1068:                     }
1069:                     foreach ($events as $event) {
1070:                         $tempArray = array();
1071:                         $tempArray['activity'] = (string)$event->EventDescription;
1072:                         $timestamp = strtotime((string)$event->Timestamp);
1073:                         if ($timestamp) {
1074:                             $tempArray['deliverydate'] = date('Y-m-d', $timestamp);
1075:                             $tempArray['deliverytime'] = date('H:i:s', $timestamp);
1076:                         }
1077:                         if (isset($event->Address)) {
1078:                             $addressArray = array();
1079:                             $address = $event->Address;
1080:                             if (isset($address->City)) {
1081:                                 $addressArray[] = (string)$address->City;
1082:                             }
1083:                             if (isset($address->StateOrProvinceCode)) {
1084:                                 $addressArray[] = (string)$address->StateOrProvinceCode;
1085:                             }
1086:                             if (isset($address->CountryCode)) {
1087:                                 $addressArray[] = (string)$address->CountryCode;
1088:                             }
1089:                             if ($addressArray) {
1090:                                 $tempArray['deliverylocation'] = implode(', ', $addressArray);
1091:                             }
1092:                         }
1093:                         $packageProgress[] = $tempArray;
1094:                     }
1095:                 }
1096: 
1097:                 $resultArray['progressdetail'] = $packageProgress;
1098:             }
1099:         }
1100: 
1101:         if (!$this->_result) {
1102:             $this->_result = Mage::getModel('shipping/tracking_result');
1103:         }
1104: 
1105:         if (isset($resultArray)) {
1106:             $tracking = Mage::getModel('shipping/tracking_result_status');
1107:             $tracking->setCarrier('fedex');
1108:             $tracking->setCarrierTitle($this->getConfigData('title'));
1109:             $tracking->setTracking($trackingValue);
1110:             $tracking->addData($resultArray);
1111:             $this->_result->append($tracking);
1112:         } else {
1113:            $error = Mage::getModel('shipping/tracking_result_error');
1114:            $error->setCarrier('fedex');
1115:            $error->setCarrierTitle($this->getConfigData('title'));
1116:            $error->setTracking($trackingValue);
1117:            $error->setErrorMessage($errorTitle ? $errorTitle : Mage::helper('usa')->__('Unable to retrieve tracking'));
1118:            $this->_result->append($error);
1119:         }
1120:     }
1121: 
1122:     /**
1123:      * Parse xml tracking response
1124:      *
1125:      * @deprecated after 1.6.0.0 see _parseTrackingResponse()
1126:      * @param array $trackingvalue
1127:      * @param string $response
1128:      * @return void
1129:      */
1130:     protected function _parseXmlTrackingResponse($trackingvalue, $response)
1131:     {
1132:          $resultArr=array();
1133:          if (strlen(trim($response))>0) {
1134:             if ($xml = $this->_parseXml($response)) {
1135: 
1136:                  if (is_object($xml->Error) && is_object($xml->Error->Message)) {
1137:                     $errorTitle = (string)$xml->Error->Message;
1138:                  } elseif (is_object($xml->SoftError) && is_object($xml->SoftError->Message)) {
1139:                     $errorTitle = (string)$xml->SoftError->Message;
1140:                  }
1141: 
1142:                  if (!isset($errorTitle)) {
1143:                       $resultArr['status'] = (string)$xml->Package->StatusDescription;
1144:                       $resultArr['service'] = (string)$xml->Package->Service;
1145:                       $resultArr['deliverydate'] = (string)$xml->Package->DeliveredDate;
1146:                       $resultArr['deliverytime'] = (string)$xml->Package->DeliveredTime;
1147:                       $resultArr['deliverylocation'] = (string)$xml->TrackProfile->DeliveredLocationDescription;
1148:                       $resultArr['signedby'] = (string)$xml->Package->SignedForBy;
1149:                       $resultArr['shippeddate'] = (string)$xml->Package->ShipDate;
1150:                       $weight = (string)$xml->Package->Weight;
1151:                       $unit = (string)$xml->Package->WeightUnits;
1152:                       $resultArr['weight'] = "{$weight} {$unit}";
1153: 
1154:                       $packageProgress = array();
1155:                       if (isset($xml->Package->Event)) {
1156:                           foreach ($xml->Package->Event as $event) {
1157:                               $tempArr=array();
1158:                               $tempArr['activity'] = (string)$event->Description;
1159:                               $tempArr['deliverydate'] = (string)$event->Date;//YYYY-MM-DD
1160:                               $tempArr['deliverytime'] = (string)$event->Time;//HH:MM:ss
1161:                               $addArr=array();
1162:                               if (isset($event->Address->City)) {
1163:                                 $addArr[] = (string)$event->Address->City;
1164:                               }
1165:                               if (isset($event->Address->StateProvinceCode)) {
1166:                                 $addArr[] = (string)$event->Address->StateProvinceCode;
1167:                               }
1168:                               if (isset($event->Address->CountryCode)) {
1169:                                 $addArr[] = (string)$event->Address->CountryCode;
1170:                               }
1171:                               if ($addArr) {
1172:                                 $tempArr['deliverylocation']=implode(', ',$addArr);
1173:                               }
1174:                               $packageProgress[] = $tempArr;
1175:                           }
1176:                       }
1177: 
1178:                       $resultArr['progressdetail'] = $packageProgress;
1179:                 }
1180:               } else {
1181:                 $errorTitle = 'Response is in the wrong format';
1182:               }
1183:          } else {
1184:              $errorTitle = false;
1185:          }
1186: 
1187:          if (!$this->_result) {
1188:              $this->_result = Mage::getModel('shipping/tracking_result');
1189:          }
1190: 
1191:          if ($resultArr) {
1192:              $tracking = Mage::getModel('shipping/tracking_result_status');
1193:              $tracking->setCarrier('fedex');
1194:              $tracking->setCarrierTitle($this->getConfigData('title'));
1195:              $tracking->setTracking($trackingvalue);
1196:              $tracking->addData($resultArr);
1197:              $this->_result->append($tracking);
1198:          } else {
1199:             $error = Mage::getModel('shipping/tracking_result_error');
1200:             $error->setCarrier('fedex');
1201:             $error->setCarrierTitle($this->getConfigData('title'));
1202:             $error->setTracking($trackingvalue);
1203:             $error->setErrorMessage($errorTitle ? $errorTitle : Mage::helper('usa')->__('Unable to retrieve tracking'));
1204:             $this->_result->append($error);
1205:          }
1206:     }
1207: 
1208:     /**
1209:      * Get tracking response
1210:      *
1211:      * @return string
1212:      */
1213:     public function getResponse()
1214:     {
1215:         $statuses = '';
1216:         if ($this->_result instanceof Mage_Shipping_Model_Tracking_Result) {
1217:             if ($trackings = $this->_result->getAllTrackings()) {
1218:                 foreach ($trackings as $tracking){
1219:                     if($data = $tracking->getAllData()){
1220:                         if (!empty($data['status'])) {
1221:                             $statuses .= Mage::helper('usa')->__($data['status']) . "\n<br/>";
1222:                         } else {
1223:                             $statuses .= Mage::helper('usa')->__('Empty response') . "\n<br/>";
1224:                         }
1225:                     }
1226:                 }
1227:             }
1228:         }
1229:         if (empty($statuses)) {
1230:             $statuses = Mage::helper('usa')->__('Empty response');
1231:         }
1232:         return $statuses;
1233:     }
1234: 
1235:     /**
1236:      * Get allowed shipping methods
1237:      *
1238:      * @return array
1239:      */
1240:     public function getAllowedMethods()
1241:     {
1242:         $allowed = explode(',', $this->getConfigData('allowed_methods'));
1243:         $arr = array();
1244:         foreach ($allowed as $k) {
1245:             $arr[$k] = $this->getCode('method', $k);
1246:         }
1247:         return $arr;
1248:     }
1249: 
1250:     /**
1251:      * Return array of authenticated information
1252:      *
1253:      * @return array
1254:      */
1255:     protected function _getAuthDetails()
1256:     {
1257:         return array(
1258:             'WebAuthenticationDetail' => array(
1259:                 'UserCredential' => array(
1260:                     'Key'      => $this->getConfigData('key'),
1261:                     'Password' => $this->getConfigData('password')
1262:                 )
1263:             ),
1264:             'ClientDetail' => array(
1265:                 'AccountNumber' => $this->getConfigData('account'),
1266:                 'MeterNumber'   => $this->getConfigData('meter_number')
1267:             ),
1268:             'TransactionDetail' => array(
1269:                 'CustomerTransactionId' => '*** Express Domestic Shipping Request v9 using PHP ***'
1270:             ),
1271:             'Version' => array(
1272:                 'ServiceId'     => 'ship',
1273:                 'Major'         => '10',
1274:                 'Intermediate'  => '0',
1275:                 'Minor'         => '0'
1276:             )
1277:         );
1278:     }
1279: 
1280:     /**
1281:      * Form array with appropriate structure for shipment request
1282:      *
1283:      * @param Varien_Object $request
1284:      * @return array
1285:      */
1286:     protected function _formShipmentRequest(Varien_Object $request)
1287:     {
1288:         if ($request->getReferenceData()) {
1289:             $referenceData = $request->getReferenceData() . $request->getPackageId();
1290:         } else {
1291:             $referenceData = 'Order #'
1292:                              . $request->getOrderShipment()->getOrder()->getIncrementId()
1293:                              . ' P'
1294:                              . $request->getPackageId();
1295:         }
1296:         $packageParams = $request->getPackageParams();
1297:         $customsValue = $packageParams->getCustomsValue();
1298:         $height = $packageParams->getHeight();
1299:         $width = $packageParams->getWidth();
1300:         $length = $packageParams->getLength();
1301:         $weightUnits = $packageParams->getWeightUnits() == Zend_Measure_Weight::POUND ? 'LB' : 'KG';
1302:         $dimensionsUnits = $packageParams->getDimensionUnits() == Zend_Measure_Length::INCH ? 'IN' : 'CM';
1303:         $unitPrice = 0;
1304:         $itemsQty = 0;
1305:         $itemsDesc = array();
1306:         $countriesOfManufacture = array();
1307:         $productIds = array();
1308:         $packageItems = $request->getPackageItems();
1309:         foreach ($packageItems as $itemShipment) {
1310:                 $item = new Varien_Object();
1311:                 $item->setData($itemShipment);
1312: 
1313:                 $unitPrice  += $item->getPrice();
1314:                 $itemsQty   += $item->getQty();
1315: 
1316:                 $itemsDesc[]    = $item->getName();
1317:                 $productIds[]   = $item->getProductId();
1318:         }
1319: 
1320:         // get countries of manufacture
1321:         $productCollection = Mage::getResourceModel('catalog/product_collection')
1322:             ->addStoreFilter($request->getStoreId())
1323:             ->addFieldToFilter('entity_id', array('in' => $productIds))
1324:             ->addAttributeToSelect('country_of_manufacture');
1325:         foreach ($productCollection as $product) {
1326:             $countriesOfManufacture[] = $product->getCountryOfManufacture();
1327:         }
1328: 
1329:         $paymentType = $request->getIsReturn() ? 'RECIPIENT' : 'SENDER';
1330:         $requestClient = array(
1331:             'RequestedShipment' => array(
1332:                 'ShipTimestamp' => time(),
1333:                 'DropoffType'   => $this->getConfigData('dropoff'),
1334:                 'PackagingType' => $request->getPackagingType(),
1335:                 'ServiceType' => $request->getShippingMethod(),
1336:                 'Shipper' => array(
1337:                     'Contact' => array(
1338:                         'PersonName' => $request->getShipperContactPersonName(),
1339:                         'CompanyName' => $request->getShipperContactCompanyName(),
1340:                         'PhoneNumber' => $request->getShipperContactPhoneNumber()
1341:                     ),
1342:                     'Address' => array(
1343:                         'StreetLines' => array($request->getShipperAddressStreet()),
1344:                         'City' => $request->getShipperAddressCity(),
1345:                         'StateOrProvinceCode' => $request->getShipperAddressStateOrProvinceCode(),
1346:                         'PostalCode' => $request->getShipperAddressPostalCode(),
1347:                         'CountryCode' => $request->getShipperAddressCountryCode()
1348:                     )
1349:                 ),
1350:                 'Recipient' => array(
1351:                     'Contact' => array(
1352:                         'PersonName' => $request->getRecipientContactPersonName(),
1353:                         'CompanyName' => $request->getRecipientContactCompanyName(),
1354:                         'PhoneNumber' => $request->getRecipientContactPhoneNumber()
1355:                     ),
1356:                     'Address' => array(
1357:                         'StreetLines' => array($request->getRecipientAddressStreet()),
1358:                         'City' => $request->getRecipientAddressCity(),
1359:                         'StateOrProvinceCode' => $request->getRecipientAddressStateOrProvinceCode(),
1360:                         'PostalCode' => $request->getRecipientAddressPostalCode(),
1361:                         'CountryCode' => $request->getRecipientAddressCountryCode(),
1362:                         'Residential' => (bool)$this->getConfigData('residence_delivery')
1363:                     ),
1364:                 ),
1365:                 'ShippingChargesPayment' => array(
1366:                     'PaymentType' => $paymentType,
1367:                     'Payor' => array(
1368:                         'AccountNumber' => $this->getConfigData('account'),
1369:                         'CountryCode'   => Mage::getStoreConfig(
1370:                             Mage_Shipping_Model_Shipping::XML_PATH_STORE_COUNTRY_ID,
1371:                             $request->getStoreId()
1372:                         )
1373:                     )
1374:                 ),
1375:                 'LabelSpecification' =>array(
1376:                     'LabelFormatType' => 'COMMON2D',
1377:                     'ImageType' => 'PNG',
1378:                     'LabelStockType' => 'PAPER_8.5X11_TOP_HALF_LABEL',
1379:                 ),
1380:                 'RateRequestTypes'  => array('ACCOUNT'),
1381:                 'PackageCount'      => 1,
1382:                 'RequestedPackageLineItems' => array(
1383:                     'SequenceNumber' => '1',
1384:                     'Weight' => array(
1385:                         'Units' => $weightUnits,
1386:                         'Value' =>  $request->getPackageWeight()
1387:                     ),
1388:                     'CustomerReferences' => array(
1389:                         'CustomerReferenceType' => 'CUSTOMER_REFERENCE',
1390:                         'Value' => $referenceData
1391:                     ),
1392:                     'SpecialServicesRequested' => array(
1393:                         'SpecialServiceTypes' => 'SIGNATURE_OPTION',
1394:                         'SignatureOptionDetail' => array('OptionType' => $packageParams->getDeliveryConfirmation())
1395:                     ),
1396:                 )
1397:             )
1398:         );
1399: 
1400:         // for international shipping
1401:         if ($request->getShipperAddressCountryCode() != $request->getRecipientAddressCountryCode()) {
1402:             $requestClient['RequestedShipment']['CustomsClearanceDetail'] =
1403:                 array(
1404:                     'CustomsValue' =>
1405:                     array(
1406:                         'Currency' => $request->getBaseCurrencyCode(),
1407:                         'Amount' => $customsValue,
1408:                     ),
1409:                     'DutiesPayment' => array(
1410:                         'PaymentType' => $paymentType,
1411:                         'Payor' => array(
1412:                             'AccountNumber' => $this->getConfigData('account'),
1413:                             'CountryCode'   => Mage::getStoreConfig(
1414:                                 Mage_Shipping_Model_Shipping::XML_PATH_STORE_COUNTRY_ID,
1415:                                 $request->getStoreId()
1416:                             )
1417:                         )
1418:                     ),
1419:                     'Commodities' => array(
1420:                         'Weight' => array(
1421:                             'Units' => $weightUnits,
1422:                             'Value' =>  $request->getPackageWeight()
1423:                         ),
1424:                         'NumberOfPieces' => 1,
1425:                         'CountryOfManufacture' => implode(',', array_unique($countriesOfManufacture)),
1426:                         'Description' => implode(', ', $itemsDesc),
1427:                         'Quantity' => ceil($itemsQty),
1428:                         'QuantityUnits' => 'pcs',
1429:                         'UnitPrice' => array(
1430:                             'Currency' => $request->getBaseCurrencyCode(),
1431:                             'Amount' =>  $unitPrice
1432:                         ),
1433:                         'CustomsValue' => array(
1434:                             'Currency' => $request->getBaseCurrencyCode(),
1435:                             'Amount' =>  $customsValue
1436:                         ),
1437:                     )
1438:                 );
1439:         }
1440: 
1441:         if ($request->getMasterTrackingId()) {
1442:             $requestClient['RequestedShipment']['MasterTrackingId'] = $request->getMasterTrackingId();
1443:         }
1444: 
1445:         // set dimensions
1446:         if ($length || $width || $height) {
1447:             $requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions'] = array();
1448:             $dimenssions = &$requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions'];
1449:             $dimenssions['Length'] = $length;
1450:             $dimenssions['Width']  = $width;
1451:             $dimenssions['Height'] = $height;
1452:             $dimenssions['Units'] = $dimensionsUnits;
1453:         }
1454: 
1455:         return $this->_getAuthDetails() + $requestClient;
1456:     }
1457: 
1458:     /**
1459:      * Do shipment request to carrier web service, obtain Print Shipping Labels and process errors in response
1460:      *
1461:      * @param Varien_Object $request
1462:      * @return Varien_Object
1463:      */
1464:     protected function _doShipmentRequest(Varien_Object $request)
1465:     {
1466:         $this->_prepareShipmentRequest($request);
1467:         $result = new Varien_Object();
1468:         $client = $this->_createShipSoapClient();
1469:         $requestClient = $this->_formShipmentRequest($request);
1470:         $response = $client->processShipment($requestClient);
1471: 
1472:         if ($response->HighestSeverity != 'FAILURE' && $response->HighestSeverity != 'ERROR') {
1473:             $shippingLabelContent = $response->CompletedShipmentDetail->CompletedPackageDetails->Label->Parts->Image;
1474:             $trackingNumber = $response->CompletedShipmentDetail->CompletedPackageDetails->TrackingIds->TrackingNumber;
1475:             $result->setShippingLabelContent($shippingLabelContent);
1476:             $result->setTrackingNumber($trackingNumber);
1477:             $debugData = array('request' => $client->__getLastRequest(), 'result' => $client->__getLastResponse());
1478:             $this->_debug($debugData);
1479:         } else {
1480:             $debugData = array(
1481:                 'request' => $client->__getLastRequest(),
1482:                 'result' => array(
1483:                     'error' => '',
1484:                     'code' => '',
1485:                     'xml' => $client->__getLastResponse()
1486:                 )
1487:             );
1488:             if (is_array($response->Notifications)) {
1489:                 foreach ($response->Notifications as $notification) {
1490:                     $debugData['result']['code'] .= $notification->Code . '; ';
1491:                     $debugData['result']['error'] .= $notification->Message . '; ';
1492:                 }
1493:             } else {
1494:                 $debugData['result']['code'] = $response->Notifications->Code . ' ';
1495:                 $debugData['result']['error'] = $response->Notifications->Message . ' ';
1496:             }
1497:             $this->_debug($debugData);
1498:             $result->setErrors($debugData['result']['error']);
1499:         }
1500:         $result->setGatewayResponse($client->__getLastResponse());
1501: 
1502:         return $result;
1503:     }
1504: 
1505:     /**
1506:      * For multi package shipments. Delete requested shipments if the current shipment
1507:      * request is failed
1508:      *
1509:      * @param array $data
1510:      * @return bool
1511:      */
1512:     public function rollBack($data)
1513:     {
1514:         $requestData = $this->_getAuthDetails();
1515:         $requestData['DeletionControl'] = 'DELETE_ONE_PACKAGE';
1516:         foreach ($data as &$item) {
1517:             $requestData['TrackingId'] = $item['tracking_number'];
1518:             $client = $this->_createShipSoapClient();
1519:             $client->deleteShipment($requestData);
1520:         }
1521:         return true;
1522:     }
1523: 
1524:     /**
1525:      * Return container types of carrier
1526:      *
1527:      * @param Varien_Object|null $params
1528:      * @return array|bool
1529:      */
1530:     public function getContainerTypes(Varien_Object $params = null)
1531:     {
1532:         if ($params == null) {
1533:             return $this->_getAllowedContainers($params);
1534:         }
1535:         $method             = $params->getMethod();
1536:         $countryShipper     = $params->getCountryShipper();
1537:         $countryRecipient   = $params->getCountryRecipient();
1538: 
1539:         if (($countryShipper == self::USA_COUNTRY_ID && $countryRecipient == self::CANADA_COUNTRY_ID
1540:             || $countryShipper == self::CANADA_COUNTRY_ID && $countryRecipient == self::USA_COUNTRY_ID)
1541:             && $method == 'FEDEX_GROUND'
1542:         ) {
1543:             return array('YOUR_PACKAGING' => Mage::helper('usa')->__('Your Packaging'));
1544:         } else if ($method == 'INTERNATIONAL_ECONOMY' || $method == 'INTERNATIONAL_FIRST') {
1545:             $allTypes = $this->getContainerTypesAll();
1546:             $exclude = array('FEDEX_10KG_BOX' => '', 'FEDEX_25KG_BOX' => '');
1547:             return array_diff_key($allTypes, $exclude);
1548:         } else if ($method == 'EUROPE_FIRST_INTERNATIONAL_PRIORITY') {
1549:             $allTypes = $this->getContainerTypesAll();
1550:             $exclude = array('FEDEX_BOX' => '', 'FEDEX_TUBE' => '');
1551:             return array_diff_key($allTypes, $exclude);
1552:         } else if ($countryShipper == self::CANADA_COUNTRY_ID && $countryRecipient == self::CANADA_COUNTRY_ID) {
1553:             // hack for Canada domestic. Apply the same filter rules as for US domestic
1554:             $params->setCountryShipper(self::USA_COUNTRY_ID);
1555:             $params->setCountryRecipient(self::USA_COUNTRY_ID);
1556:         }
1557: 
1558:         return $this->_getAllowedContainers($params);
1559:     }
1560: 
1561:     /**
1562:      * Return all container types of carrier
1563:      *
1564:      * @return array|bool
1565:      */
1566:     public function getContainerTypesAll()
1567:     {
1568:         return $this->getCode('packaging');
1569:     }
1570: 
1571:     /**
1572:      * Return structured data of containers witch related with shipping methods
1573:      *
1574:      * @return array|bool
1575:      */
1576:     public function getContainerTypesFilter()
1577:     {
1578:         return $this->getCode('containers_filter');
1579:     }
1580: 
1581:     /**
1582:      * Return delivery confirmation types of carrier
1583:      *
1584:      * @param Varien_Object|null $params
1585:      * @return array
1586:      */
1587:     public function getDeliveryConfirmationTypes(Varien_Object $params = null)
1588:     {
1589:         return $this->getCode('delivery_confirmation_types');
1590:     }
1591: }
1592: 
Magento 1.7.0.2 API documentation generated by ApiGen 2.8.0