<?php
if(!defined('QUINTACMS') ) die('No Quinta.');

if (!defined("FEDEXREQUEST.CLASS.PHP")){
define("FEDEXREQUEST.CLASS.PHP",1);

/**
* Class FedexRequest - provides services for Fedex Direct Connect
* This class implements the FEDEX SOAP API for web service requests. 
*
*     NOTE: ServiceType as configured in the database must be one of the following:
*    - EUROPE_FIRST_INTERNATIONAL_PRIORITY
*    - FEDEX_1_DAY_FREIGHT
*    - FEDEX_2_DAY
*    - FEDEX_2_DAY_FREIGHT
*    - FEDEX_3_DAY_FREIGHT
*    - FEDEX_EXPRESS_SAVER
*    - FEDEX_GROUND
*    - FIRST_OVERNIGHT
*    - GROUND_HOME_DELIVERY
*    - INTERNATIONAL_ECONOMY
*    - INTERNATIONAL_ECONOMY_FREIGHT
*    - INTERNATIONAL_FIRST
*    - INTERNATIONAL_PRIORITY
*    - INTERNATIONAL_PRIORITY_FREIGHT
*    - PRIORITY_OVERNIGHT
*    - STANDARD_OVERNIGHT
*
*@todo - document this class ..
* 
*@author Erik Winn <sidewalksoftware@gmail.com>
*
*@version 0.3
*
*@package Quinta
* @subpackage Classes
*/

 class FedexRequest extends ShippingRequest
 {
		/**
		*     NOTE: LabelStockType must be one of the following:
		*    - PAPER_4X6
		*    - PAPER_4X8
		*    - PAPER_4X9
		*    - PAPER_7X4.75
		*    - PAPER_8.5X11_BOTTOM_HALF_LABEL
		*    - PAPER_8.5X11_TOP_HALF_LABEL
		*    - STOCK_4X6
		*    - STOCK_4X6.75_LEADING_DOC_TAB
		*    - STOCK_4X6.75_TRAILING_DOC_TAB
		*    - STOCK_4X8
		*    - STOCK_4X9_LEADING_DOC_TAB
		*    - STOCK_4X9_TRAILING_DOC_TAB
		* @var string
		*/
		protected $strLabelStockType = 'PAPER_4X6';
		/**
		*     NOTE: LabelFormatType must be one of the following:
		*     COMMON2D
		*     LABEL_DATA_ONLY
		*@var string paper format for label
		*/
		protected $strLabelFormatType = 'COMMON2D';
		/**
		*     NOTE: ImageType must be one of the following:
		*     DPL
		*     EPL2
		*     PDF
		*     PNG
		*     ZPLII
		*@var string image format for label
		*/
		protected $strImageType = 'PNG';
		/**
		*@var string Account number provided by Fedex
		*/
		protected $strAccountNumber;
		/**
		*@var string Meter number provided by Fedex
		*/
		protected $strMeterNumber;
		/**
		*@var string strPayorType - who pays for the shipping
		*/
		protected $strPayorType = 'SENDER';
		/**
		*     NOTE: DropoffType must be one of the following:
		*    - BUSINESS_SERVICE_CENTER
		*    - DROP_BOX
		*    - REGULAR_PICKUP
		*    - REQUEST_COURIER
		*    - STATION
		*@var string Drop off method
		*/
		protected $strDropoffType = 'REGULAR_PICKUP';
		/**
		*     NOTE: PackagingType must be one of the following:
		*    - FEDEX_10KG_BOX
		*    - FEDEX_25KG_BOX
		*    - FEDEX_BOX
		*    - FEDEX_ENVELOPE
		*    - FEDEX_PAK
		*    - FEDEX_TUBE
		*    - YOUR_PACKAGING
		*@var string Packaging type
		*/
		protected $strPackagingType = 'YOUR_PACKAGING';
		/**
		*@var string Indicates units of weight (LB | KG)
		*/
		protected $strWeightUnits = 'LB';
		/**
		*@var string Indicates units of length (IN | CM)
		*/
		protected $strLengthUnits = 'IN';
		/**
		*  FedEx combines units (eg. pounds and ounces ) into one figure ..
		*@var float fltWeight - weight of package in designated units ..
		*/
		protected $fltWeight;

		/**
		* FedexRequest Constructor
		*
		* @param ShippingMethod objShippingMethod - the method for which to obtain estimate
		*/
		public function __construct(ShippingMethod $objShippingMethod){
			parent::__construct($objShippingMethod);

			//now unused ..
			$this->strRemoteCgiUrl = '/GatewayDC';
			
			if($objShippingMethod->TestMode){///@todo  defined in config - fixme!
				$this->strRemoteDomainName = 'gatewaybeta.fedex.com';
				$this->strRemoteAccountId = FEDEX_TESTKEY;
				$this->strRemotePassword = FEDEX_TESTPASSWORD;
				$this->strAccountNumber = FEDEX_TESTACCOUNT_NUMBER;
				$this->strMeterNumber = FEDEX_TESTMETER_NUMBER;
				$this->strRemoteDomainName = 'gatewaybeta.fedex.com'; //unused ..
			}else{
				$this->strRemoteDomainName = 'gateway.fedex.com';
				$this->strRemoteAccountId = FEDEX_KEY;
				$this->strRemotePassword = FEDEX_PASSWORD;
				$this->strAccountNumber = FEDEX_ACCOUNT_NUMBER;
				$this->strMeterNumber = FEDEX_METER_NUMBER;
				$this->strRemoteDomainName = 'gateway.fedex.com'; //unused ..
			}
			
			$this->fltWeight = $objShippingMethod->Pounds;
			$this->fltWeight += $objShippingMethod->Ounces / 16;
		}
		//Public interface ..
		/**
		* Returns a shipping rate for the order for this method
		*@return image object containing the image code
		*/
		public function GetRate(){
			$this->intShippingRequestType = ShippingRequestType::Rate;
			//Example code does this - not sure why or if it is needed ..
			ini_set("soap.wsdl_cache_enabled", "0");
			if($this->blnTestMode)
				$this->strWsdlUri = __QUINTA_CORE__ . '/utilities/FDXRateService_v5_test.wsdl';
			else
				$this->strWsdlUri = __QUINTA_CORE__ . '/utilities/FDXRateService_v5.wsdl';
			$this->strSoapFunction = 'getRates';
			$this->createSoapRateRequest();

			try{
				$this->submitSoapRequest();
			} catch (SoapFault $objFault) {
 //               exit(var_dump($objFault));
			   throw new Exception($objFault->faultstring);
			}
			$this->handleRateResponse();
			return $this->Rate;
		}
	   /**
		* Utility function to format the mult-dimensional array of RateRequest (labels) request data
		* to be passed on the the Soap client function.
		*@return array - suitable for passing to the Soap function for RateRequest (labels) requests
		*/
		protected function createSoapRateRequest(){                        
			$arySoapParamsToReturn['WebAuthenticationDetail'] = $this->createWebAuthDetailArray();
			$arySoapParamsToReturn['ClientDetail'] = $this->createClientDetailArray();
			$arySoapParamsToReturn['TransactionDetail'] = $this->createTransactionDetailArray();
			$arySoapParamsToReturn['Version'] = $this->createVersionDetailArray('crs');
			
			$aryRequestedShipment = $this->createRequestedShipmentDetailArray();
			$aryRequestedShipment['PackageDetail'] = 'INDIVIDUAL_PACKAGES';
			
			$arySoapParamsToReturn['RequestedShipment'] = $aryRequestedShipment;
			$this->arySoapRequest = $arySoapParamsToReturn;
		}
		/**
		* Parses the rate request SOAP response from FEDEX server
		* @todo - handle errors more elegantly, make more robust ..
		*/
		protected function handleRateResponse(){
			$this->Rate = 0;
			//first check for Fedex errors
			if('FAILURE' == $this->mixSoapResponse->HighestSeverity
				|| 'ERROR' == $this->mixSoapResponse->HighestSeverity )
			{
				$this->blnIsAvailable = false;
				$this->blnHasErrors = true;
				$mixNotifications = $this->mixSoapResponse->Notifications;   
				if(is_array($mixNotifications))
					foreach($mixNotifications as $objNotification)
						$this->strErrors .=  $objNotification->Severity . ':' . $objNotification->Message . "\n";
				else
					$this->strErrors .= $mixNotifications->Severity . ' : ' .$mixNotifications->Message . "\n";
									
				//Service is not allowed to destination - eg. fedex_2_day to Australia .. not really
				// an error, just not available so reset HasErrors:
				if( false !== stripos( $this->strErrors, 'Service is not allowed' ) )
				   $this->blnHasErrors = false;
										 
			} else {
				$this->blnIsAvailable = true;
				$mixDetails = $this->mixSoapResponse->RateReplyDetails->RatedShipmentDetails;
				if(is_array($mixDetails))
					$this->Rate = $mixDetails[0]->ShipmentRateDetail->TotalNetCharge->Amount;
				else
					$this->Rate = $mixDetails->ShipmentRateDetail->TotalNetCharge->Amount;
			}
			
			if($this->blnIsAvailable && !$this->Rate){
				$this->blnIsAvailable = false;
				$this->HasErrors = true;
				$this->strErrors = 'Unknown Fedex Error.';
			}

			return $this->Rate;
		}
		/**
		* Returns a shipping label for this method to the order address
		* Note: this function uses the SOAP interface provided from Fedex - partially due to time constraints
		* and partially to remind me to reimplement these service request classes to use SOAP; all of the
		* web services seem to be providing WSDLs now and it is builtin to PHP so it makes sense. Later.
		* This quick example implements the "new" style Fedex API.
		*@return string containing label image or null on failure ..
		*/
		public function GetLabel(){
			$this->intShippingRequestType = ShippingRequestType::Label;
			//Example code does this - not sure why or if it is needed ..
			ini_set("soap.wsdl_cache_enabled", "0");
			if($this->blnTestMode)
				$this->strWsdlUri = __QUINTA_CORE__ . '/utilities/FDXShipService_v5_test.wsdl';
			else
				$this->strWsdlUri = __QUINTA_CORE__ . '/utilities/FDXShipService_v5.wsdl';
			$this->strSoapFunction = 'processShipment';
			$this->createSoapShipRequest();

			try{
				$this->submitSoapRequest();
			} catch (SoapFault $objFault) {
//                exit(var_dump($objFault));
				throw new Exception($objFault->faultstring . ' - '
												  . $objFault->detail->fault->details->ValidationFailureDetail->message);
			}
			return $this->handleLabelResponse();
		}
		/**
		* Handles a label response returning the image. On failure the image is null and errors are
		* stored in strErrors. The image is the string containing the formatted image from Fedex - this
		* can be written directly to a file as eg. image.png or image.pdf ..
		* @return string containing the image code returned from Fedex
		*/
		protected function handleLabelResponse(){            
			$this->objShippingLabelImage = null;            
			//first check for Fedex errors
			if('FAILURE' == $this->mixSoapResponse->HighestSeverity
				|| 'ERROR' == $this->mixSoapResponse->HighestSeverity )
			{
				$this->blnHasErrors = true;
				//Note: this part is unclear - Notifications can be an array or object?? May not work correctly ..
				if(is_array($this->mixSoapResponse->Notifications))
					foreach($this->mixSoapResponse->Notifications as $objNotification)
						$this->strErrors .= $objNotification->Severity . ':' . $objNotification->Message . "\n";
				else
					$this->strErrors .= $this->mixSoapResponse->Notifications->Severity
													. ': ' . $this->mixSoapResponse->Notifications->Message . "\n";
			} else {
			   $objPackageDetails = $this->mixSoapResponse->CompletedShipmentDetail->CompletedPackageDetails;
			   $this->objShippingLabelImage = $objPackageDetails->Label->Parts->Image;
			   $strFinalPrice = $this->mixSoapResponse->CompletedShipmentDetail->ShipmentRating;
				if(!empty($strFinalPrice))
				{
					$this->objShippingMethod->Order->ShippingCost = $strFinalPrice;
					$this->objShippingMethod->Order->Save(false,true);
				}
			   
				if($this->Order->IsInternational){
					if(property_exists($objPackageDetails, 'PackageDocuments')){
						$aryPackageDocuments = $objPackageDetails->PackageDocuments;
						foreach($aryPackageDocuments as $objDocument){
							$this->aryExtraDocumentImages[] = new ExtraDocumentImage(
																									$objDocument->Parts->Image,
																									$objDocument->Type,
																									$objDocument->CopiesToPrint
																									  );
						}
					}
				}
				$strTrackingNumber = $objPackageDetails->TrackingId->TrackingNumber;
				if(!empty($strTrackingNumber)){
					if(! TrackingNumber::LoadByOrderIdNumber($this->objShippingMethod->OrderId, $strTrackingNumber )){
						$objTrackingNumber = new TrackingNumber();
						$objTrackingNumber->OrderId = $this->objShippingMethod->OrderId;
						$objTrackingNumber->Number = $strTrackingNumber;
						$objTrackingNumber->Save();
					}
				}
			}
			return $this->objShippingLabelImage;
		}
	   /**
		* Utility function to format the mult-dimensional array of ShipRequest (labels) request data
		* to be passed on the the Soap client function.
		*@return array - suitable for passing to the Soap function for ShipRequest (labels) requests
		*/
		protected function createSoapShipRequest(){                        
			$arySoapParamsToReturn['WebAuthenticationDetail'] = $this->createWebAuthDetailArray();
			$arySoapParamsToReturn['ClientDetail'] = $this->createClientDetailArray();
			$arySoapParamsToReturn['TransactionDetail'] = $this->createTransactionDetailArray();
			$arySoapParamsToReturn['Version'] = $this->createVersionDetailArray('ship');
			
			$aryRequestedShipment = $this->createRequestedShipmentDetailArray();
			$aryRequestedShipment['LabelSpecification'] = array('LabelFormatType' => $this->strLabelFormatType,
																								'ImageType' => $this->strImageType,
																								'LabelStockType' => $this->strLabelStockType,
																							   );
			$arySoapParamsToReturn['RequestedShipment'] = $aryRequestedShipment;
			$this->arySoapRequest = $arySoapParamsToReturn;
		}
		protected function createWebAuthDetailArray(){
			return array('UserCredential' => array('Key' => $this->strRemoteAccountId,
																		'Password' => $this->strRemotePassword
																		)
								);
		}
		protected function createClientDetailArray(){
			return array('AccountNumber' => $this->strAccountNumber,
								'MeterNumber' => $this->strMeterNumber
								);
		}
		protected function createTransactionDetailArray(){
			return array('CustomerTransactionId' => STORE_NAME . ' Order ' . $this->Order->Id );
		}
		protected function createVersionDetailArray($strService){
			return  array('ServiceId' => $strService, 'Major' => '5', 'Intermediate' => '0', 'Minor' => '0');
		}
		protected function createShipperDetailArray(){
			$strSenderCountryCode = CountryType::ToIsoCode2( $this->OriginCountryId);
			$arySenderStreets = array();
			if(STORE_ADDRESS1)
				$arySenderStreets[] = STORE_ADDRESS1;
			if(STORE_ADDRESS2)
				$arySenderStreets[] = STORE_ADDRESS2;
			if(! count($arySenderStreets))
				throw new Exception('FedexRequest: Shipper address must have at least one street line!');
			return array('Contact' => array('PersonName' => STORE_OWNER,
															'CompanyName' => STORE_NAME,
															'PhoneNumber' => STORE_PHONE,
															),
								'Address' => array('StreetLines' =>$arySenderStreets,
															'City' => STORE_CITY,
															'StateOrProvinceCode' => STORE_STATE,
															'PostalCode' => STORE_POSTAL_CODE,
															'CountryCode' => $strSenderCountryCode,
															),
								);
		}
		protected function createRecipientDetailArray(){
			$strRecipientName = $this->Order->FullShippingName;
			$strRecipientCompany  = $this->Order->ShippingCompany;
			$strRecipientCity  = $this->Order->ShippingCity;
			$strRecipientPhone  = $this->Order->Account->Person->PhoneNumber;
			$aryRecipientStreets = array();
			if($this->Order->ShippingStreet1)
				$aryRecipientStreets[] = $this->Order->ShippingStreet1;
			if($this->Order->ShippingStreet2)
				$aryRecipientStreets[] = $this->Order->ShippingStreet2;
			if(! count($aryRecipientStreets))
				throw new Exception('FedexRequest: Recipient address must have at least one street line!');
			$aryToReturn = array('Contact' => array('PersonName' => $strRecipientName,
																			'CompanyName' => $strRecipientCompany,
																			'PhoneNumber' => $strRecipientPhone,
																			),
												'Address' => array('StreetLines' => $aryRecipientStreets,
																			'City' => $strRecipientCity,
																			//Fedex barfs if the state or province is over 2 chars ..
																			'StateOrProvinceCode' => substr($this->DestinationStateCode, 0, 2),
																			'PostalCode' => $this->DestinationZip,
																			'CountryCode' => $this->DestinationCountryCode,
																			),
												);
			return $aryToReturn;
		}
		
		protected function createRequestedShipmentDetailArray(){
			$strSenderCountryCode = CountryType::ToIsoCode2( $this->OriginCountryId);
			$aryToReturn = array( 'ShipTimestamp' => date('c'),
											'DropoffType' => $this->strDropoffType,
											'ServiceType' => $this->ServiceType,
											'PackagingType' => $this->strPackagingType,
											'TotalWeight' => array(
																			'Value' => number_format($this->Weight, 1),
																			'Units' => $this->strWeightUnits,
																			),
											'Shipper' => $this->createShipperDetailArray(),
											'Recipient' => $this->createRecipientDetailArray(),
											'ShippingChargesPayment' => array(
																					'PaymentType' => $this->strPayorType,
																					'Payor' => array('AccountNumber' => $this->strAccountNumber,
																											  'CountryCode' => $strSenderCountryCode,
																											 ),
																									),
											'RateRequestTypes' => array('ACCOUNT'), // valid values ACCOUNT and LIST
											'PackageCount' => 1,
											'RequestedPackages' => $this->createRequestedPackagesDetailArray(),
												);
												
			if($this->Order->IsInternational && ShippingRequestType::Label == $this->intShippingRequestType)
				$aryToReturn['InternationalDetail'] = $this->createInternationalDetailArray();
				
			return $aryToReturn;
		}
		
		protected function createInternationalDetailArray(){
			//start with "Contact" and "Address" populated ..
			$aryToReturn = $this->createShipperDetailArray();
			$aryToReturn['CustomsValue'] = array('Amount' => number_format($this->TotalValue, 2),
																		 'Currency' => 'USD',
																		 );
			$aryToReturn['DocumentContent'] = 'DOCUMENTS_ONLY';
			$aryToReturn['DutiesPayment'] = array('PaymentType' => 'SENDER',
																		  'Payor' => array('AccountNumber' => $this->strAccountNumber,
																									'CountryCode' => $this->DestinationCountryCode,
																								   ),
																		  );
			$aryCommodities = array();
			foreach( OrderItem::LoadArrayByOrderId( $this->Order->Id ) as $objOrderItem ){
				$objProduct = $objOrderItem->Product;
				$fltWeight = $objProduct->Weight / 16;
				if('KG' == $this->strWeightUnits )
					$fltWeight = $fltWeight / 2.2;
				$fltTotalAmount = $objProduct->RetailPrice * $objOrderItem->Quantity;
				$aryCommodities[] = array('NumberOfPieces' => $objOrderItem->Quantity,
														'Description' => $objProduct->ShortDescription,
														'CountryOfManufacture' => 'US',
														'Weight' => array('Value' => $fltWeight,
																				   'Units' => $this->strWeightUnits,
																				   ),
														'Quantity' => $objOrderItem->Quantity,
														'QuantityUnits' => 'EA',
														'UnitPrice' => array('Amount' => $objProduct->RetailPrice,
																					   'Currency' => 'USD',
																					  ),
														'CustomsValue' => array('Amount' => $fltTotalAmount,
																								'Currency' => 'USD',
																							   ),
														 );
			}
			if(empty($aryCommodities))
				throw new Exception('No order items for international Order #' . $this->Order->Id);
			$aryToReturn['Commodities'] = $aryCommodities;
			return $aryToReturn;
		}
		
	   protected function createRequestedPackagesDetailArray(){
			return array('0' => array('SequenceNumber' => '1',
													  'Weight' => array(
																				'Value' => number_format($this->Weight, 1),
																				'Units' => $this->strWeightUnits,
																				 ),
													),
								 );
	   }
		
		public function __get($strName){
			switch ($strName){
				case 'Weight':
					return $this->fltWeight ;
				case 'WeightUnits':
					return $this->strWeightUnits ;
				case 'LengthUnits':
					return $this->strLengthUnits ;
				case 'MeterNumber':
					return $this->strMeterNumber ;
			   default:
					try {
						return parent::__get($strName);
					} catch (QCallerException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
			}
		}
		public function __set($strName, $mixValue){
			switch ($strName){
				case 'Weight':
					try {
						return ($this->fltWeight = QType::Cast($mixValue, QType::Float ));
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case 'WeightUnits':
					try {
						return ($this->strWeightUnits = QType::Cast($mixValue, QType::String ));
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case 'LengthUnits':
					try {
						return ($this->strLengthUnits = QType::Cast($mixValue, QType::String ));
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
				case 'MeterNumber':
					try {
						return ($this->strMeterNumber = QType::Cast($mixValue, QType::String ));
					} catch (QInvalidCastException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}

				default:
					try {
						return (parent::__set($strName, $mixValue));
					} catch (QCallerException $objExc) {
						$objExc->IncrementOffset();
						throw $objExc;
					}
			}
		}

/*************************************************************************************/
///@todo - implement me:        
		/**
		* Returns an account status report
		*@return string containing the status report
		*/
		public function GetAccountStatus()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}
		/**
		* Returns whether this method is available for the order address
		*@return boolean true if method is available
		*/
		public function GetAvailability()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}
		/**
		* Submits an account credit payment
		*@return boolean true on success
		*/
		public function CreditAccount()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}
		
		//Request string creators
		/**
		* Creates a method available request
		*/
		protected function createAvailabilityRequest()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}
		/**
		* Creates a label printing request
		*/
		protected function createLabelRequest()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}
		/**
		* Creates a request submitting an account credit payment
		*/
		protected function createCreditAccountRequest()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}
		/**
		* Creates an account status request
		*/
		protected function createAccountStatusRequest()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}
		
		/**
		* Handles an account status request
		*/
		protected function handleAccountStatusResponse()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}
		/**
		* Handles a request submitting an account credit payment
		*/
		protected function handleCreditAccountResponse()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}
		/**
		* Handles a method available request
		*/
		protected function handleAvailabilityResponse()
		{
			throw new QCallerException(sprintf('FEDEXRequest: Shipping request type %s unsupported! ',
																		ShippingRequestType::ToString($this->ShippingRequestType)) );
		}

