A Qcodo based CMS/ecommerce framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

805 lines
39 KiB

13 years ago
  1. <?php
  2. /**
  3. * This file is a part of Quasi CMS
  4. *@package Quasi
  5. */
  6. if(!defined('QUASICMS') ) die('No Quasi.');
  7. if (!defined("FEDEXREQUEST.CLASS.PHP")){
  8. define("FEDEXREQUEST.CLASS.PHP",1);
  9. /**
  10. * Class FedexRequest - provides services for Fedex Direct Connect
  11. * This class implements the FEDEX SOAP API for web service requests.
  12. *
  13. * NOTE: ServiceType as configured in the database must be one of the following:
  14. * - EUROPE_FIRST_INTERNATIONAL_PRIORITY
  15. * - FEDEX_1_DAY_FREIGHT
  16. * - FEDEX_2_DAY
  17. * - FEDEX_2_DAY_FREIGHT
  18. * - FEDEX_3_DAY_FREIGHT
  19. * - FEDEX_EXPRESS_SAVER
  20. * - FEDEX_GROUND
  21. * - FIRST_OVERNIGHT
  22. * - GROUND_HOME_DELIVERY
  23. * - INTERNATIONAL_ECONOMY
  24. * - INTERNATIONAL_ECONOMY_FREIGHT
  25. * - INTERNATIONAL_FIRST
  26. * - INTERNATIONAL_PRIORITY
  27. * - INTERNATIONAL_PRIORITY_FREIGHT
  28. * - PRIORITY_OVERNIGHT
  29. * - STANDARD_OVERNIGHT
  30. *
  31. *@todo - document this class ..
  32. *
  33. *@author Erik Winn <erikwinnmail@yahoo.com>
  34. *
  35. * $Id: FedexRequest.class.php 517 2009-03-24 17:59:23Z erikwinn $
  36. *@version 0.1
  37. *
  38. *@copyright (C) 2008 by Erik Winn
  39. *@license GPL v.2
  40. This program is free software; you can redistribute it and/or modify
  41. it under the terms of the GNU General Public License as published by
  42. the Free Software Foundation; either version 2 of the License, or
  43. (at your option) any later version.
  44. This program is distributed in the hope that it will be useful,
  45. but WITHOUT ANY WARRANTY; without even the implied warranty of
  46. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  47. GNU General Public License for more details.
  48. You should have received a copy of the GNU General Public License
  49. along with this program; if not, write to the Free Software
  50. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
  51. *
  52. *@package Quasi
  53. * @subpackage Classes
  54. */
  55. class FedexRequest extends ShippingRequest
  56. {
  57. /**
  58. * NOTE: LabelStockType must be one of the following:
  59. * - PAPER_4X6
  60. * - PAPER_4X8
  61. * - PAPER_4X9
  62. * - PAPER_7X4.75
  63. * - PAPER_8.5X11_BOTTOM_HALF_LABEL
  64. * - PAPER_8.5X11_TOP_HALF_LABEL
  65. * - STOCK_4X6
  66. * - STOCK_4X6.75_LEADING_DOC_TAB
  67. * - STOCK_4X6.75_TRAILING_DOC_TAB
  68. * - STOCK_4X8
  69. * - STOCK_4X9_LEADING_DOC_TAB
  70. * - STOCK_4X9_TRAILING_DOC_TAB
  71. * @var string
  72. */
  73. protected $strLabelStockType = 'PAPER_4X6';
  74. /**
  75. * NOTE: LabelFormatType must be one of the following:
  76. * COMMON2D
  77. * LABEL_DATA_ONLY
  78. *@var string paper format for label
  79. */
  80. protected $strLabelFormatType = 'COMMON2D';
  81. /**
  82. * NOTE: ImageType must be one of the following:
  83. * DPL
  84. * EPL2
  85. * PDF
  86. * PNG
  87. * ZPLII
  88. *@var string image format for label
  89. */
  90. protected $strImageType = 'PNG';
  91. /**
  92. *@var string Account number provided by Fedex
  93. */
  94. protected $strAccountNumber;
  95. /**
  96. *@var string Meter number provided by Fedex
  97. */
  98. protected $strMeterNumber;
  99. /**
  100. *@var string strPayorType - who pays for the shipping
  101. */
  102. protected $strPayorType = 'SENDER';
  103. /**
  104. * NOTE: DropoffType must be one of the following:
  105. * - BUSINESS_SERVICE_CENTER
  106. * - DROP_BOX
  107. * - REGULAR_PICKUP
  108. * - REQUEST_COURIER
  109. * - STATION
  110. *@var string Drop off method
  111. */
  112. protected $strDropoffType = 'REGULAR_PICKUP';
  113. /**
  114. * NOTE: PackagingType must be one of the following:
  115. * - FEDEX_10KG_BOX
  116. * - FEDEX_25KG_BOX
  117. * - FEDEX_BOX
  118. * - FEDEX_ENVELOPE
  119. * - FEDEX_PAK
  120. * - FEDEX_TUBE
  121. * - YOUR_PACKAGING
  122. *@var string Packaging type
  123. */
  124. protected $strPackagingType = 'YOUR_PACKAGING';
  125. /**
  126. *@var string Indicates units of weight (LB | KG)
  127. */
  128. protected $strWeightUnits = 'LB';
  129. /**
  130. *@var string Indicates units of length (IN | CM)
  131. */
  132. protected $strLengthUnits = 'IN';
  133. /**
  134. * FedEx combines units (eg. pounds and ounces ) into one figure ..
  135. *@var float fltWeight - weight of package in designated units ..
  136. */
  137. protected $fltWeight;
  138. /**
  139. * FedexRequest Constructor
  140. *
  141. * @param ShippingMethod objShippingMethod - the method for which to obtain estimate
  142. */
  143. public function __construct(ShippingMethod $objShippingMethod)
  144. {
  145. parent::__construct($objShippingMethod);
  146. //now unused ..
  147. $this->strRemoteCgiUrl = '/GatewayDC';
  148. if($objShippingMethod->TestMode)
  149. {///@todo defined in config - fixme!
  150. $this->strRemoteDomainName = 'gatewaybeta.fedex.com';
  151. $this->strRemoteAccountId = FEDEX_TESTKEY;
  152. $this->strRemotePassword = FEDEX_TESTPASSWORD;
  153. $this->strAccountNumber = FEDEX_TESTACCOUNT_NUMBER;
  154. $this->strMeterNumber = FEDEX_TESTMETER_NUMBER;
  155. $this->strRemoteDomainName = 'gatewaybeta.fedex.com'; //unused ..
  156. }
  157. else
  158. {
  159. $this->strRemoteDomainName = 'gateway.fedex.com';
  160. $this->strRemoteAccountId = FEDEX_KEY;
  161. $this->strRemotePassword = FEDEX_PASSWORD;
  162. $this->strAccountNumber = FEDEX_ACCOUNT_NUMBER;
  163. $this->strMeterNumber = FEDEX_METER_NUMBER;
  164. $this->strRemoteDomainName = 'gateway.fedex.com'; //unused ..
  165. }
  166. $this->fltWeight = $objShippingMethod->Pounds;
  167. $this->fltWeight += $objShippingMethod->Ounces / 16;
  168. }
  169. //Public interface ..
  170. /**
  171. * Returns a shipping rate for the order for this method
  172. *@return image object containing the image code
  173. */
  174. public function GetRate()
  175. {
  176. $this->intShippingRequestType = ShippingRequestType::Rate;
  177. //Example code does this - not sure why or if it is needed ..
  178. ini_set("soap.wsdl_cache_enabled", "0");
  179. if($this->blnTestMode)
  180. $this->strWsdlUri = __QUASI_CORE__ . '/utilities/FDXRateService_v5_test.wsdl';
  181. else
  182. $this->strWsdlUri = __QUASI_CORE__ . '/utilities/FDXRateService_v5.wsdl';
  183. $this->strSoapFunction = 'getRates';
  184. $this->createSoapRateRequest();
  185. try{
  186. $this->submitSoapRequest();
  187. } catch (SoapFault $objFault) {
  188. // exit(var_dump($objFault));
  189. throw new Exception($objFault->faultstring);
  190. }
  191. $this->handleRateResponse();
  192. return $this->Rate;
  193. }
  194. /**
  195. * Utility function to format the mult-dimensional array of RateRequest (labels) request data
  196. * to be passed on the the Soap client function.
  197. *@return array - suitable for passing to the Soap function for RateRequest (labels) requests
  198. */
  199. protected function createSoapRateRequest()
  200. {
  201. $arySoapParamsToReturn['WebAuthenticationDetail'] = $this->createWebAuthDetailArray();
  202. $arySoapParamsToReturn['ClientDetail'] = $this->createClientDetailArray();
  203. $arySoapParamsToReturn['TransactionDetail'] = $this->createTransactionDetailArray();
  204. $arySoapParamsToReturn['Version'] = $this->createVersionDetailArray('crs');
  205. $aryRequestedShipment = $this->createRequestedShipmentDetailArray();
  206. $aryRequestedShipment['PackageDetail'] = 'INDIVIDUAL_PACKAGES';
  207. $arySoapParamsToReturn['RequestedShipment'] = $aryRequestedShipment;
  208. $this->arySoapRequest = $arySoapParamsToReturn;
  209. }
  210. /**
  211. * Parses the rate request SOAP response from FEDEX server
  212. * @todo - handle errors more elegantly, make more robust ..
  213. */
  214. protected function handleRateResponse()
  215. {
  216. $this->Rate = 0;
  217. //first check for Fedex errors
  218. if('FAILURE' == $this->mixSoapResponse->HighestSeverity
  219. || 'ERROR' == $this->mixSoapResponse->HighestSeverity )
  220. {
  221. $this->blnIsAvailable = false;
  222. $this->blnHasErrors = true;
  223. $mixNotifications = $this->mixSoapResponse->Notifications;
  224. if(is_array($mixNotifications))
  225. foreach($mixNotifications as $objNotification)
  226. $this->strErrors .= $objNotification->Severity . ':' . $objNotification->Message . "\n";
  227. else
  228. $this->strErrors .= $mixNotifications->Severity . ' : ' .$mixNotifications->Message . "\n";
  229. //Service is not allowed to destination - eg. fedex_2_day to Australia .. not really
  230. // an error, just not available so reset HasErrors:
  231. if( false !== stripos( $this->strErrors, 'Service is not allowed' ) )
  232. $this->blnHasErrors = false;
  233. } else {
  234. $this->blnIsAvailable = true;
  235. $mixDetails = $this->mixSoapResponse->RateReplyDetails->RatedShipmentDetails;
  236. if(is_array($mixDetails))
  237. $this->Rate = $mixDetails[0]->ShipmentRateDetail->TotalNetCharge->Amount;
  238. else
  239. $this->Rate = $mixDetails->ShipmentRateDetail->TotalNetCharge->Amount;
  240. }
  241. if($this->blnIsAvailable && !$this->Rate)
  242. {
  243. $this->blnIsAvailable = false;
  244. $this->HasErrors = true;
  245. $this->strErrors = 'Unknown Fedex Error.';
  246. }
  247. return $this->Rate;
  248. }
  249. /**
  250. * Returns a shipping label for this method to the order address
  251. * Note: this function uses the SOAP interface provided from Fedex - partially due to time constraints
  252. * and partially to remind me to reimplement these service request classes to use SOAP; all of the
  253. * web services seem to be providing WSDLs now and it is builtin to PHP so it makes sense. Later.
  254. * This quick example implements the "new" style Fedex API.
  255. *@return string containing label image or null on failure ..
  256. */
  257. public function GetLabel()
  258. {
  259. $this->intShippingRequestType = ShippingRequestType::Label;
  260. //Example code does this - not sure why or if it is needed ..
  261. ini_set("soap.wsdl_cache_enabled", "0");
  262. if($this->blnTestMode)
  263. $this->strWsdlUri = __QUASI_CORE__ . '/utilities/FDXShipService_v5_test.wsdl';
  264. else
  265. $this->strWsdlUri = __QUASI_CORE__ . '/utilities/FDXShipService_v5.wsdl';
  266. $this->strSoapFunction = 'processShipment';
  267. $this->createSoapShipRequest();
  268. try{
  269. $this->submitSoapRequest();
  270. } catch (SoapFault $objFault) {
  271. // exit(var_dump($objFault));
  272. throw new Exception($objFault->faultstring . ' - '
  273. . $objFault->detail->fault->details->ValidationFailureDetail->message);
  274. }
  275. return $this->handleLabelResponse();
  276. }
  277. /**
  278. * Handles a label response returning the image. On failure the image is null and errors are
  279. * stored in strErrors. The image is the string containing the formatted image from Fedex - this
  280. * can be written directly to a file as eg. image.png or image.pdf ..
  281. * @return string containing the image code returned from Fedex
  282. */
  283. protected function handleLabelResponse()
  284. {
  285. $this->objShippingLabelImage = null;
  286. //first check for Fedex errors
  287. if('FAILURE' == $this->mixSoapResponse->HighestSeverity
  288. || 'ERROR' == $this->mixSoapResponse->HighestSeverity )
  289. {
  290. $this->blnHasErrors = true;
  291. //Note: this part is unclear - Notifications can be an array or object?? May not work correctly ..
  292. if(is_array($this->mixSoapResponse->Notifications))
  293. foreach($this->mixSoapResponse->Notifications as $objNotification)
  294. $this->strErrors .= $objNotification->Severity . ':' . $objNotification->Message . "\n";
  295. else
  296. $this->strErrors .= $this->mixSoapResponse->Notifications->Severity
  297. . ': ' . $this->mixSoapResponse->Notifications->Message . "\n";
  298. } else {
  299. $objPackageDetails = $this->mixSoapResponse->CompletedShipmentDetail->CompletedPackageDetails;
  300. $this->objShippingLabelImage = $objPackageDetails->Label->Parts->Image;
  301. $strFinalPrice = $this->mixSoapResponse->CompletedShipmentDetail->ShipmentRating;
  302. if(!empty($strFinalPrice))
  303. {
  304. $this->objShippingMethod->Order->ShippingCost = $strFinalPrice;
  305. $this->objShippingMethod->Order->Save(false,true);
  306. }
  307. if($this->Order->IsInternational)
  308. {
  309. if(property_exists($objPackageDetails, 'PackageDocuments'))
  310. {
  311. $aryPackageDocuments = $objPackageDetails->PackageDocuments;
  312. foreach($aryPackageDocuments as $objDocument)
  313. {
  314. $this->aryExtraDocumentImages[] = new ExtraDocumentImage(
  315. $objDocument->Parts->Image,
  316. $objDocument->Type,
  317. $objDocument->CopiesToPrint
  318. );
  319. }
  320. }
  321. }
  322. $strTrackingNumber = $objPackageDetails->TrackingId->TrackingNumber;
  323. if(!empty($strTrackingNumber))
  324. {
  325. if(! TrackingNumber::LoadByOrderIdNumber($this->objShippingMethod->OrderId, $strTrackingNumber ))
  326. {
  327. $objTrackingNumber = new TrackingNumber();
  328. $objTrackingNumber->OrderId = $this->objShippingMethod->OrderId;
  329. $objTrackingNumber->Number = $strTrackingNumber;
  330. $objTrackingNumber->Save();
  331. }
  332. }
  333. }
  334. return $this->objShippingLabelImage;
  335. }
  336. /**
  337. * Utility function to format the mult-dimensional array of ShipRequest (labels) request data
  338. * to be passed on the the Soap client function.
  339. *@return array - suitable for passing to the Soap function for ShipRequest (labels) requests
  340. */
  341. protected function createSoapShipRequest()
  342. {
  343. $arySoapParamsToReturn['WebAuthenticationDetail'] = $this->createWebAuthDetailArray();
  344. $arySoapParamsToReturn['ClientDetail'] = $this->createClientDetailArray();
  345. $arySoapParamsToReturn['TransactionDetail'] = $this->createTransactionDetailArray();
  346. $arySoapParamsToReturn['Version'] = $this->createVersionDetailArray('ship');
  347. $aryRequestedShipment = $this->createRequestedShipmentDetailArray();
  348. $aryRequestedShipment['LabelSpecification'] = array('LabelFormatType' => $this->strLabelFormatType,
  349. 'ImageType' => $this->strImageType,
  350. 'LabelStockType' => $this->strLabelStockType,
  351. );
  352. $arySoapParamsToReturn['RequestedShipment'] = $aryRequestedShipment;
  353. $this->arySoapRequest = $arySoapParamsToReturn;
  354. }
  355. protected function createWebAuthDetailArray()
  356. {
  357. return array('UserCredential' => array('Key' => $this->strRemoteAccountId,
  358. 'Password' => $this->strRemotePassword
  359. )
  360. );
  361. }
  362. protected function createClientDetailArray()
  363. {
  364. return array('AccountNumber' => $this->strAccountNumber,
  365. 'MeterNumber' => $this->strMeterNumber
  366. );
  367. }
  368. protected function createTransactionDetailArray()
  369. {
  370. return array('CustomerTransactionId' => STORE_NAME . ' Order ' . $this->Order->Id );
  371. }
  372. protected function createVersionDetailArray($strService)
  373. {
  374. return array('ServiceId' => $strService, 'Major' => '5', 'Intermediate' => '0', 'Minor' => '0');
  375. }
  376. protected function createShipperDetailArray()
  377. {
  378. $strSenderCountryCode = CountryType::ToIsoCode2( $this->OriginCountryId);
  379. $arySenderStreets = array();
  380. if(STORE_ADDRESS1)
  381. $arySenderStreets[] = STORE_ADDRESS1;
  382. if(STORE_ADDRESS2)
  383. $arySenderStreets[] = STORE_ADDRESS2;
  384. if(! count($arySenderStreets))
  385. throw new Exception('FedexRequest: Shipper address must have at least one street line!');
  386. return array('Contact' => array('PersonName' => STORE_OWNER,
  387. 'CompanyName' => STORE_NAME,
  388. 'PhoneNumber' => STORE_PHONE,
  389. ),
  390. 'Address' => array('StreetLines' =>$arySenderStreets,
  391. 'City' => STORE_CITY,
  392. 'StateOrProvinceCode' => STORE_STATE,
  393. 'PostalCode' => STORE_POSTAL_CODE,
  394. 'CountryCode' => $strSenderCountryCode,
  395. ),
  396. );
  397. }
  398. protected function createRecipientDetailArray()
  399. {
  400. $strRecipientName = $this->Order->FullShippingName;
  401. $strRecipientCompany = $this->Order->ShippingCompany;
  402. $strRecipientCity = $this->Order->ShippingCity;
  403. $strRecipientPhone = $this->Order->Account->Person->PhoneNumber;
  404. $aryRecipientStreets = array();
  405. if($this->Order->ShippingStreet1)
  406. $aryRecipientStreets[] = $this->Order->ShippingStreet1;
  407. if($this->Order->ShippingStreet2)
  408. $aryRecipientStreets[] = $this->Order->ShippingStreet2;
  409. if(! count($aryRecipientStreets))
  410. throw new Exception('FedexRequest: Recipient address must have at least one street line!');
  411. $aryToReturn = array('Contact' => array('PersonName' => $strRecipientName,
  412. 'CompanyName' => $strRecipientCompany,
  413. 'PhoneNumber' => $strRecipientPhone,
  414. ),
  415. 'Address' => array('StreetLines' => $aryRecipientStreets,
  416. 'City' => $strRecipientCity,
  417. //Fedex barfs if the state or province is over 2 chars ..
  418. 'StateOrProvinceCode' => substr($this->DestinationStateCode, 0, 2),
  419. 'PostalCode' => $this->DestinationZip,
  420. 'CountryCode' => $this->DestinationCountryCode,
  421. ),
  422. );
  423. return $aryToReturn;
  424. }
  425. protected function createRequestedShipmentDetailArray()
  426. {
  427. $strSenderCountryCode = CountryType::ToIsoCode2( $this->OriginCountryId);
  428. $aryToReturn = array( 'ShipTimestamp' => date('c'),
  429. 'DropoffType' => $this->strDropoffType,
  430. 'ServiceType' => $this->ServiceType,
  431. 'PackagingType' => $this->strPackagingType,
  432. 'TotalWeight' => array(
  433. 'Value' => number_format($this->Weight, 1),
  434. 'Units' => $this->strWeightUnits,
  435. ),
  436. 'Shipper' => $this->createShipperDetailArray(),
  437. 'Recipient' => $this->createRecipientDetailArray(),
  438. 'ShippingChargesPayment' => array(
  439. 'PaymentType' => $this->strPayorType,
  440. 'Payor' => array('AccountNumber' => $this->strAccountNumber,
  441. 'CountryCode' => $strSenderCountryCode,
  442. ),
  443. ),
  444. 'RateRequestTypes' => array('ACCOUNT'), // valid values ACCOUNT and LIST
  445. 'PackageCount' => 1,
  446. 'RequestedPackages' => $this->createRequestedPackagesDetailArray(),
  447. );
  448. if($this->Order->IsInternational && ShippingRequestType::Label == $this->intShippingRequestType)
  449. $aryToReturn['InternationalDetail'] = $this->createInternationalDetailArray();
  450. return $aryToReturn;
  451. }
  452. protected function createInternationalDetailArray()
  453. {
  454. //start with "Contact" and "Address" populated ..
  455. $aryToReturn = $this->createShipperDetailArray();
  456. $aryToReturn['CustomsValue'] = array('Amount' => number_format($this->TotalValue, 2),
  457. 'Currency' => 'USD',
  458. );
  459. $aryToReturn['DocumentContent'] = 'DOCUMENTS_ONLY';
  460. $aryToReturn['DutiesPayment'] = array('PaymentType' => 'SENDER',
  461. 'Payor' => array('AccountNumber' => $this->strAccountNumber,
  462. 'CountryCode' => $this->DestinationCountryCode,
  463. ),
  464. );
  465. $aryCommodities = array();
  466. foreach( OrderItem::LoadArrayByOrderId( $this->Order->Id ) as $objOrderItem )
  467. {
  468. $objProduct = $objOrderItem->Product;
  469. $fltWeight = $objProduct->Weight / 16;
  470. if('KG' == $this->strWeightUnits )
  471. $fltWeight = $fltWeight / 2.2;
  472. $fltTotalAmount = $objProduct->RetailPrice * $objOrderItem->Quantity;
  473. $aryCommodities[] = array('NumberOfPieces' => $objOrderItem->Quantity,
  474. 'Description' => $objProduct->ShortDescription,
  475. 'CountryOfManufacture' => 'US',
  476. 'Weight' => array('Value' => $fltWeight,
  477. 'Units' => $this->strWeightUnits,
  478. ),
  479. 'Quantity' => $objOrderItem->Quantity,
  480. 'QuantityUnits' => 'EA',
  481. 'UnitPrice' => array('Amount' => $objProduct->RetailPrice,
  482. 'Currency' => 'USD',
  483. ),
  484. 'CustomsValue' => array('Amount' => $fltTotalAmount,
  485. 'Currency' => 'USD',
  486. ),
  487. );
  488. }
  489. if(empty($aryCommodities))
  490. throw new Exception('No order items for international Order #' . $this->Order->Id);
  491. $aryToReturn['Commodities'] = $aryCommodities;
  492. return $aryToReturn;
  493. }
  494. protected function createRequestedPackagesDetailArray()
  495. {
  496. return array('0' => array('SequenceNumber' => '1',
  497. 'Weight' => array(
  498. 'Value' => number_format($this->Weight, 1),
  499. 'Units' => $this->strWeightUnits,
  500. ),
  501. ),
  502. );
  503. }
  504. public function __get($strName)
  505. {
  506. switch ($strName)
  507. {
  508. case 'Weight':
  509. return $this->fltWeight ;
  510. case 'WeightUnits':
  511. return $this->strWeightUnits ;
  512. case 'LengthUnits':
  513. return $this->strLengthUnits ;
  514. case 'MeterNumber':
  515. return $this->strMeterNumber ;
  516. default:
  517. try {
  518. return parent::__get($strName);
  519. } catch (QCallerException $objExc) {
  520. $objExc->IncrementOffset();
  521. throw $objExc;
  522. }
  523. }
  524. }
  525. public function __set($strName, $mixValue)
  526. {
  527. switch ($strName)
  528. {
  529. case 'Weight':
  530. try {
  531. return ($this->fltWeight = QType::Cast($mixValue, QType::Float ));
  532. } catch (QInvalidCastException $objExc) {
  533. $objExc->IncrementOffset();
  534. throw $objExc;
  535. }
  536. case 'WeightUnits':
  537. try {
  538. return ($this->strWeightUnits = QType::Cast($mixValue, QType::String ));
  539. } catch (QInvalidCastException $objExc) {
  540. $objExc->IncrementOffset();
  541. throw $objExc;
  542. }
  543. case 'LengthUnits':
  544. try {
  545. return ($this->strLengthUnits = QType::Cast($mixValue, QType::String ));
  546. } catch (QInvalidCastException $objExc) {
  547. $objExc->IncrementOffset();
  548. throw $objExc;
  549. }
  550. case 'MeterNumber':
  551. try {
  552. return ($this->strMeterNumber = QType::Cast($mixValue, QType::String ));
  553. } catch (QInvalidCastException $objExc) {
  554. $objExc->IncrementOffset();
  555. throw $objExc;
  556. }
  557. default:
  558. try {
  559. return (parent::__set($strName, $mixValue));
  560. } catch (QCallerException $objExc) {
  561. $objExc->IncrementOffset();
  562. throw $objExc;
  563. }
  564. }
  565. }
  566. /*************************************************************************************/
  567. ///@todo - implement me:
  568. /**
  569. * Returns an account status report
  570. *@return string containing the status report
  571. */
  572. public function GetAccountStatus()
  573. {
  574. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  575. ShippingRequestType::ToString($this->ShippingRequestType)) );
  576. }
  577. /**
  578. * Returns whether this method is available for the order address
  579. *@return boolean true if method is available
  580. */
  581. public function GetAvailability()
  582. {
  583. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  584. ShippingRequestType::ToString($this->ShippingRequestType)) );
  585. }
  586. /**
  587. * Submits an account credit payment
  588. *@return boolean true on success
  589. */
  590. public function CreditAccount()
  591. {
  592. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  593. ShippingRequestType::ToString($this->ShippingRequestType)) );
  594. }
  595. //Request string creators
  596. /**
  597. * Creates a method available request
  598. */
  599. protected function createAvailabilityRequest()
  600. {
  601. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  602. ShippingRequestType::ToString($this->ShippingRequestType)) );
  603. }
  604. /**
  605. * Creates a label printing request
  606. */
  607. protected function createLabelRequest()
  608. {
  609. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  610. ShippingRequestType::ToString($this->ShippingRequestType)) );
  611. }
  612. /**
  613. * Creates a request submitting an account credit payment
  614. */
  615. protected function createCreditAccountRequest()
  616. {
  617. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  618. ShippingRequestType::ToString($this->ShippingRequestType)) );
  619. }
  620. /**
  621. * Creates an account status request
  622. */
  623. protected function createAccountStatusRequest()
  624. {
  625. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  626. ShippingRequestType::ToString($this->ShippingRequestType)) );
  627. }
  628. /**
  629. * Handles an account status request
  630. */
  631. protected function handleAccountStatusResponse()
  632. {
  633. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  634. ShippingRequestType::ToString($this->ShippingRequestType)) );
  635. }
  636. /**
  637. * Handles a request submitting an account credit payment
  638. */
  639. protected function handleCreditAccountResponse()
  640. {
  641. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  642. ShippingRequestType::ToString($this->ShippingRequestType)) );
  643. }
  644. /**
  645. * Handles a method available request
  646. */
  647. protected function handleAvailabilityResponse()
  648. {
  649. throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
  650. ShippingRequestType::ToString($this->ShippingRequestType)) );
  651. }
  652. /*************************************************************************************/
  653. /*********************** Old XML POST version functions - DEPRECATED **********************************
  654. These are left as examples and in case some would prefer to use them (as PHP SOAP is a bit buggy ..)
  655. public function GetRate()
  656. {
  657. $this->createRequest(ShippingRequestType::Rate, WebRequestType::POST);
  658. $this->submitRequest();
  659. return $this->Rate;
  660. }*/
  661. /**
  662. * Creates the POST string for the rate request - DEPRECATED
  663. */
  664. protected function createRateRequest()
  665. {
  666. $this->strRequest = '';
  667. $blnAddOriginStateAndZip = ( CountryType::UnitedStates == $this->OriginCountryId
  668. ||CountryType::Canada == $this->OriginCountryId );
  669. $blnAddDestinationStateAndZip = ( CountryType::UnitedStates == $this->DestinationCountryId
  670. ||CountryType::Canada == $this->DestinationCountryId );
  671. $str = $this->createXMLOpenTags('FDXRateRequest');
  672. $str .= '<RequestHeader>';
  673. // $str .= '<CustomerTransactionIdentifier>Express Rate</CustomerTransactionIdentifier>';
  674. $str .= '<AccountNumber>' . $this->strAccountNumber . '</AccountNumber>';
  675. $str .= '<MeterNumber>' . $this->strMeterNumber . '</MeterNumber>';
  676. $str .= '<CarrierCode>' . $this->Carrier . '</CarrierCode>';
  677. $str .= '</RequestHeader>';
  678. $str .= '<DropoffType>' . $this->strDropoffType . '</DropoffType>';
  679. $str .= '<Service>' . $this->ServiceType . '</Service>';
  680. $str .= '<Packaging>'.$this->strPackagingType . '</Packaging>';
  681. $str .= '<WeightUnits>'. $this->strWeightUnits . '</WeightUnits>';
  682. $str .= '<Weight>' . number_format($this->Weight, 1) . '</Weight>';
  683. $str .= '<OriginAddress>';
  684. if($blnAddOriginStateAndZip)
  685. {
  686. $str .= '<StateOrProvinceCode>' . $this->OriginStateCode . '</StateOrProvinceCode>';
  687. $str .= '<PostalCode>' . $this->OriginZip.'</PostalCode>';
  688. }
  689. $str .= '<CountryCode>' . $this->OriginCountryCode . '</CountryCode>';
  690. $str .= '</OriginAddress>';
  691. $str .= '<DestinationAddress>';
  692. if($blnAddDestinationStateAndZip)
  693. {
  694. $str .= '<StateOrProvinceCode>' . $this->DestinationStateCode . '</StateOrProvinceCode>';
  695. $str .= '<PostalCode>' . $this->DestinationZip.'</PostalCode>';
  696. }
  697. $str .= '<CountryCode>' . $this->DestinationCountryCode . '</CountryCode>';
  698. $str .= '</DestinationAddress>';
  699. $str .= '<Payment>';
  700. $str .= '<PayorType>' . $this->strPayorType . '</PayorType>';
  701. $str .= '</Payment>';
  702. $str .= '<PackageCount>1</PackageCount>';
  703. $str .= '</FDXRateRequest>';
  704. $this->strRequest = $str;
  705. }
  706. /**
  707. * Parses the rate request response from FEDEX server - XML POST VERSION: DEPRECATED.
  708. * @todo - handle errors more elegantly, make more robust ..
  709. */
  710. /* protected function handleRateResponse()
  711. {
  712. $objDomDoc = $this->getDomDocument('FDXRateReply');
  713. if($objDomDoc)
  714. {
  715. $strErrorMessage = $this->requestErrors($objDomDoc);
  716. if($strErrorMessage)
  717. {
  718. $this->blnHasErrors = true;
  719. $this->strErrors = $strErrorMessage;
  720. $this->Rate = 0;
  721. } else {
  722. $nodeList = $objDomDoc->getElementsByTagName('NetCharge');
  723. if($nodeList->length > 0)
  724. $this->Rate = $nodeList->item(0)->nodeValue;
  725. else
  726. $this->Rate = 0;
  727. }
  728. } else {
  729. $this->HasErrors = true;
  730. $this->Errors = 'Unknown FEDEX error ..Request:' . $this->strRequest . ' Response:' . $this->strResponse;
  731. $this->Rate = 0;
  732. }
  733. //debugging:
  734. // if(!$this->Rate)
  735. // {
  736. // $this->Errors = 'Unknown FEDEX error ..Request:' . $this->strRequest . ' Response:' . $this->strResponse;
  737. // die($this->Errors);
  738. // }
  739. }*/
  740. /**
  741. * Utility function to check for request errors - returns either a string containing
  742. * server error messages or false if there were none.
  743. *@param DOMDocument objDomDoc - the server response ..
  744. *@return string | boolean error messages or false if request succeeded.
  745. */
  746. private function requestErrors($objDomDoc)
  747. {
  748. $mixToReturn = false;
  749. $nodeListErrors = $objDomDoc->getElementsByTagName('Error');
  750. if( $nodeListErrors->length > 0 )
  751. {
  752. $this->blnHasErrors = true;
  753. $mixToReturn = 'Request: ' . $this->strRequest;
  754. $nodeListErrorMessages = $objDomDoc->getElementsByTagName('Message');
  755. if( $nodeListErrorMessages->length)
  756. $mixToReturn .= ' Message: ' . $nodeListErrorMessages->item(0)->nodeValue;
  757. }
  758. return $mixToReturn;
  759. }
  760. private function createXMLOpenTags($strApi)
  761. {
  762. $strToReturn = '<?xml version="1.0" encoding="UTF-8" ?>';
  763. $strToReturn .= sprintf('<%s xmlns:api="http://www.fedex.com/fsmapi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="%s.xsd">',
  764. $strApi, $strApi);
  765. return $strToReturn;
  766. }
  767. }//end class
  768. }//end define
  769. ?>