/*************************************************************************************/

/***********************  Old XML POST version functions - DEPRECATED **********************************
 These are left as examples and in case some would prefer to use them (as PHP SOAP is a bit buggy ..)
		public function GetRate()
		{
			$this->createRequest(ShippingRequestType::Rate, WebRequestType::POST);
			$this->submitRequest();
			return $this->Rate;
		}*/
		
		/**
		* Creates the POST string for the rate request - DEPRECATED
		*/
		protected function createRateRequest()
		{
			$this->strRequest = '';
			$blnAddOriginStateAndZip = ( CountryType::UnitedStates == $this->OriginCountryId
														||CountryType::Canada == $this->OriginCountryId );
			$blnAddDestinationStateAndZip = ( CountryType::UnitedStates == $this->DestinationCountryId
														||CountryType::Canada == $this->DestinationCountryId );

			$str = $this->createXMLOpenTags('FDXRateRequest');
			$str .= '<RequestHeader>';
//            $str .= '<CustomerTransactionIdentifier>Express Rate</CustomerTransactionIdentifier>';
			$str .= '<AccountNumber>' . $this->strAccountNumber . '</AccountNumber>';
			$str .= '<MeterNumber>' . $this->strMeterNumber . '</MeterNumber>';
			$str .= '<CarrierCode>' . $this->Carrier . '</CarrierCode>';
			$str .= '</RequestHeader>';
			
			$str .= '<DropoffType>' . $this->strDropoffType . '</DropoffType>';
			$str .= '<Service>' . $this->ServiceType . '</Service>';
			$str .= '<Packaging>'.$this->strPackagingType . '</Packaging>';
			$str .= '<WeightUnits>'. $this->strWeightUnits . '</WeightUnits>';
			$str .= '<Weight>' . number_format($this->Weight, 1) . '</Weight>';
			$str .= '<OriginAddress>';
			if($blnAddOriginStateAndZip)
			{
				$str .= '<StateOrProvinceCode>' . $this->OriginStateCode . '</StateOrProvinceCode>';
				$str .= '<PostalCode>' . $this->OriginZip.'</PostalCode>';
			}
			$str .= '<CountryCode>' . $this->OriginCountryCode . '</CountryCode>';
			$str .= '</OriginAddress>';
			$str .= '<DestinationAddress>';
			if($blnAddDestinationStateAndZip)
			{
				$str .= '<StateOrProvinceCode>' . $this->DestinationStateCode . '</StateOrProvinceCode>';
				$str .= '<PostalCode>' . $this->DestinationZip.'</PostalCode>';
			}
			$str .= '<CountryCode>' . $this->DestinationCountryCode . '</CountryCode>';
			$str .= '</DestinationAddress>';
			$str .= '<Payment>';
			$str .= '<PayorType>' . $this->strPayorType . '</PayorType>';
			$str .= '</Payment>';
			$str .= '<PackageCount>1</PackageCount>';
			$str .= '</FDXRateRequest>';
			
			$this->strRequest = $str;
		}
		/**
		* Parses the rate request response from FEDEX server - XML POST VERSION: DEPRECATED.
		* @todo - handle errors more elegantly, make more robust ..
		*/
/*        protected function handleRateResponse()
		{
			$objDomDoc = $this->getDomDocument('FDXRateReply');
			if($objDomDoc)
			{
				$strErrorMessage = $this->requestErrors($objDomDoc);
				if($strErrorMessage)
				{
					$this->blnHasErrors = true;
					$this->strErrors =  $strErrorMessage;
					$this->Rate = 0;
				} else {
					$nodeList = $objDomDoc->getElementsByTagName('NetCharge');                    
					if($nodeList->length > 0)
						$this->Rate = $nodeList->item(0)->nodeValue;
					else
						$this->Rate = 0;
				}
			 } else {
				$this->HasErrors = true;
				$this->Errors = 'Unknown FEDEX error ..Request:' . $this->strRequest . ' Response:'  . $this->strResponse;
				$this->Rate = 0;
			}
 //debugging:
//             if(!$this->Rate)
//             {
//                 $this->Errors = 'Unknown FEDEX error ..Request:' . $this->strRequest . ' Response:'  . $this->strResponse;
//                 die($this->Errors);
//             }

		}*/
	   /**
		* Utility function to check for request errors - returns either a string containing
		* server error messages or false if there were none.
		*@param DOMDocument objDomDoc - the server response ..
		*@return string | boolean error messages or false if request succeeded.
		*/
		private function requestErrors($objDomDoc)
		{
			$mixToReturn = false;
			$nodeListErrors = $objDomDoc->getElementsByTagName('Error');
			if( $nodeListErrors->length > 0 )
			{
				$this->blnHasErrors = true;
				$mixToReturn =  'Request: ' . $this->strRequest;
				$nodeListErrorMessages = $objDomDoc->getElementsByTagName('Message');
				if( $nodeListErrorMessages->length)
					$mixToReturn .= ' Message: ' . $nodeListErrorMessages->item(0)->nodeValue;
			}
			return $mixToReturn;
		}
		private function createXMLOpenTags($strApi)
		{
			$strToReturn = '<?xml version="1.0" encoding="UTF-8" ?>';
			$strToReturn .= sprintf('<%s xmlns:api="http://www.fedex.com/fsmapi" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="%s.xsd">',
									$strApi, $strApi);
			return $strToReturn;
		}
  
  }//end class
}//end define

?>