diff --git a/app/code/Magento/Customer/Controller/Section/Load.php b/app/code/Magento/Customer/Controller/Section/Load.php
index 19b361b2c830d..f299d3a438178 100644
--- a/app/code/Magento/Customer/Controller/Section/Load.php
+++ b/app/code/Magento/Customer/Controller/Section/Load.php
@@ -8,10 +8,13 @@
use Magento\Customer\CustomerData\SectionPoolInterface;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\Result\JsonFactory;
-use Magento\Framework\Exception\LocalizedException;
+use Magento\Customer\CustomerData\Section\Identifier;
+use Magento\Framework\Escaper;
/**
* Customer section controller
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Load extends \Magento\Framework\App\Action\Action
{
@@ -30,16 +33,21 @@ class Load extends \Magento\Framework\App\Action\Action
*/
protected $sectionPool;
+ /**
+ * @var \Magento\Framework\Escaper
+ */
+ private $escaper;
+
/**
* @param Context $context
* @param JsonFactory $resultJsonFactory
- * @param \Magento\Customer\CustomerData\Section\Identifier $sectionIdentifier
+ * @param Identifier $sectionIdentifier
* @param SectionPoolInterface $sectionPool
*/
public function __construct(
Context $context,
JsonFactory $resultJsonFactory,
- \Magento\Customer\CustomerData\Section\Identifier $sectionIdentifier,
+ Identifier $sectionIdentifier,
SectionPoolInterface $sectionPool
) {
parent::__construct($context);
@@ -48,6 +56,19 @@ public function __construct(
$this->sectionPool = $sectionPool;
}
+ /**
+ * Get new Escaper dependency for application code.
+ * @return \Magento\Framework\Escaper
+ * @deprecated
+ */
+ private function getEscaper()
+ {
+ if ($this->escaper === null) {
+ $this->escaper = \Magento\Framework\App\ObjectManager::getInstance()->get(Escaper::class);
+ }
+ return $this->escaper;
+ }
+
/**
* @return \Magento\Framework\Controller\Result\Json|\Magento\Framework\Controller\Result\Redirect
*/
@@ -70,7 +91,7 @@ public function execute()
\Zend\Http\AbstractMessage::VERSION_11,
'Bad Request'
);
- $response = ['message' => $e->getMessage()];
+ $response = ['message' => $this->getEscaper()->escapeHtml($e->getMessage())];
}
return $resultJson->setData($response);
diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php
new file mode 100644
index 0000000000000..474925f6561a4
--- /dev/null
+++ b/app/code/Magento/Customer/Test/Unit/Controller/Section/LoadTest.php
@@ -0,0 +1,152 @@
+objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
+
+ $this->resultJsonFactory = $this->getMockBuilder(\Magento\Framework\Controller\Result\JsonFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+
+ $this->sectionIdentifier = $this->getMockBuilder(\Magento\Customer\CustomerData\Section\Identifier::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->sectionPool = $this->getMockBuilder(\Magento\Customer\CustomerData\SectionPoolInterface::class)
+ ->getMockForAbstractClass();
+
+ $this->request = $this->getMockBuilder(\Magento\Framework\App\Request\Http::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->controller = $this->objectManager->getObject(
+ 'Magento\Customer\Controller\Section\Load',
+ [
+ 'resultJsonFactory' => $this->resultJsonFactory,
+ 'sectionIdentifier' => $this->sectionIdentifier,
+ 'sectionPool' => $this->sectionPool,
+ 'request' => $this->request
+ ]
+ );
+ }
+
+ public function testExecuteBadSectionData()
+ {
+ $resultJson = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->resultJsonFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($resultJson);
+
+ $this->request->expects($this->at(0))
+ ->method('getParam')
+ ->with('sections')
+ ->willReturn('badSectionData');
+
+ $this->request->expects($this->at(1))
+ ->method('getParam')
+ ->with('update_section_id')
+ ->willReturn(false);
+
+ $message = '"badSectionData" section source is not supported';
+ $expected = ['message' => $message];
+ $this->sectionPool->expects($this->once())
+ ->method('getSectionsData')
+ ->will($this->throwException(new \Exception));
+
+ $escaper = $this->getMockBuilder(\Magento\Framework\Escaper::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $escaper->expects($this->once())
+ ->method('escapeHtml')
+ ->willReturn($message);
+
+ $reflection = new \ReflectionClass(get_class($this->controller));
+ $reflectionProperty = $reflection->getProperty('escaper');
+ $reflectionProperty->setAccessible(true);
+ $reflectionProperty->setValue($this->controller, $escaper);
+
+ $resultJson->expects($this->once())
+ ->method('setData')
+ ->with($expected)
+ ->willReturn(json_encode($expected));
+ $this->assertSame(json_encode($expected), $this->controller->execute());
+ }
+
+ public function testExecuteSuccess()
+ {
+ $resultJson = $this->getMockBuilder(\Magento\Framework\Controller\Result\Json::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->resultJsonFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($resultJson);
+
+ $this->request->expects($this->at(0))
+ ->method('getParam')
+ ->with('sections')
+ ->willReturn('cart');
+
+ $this->request->expects($this->at(1))
+ ->method('getParam')
+ ->with('update_section_id')
+ ->willReturn(false);
+
+ $expected = ['section' => 'cart'];
+ $this->sectionPool->expects($this->once())
+ ->method('getSectionsData')
+ ->willReturn($expected);
+
+ $resultJson->expects($this->once())
+ ->method('setData')
+ ->with($expected)
+ ->willReturn(json_encode($expected));
+ $this->assertSame(json_encode($expected), $this->controller->execute());
+ }
+}
diff --git a/app/code/Magento/Customer/composer.json b/app/code/Magento/Customer/composer.json
index 8c43ad3d16d5b..632fbcb2f19ad 100644
--- a/app/code/Magento/Customer/composer.json
+++ b/app/code/Magento/Customer/composer.json
@@ -29,7 +29,7 @@
"magento/module-customer-sample-data": "Sample Data version:100.0.*"
},
"type": "magento2-module",
- "version": "100.0.8",
+ "version": "100.0.9",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/code/Magento/Dhl/composer.json b/app/code/Magento/Dhl/composer.json
index 76bd8bbfe33f0..611362cdf8f11 100644
--- a/app/code/Magento/Dhl/composer.json
+++ b/app/code/Magento/Dhl/composer.json
@@ -19,7 +19,7 @@
"magento/module-checkout": "100.0.*"
},
"type": "magento2-module",
- "version": "100.0.5",
+ "version": "100.0.6",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/code/Magento/Dhl/view/frontend/web/js/model/shipping-rates-validation-rules.js b/app/code/Magento/Dhl/view/frontend/web/js/model/shipping-rates-validation-rules.js
index c6e03eb9ebf84..e51a6cefa2cb0 100644
--- a/app/code/Magento/Dhl/view/frontend/web/js/model/shipping-rates-validation-rules.js
+++ b/app/code/Magento/Dhl/view/frontend/web/js/model/shipping-rates-validation-rules.js
@@ -15,6 +15,9 @@ define(
},
'country_id': {
'required': true
+ },
+ 'city': {
+ 'required': true
}
};
}
diff --git a/app/code/Magento/Fedex/Model/Carrier.php b/app/code/Magento/Fedex/Model/Carrier.php
index c5b4ab7846ddc..6768c524d564b 100644
--- a/app/code/Magento/Fedex/Model/Carrier.php
+++ b/app/code/Magento/Fedex/Model/Carrier.php
@@ -9,10 +9,10 @@
namespace Magento\Fedex\Model;
use Magento\Framework\Module\Dir;
+use Magento\Framework\Xml\Security;
use Magento\Quote\Model\Quote\Address\RateRequest;
use Magento\Shipping\Model\Carrier\AbstractCarrierOnline;
use Magento\Shipping\Model\Rate\Result;
-use Magento\Framework\Xml\Security;
/**
* Fedex shipping implementation
@@ -119,6 +119,25 @@ class Carrier extends AbstractCarrierOnline implements \Magento\Shipping\Model\C
*/
protected $_productCollectionFactory;
+ /**
+ * @inheritdoc
+ */
+ protected $_debugReplacePrivateDataKeys = [
+ 'Key', 'Password', 'MeterNumber',
+ ];
+
+ /**
+ * Version of tracking service
+ * @var int
+ */
+ private static $trackServiceVersion = 10;
+
+ /**
+ * List of TrackReply errors
+ * @var array
+ */
+ private static $trackingErrors = ['FAILURE', 'ERROR'];
+
/**
* @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
* @param \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory $rateErrorFactory
@@ -186,7 +205,7 @@ public function __construct(
$wsdlBasePath = $configReader->getModuleDir(Dir::MODULE_ETC_DIR, 'Magento_Fedex') . '/wsdl/';
$this->_shipServiceWsdl = $wsdlBasePath . 'ShipService_v10.wsdl';
$this->_rateServiceWsdl = $wsdlBasePath . 'RateService_v10.wsdl';
- $this->_trackServiceWsdl = $wsdlBasePath . 'TrackService_v5.wsdl';
+ $this->_trackServiceWsdl = $wsdlBasePath . 'TrackService_v' . self::$trackServiceVersion . '.wsdl';
}
/**
@@ -331,6 +350,10 @@ public function setRequest(RateRequest $request)
} else {
}
+ if ($request->getDestCity()) {
+ $r->setDestCity($request->getDestCity());
+ }
+
$weight = $this->getTotalNumOfBoxes($request->getPackageWeight());
$r->setWeight($weight);
if ($request->getFreeMethodWeight() != $request->getPackageWeight()) {
@@ -360,6 +383,9 @@ public function setRequest(RateRequest $request)
*/
public function getResult()
{
+ if (!$this->_result) {
+ $this->_result = $this->_trackFactory->create();
+ }
return $this->_result;
}
@@ -425,6 +451,10 @@ protected function _formRateRequest($purpose)
],
];
+ if ($r->getDestCity()) {
+ $ratesRequest['RequestedShipment']['Recipient']['Address']['City'] = $r->getDestCity();
+ }
+
if ($purpose == self::RATE_REQUEST_GENERAL) {
$ratesRequest['RequestedShipment']['RequestedPackageLineItems'][0]['InsuredValue'] = [
'Amount' => $r->getValue(),
@@ -454,7 +484,7 @@ protected function _doRatesRequest($purpose)
$ratesRequest = $this->_formRateRequest($purpose);
$requestString = serialize($ratesRequest);
$response = $this->_getCachedQuotes($requestString);
- $debugData = ['request' => $ratesRequest];
+ $debugData = ['request' => $this->filterDebugData($ratesRequest)];
if ($response === null) {
try {
$client = $this->_createRateSoapClient();
@@ -494,7 +524,10 @@ protected function _getQuotes()
// make general request for all methods
$response = $this->_doRatesRequest(self::RATE_REQUEST_GENERAL);
$preparedGeneral = $this->_prepareRateResponse($response);
- if (!$preparedGeneral->getError() || $this->_result->getError() && $preparedGeneral->getError()) {
+ if (!$preparedGeneral->getError()
+ || $this->_result->getError() && $preparedGeneral->getError()
+ || empty($this->_result->getAllRates())
+ ) {
$this->_result->append($preparedGeneral);
}
@@ -714,7 +747,7 @@ protected function _parseXmlResponse($response)
$priceArr = [];
if (strlen(trim($response)) > 0) {
- $xml = $this->parseXml($response, 'Magento\Shipping\Model\Simplexml\Element');
+ $xml = $this->parseXml($response, \Magento\Shipping\Model\Simplexml\Element::class);
if (is_object($xml)) {
if (is_object($xml->Error) && is_object($xml->Error->Message)) {
$errorTitle = (string)$xml->Error->Message;
@@ -752,6 +785,7 @@ protected function _parseXmlResponse($response)
$error = $this->_rateErrorFactory->create();
$error->setCarrier('fedex');
$error->setCarrierTitle($this->getConfigData('title'));
+ $error->setErrorMessage($errorTitle);
$error->setErrorMessage($this->getConfigData('specificerrmsg'));
$result->append($error);
} else {
@@ -859,14 +893,14 @@ public function getCode($type, $code = '')
'from_us' => [
'method' => ['INTERNATIONAL_FIRST', 'INTERNATIONAL_ECONOMY', 'INTERNATIONAL_PRIORITY'],
],
- ]
+ ],
],
[
'containers' => ['FEDEX_10KG_BOX', 'FEDEX_25KG_BOX'],
'filters' => [
'within_us' => [],
'from_us' => ['method' => ['INTERNATIONAL_PRIORITY']],
- ]
+ ],
],
[
'containers' => ['YOUR_PACKAGING'],
@@ -904,7 +938,7 @@ public function getCode($type, $code = '')
'INTERNATIONAL_PRIORITY_FREIGHT',
],
],
- ]
+ ],
],
],
'delivery_confirmation_types' => [
@@ -1016,9 +1050,16 @@ protected function _getXMLTracking($tracking)
'AccountNumber' => $this->getConfigData('account'),
'MeterNumber' => $this->getConfigData('meter_number'),
],
- 'Version' => ['ServiceId' => 'trck', 'Major' => '5', 'Intermediate' => '0', 'Minor' => '0'],
- 'PackageIdentifier' => ['Type' => 'TRACKING_NUMBER_OR_DOORTAG', 'Value' => $tracking],
- 'IncludeDetailedScans' => 1,
+ 'Version' => [
+ 'ServiceId' => 'trck',
+ 'Major' => self::$trackServiceVersion,
+ 'Intermediate' => '0',
+ 'Minor' => '0',
+ ],
+ 'SelectionDetails' => [
+ 'PackageIdentifier' => ['Type' => 'TRACKING_NUMBER_OR_DOORTAG', 'Value' => $tracking],
+ ],
+ 'ProcessingOptions' => 'INCLUDE_DETAILED_SCANS'
];
$requestString = serialize($trackRequest);
$response = $this->_getCachedQuotes($requestString);
@@ -1045,114 +1086,48 @@ protected function _getXMLTracking($tracking)
/**
* Parse tracking response
*
- * @param string[] $trackingValue
+ * @param string $trackingValue
* @param \stdClass $response
* @return void
- * @SuppressWarnings(PHPMD.CyclomaticComplexity)
- * @SuppressWarnings(PHPMD.NPathComplexity)
- * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
*/
protected function _parseTrackingResponse($trackingValue, $response)
{
- if (is_object($response)) {
- if ($response->HighestSeverity == 'FAILURE' || $response->HighestSeverity == 'ERROR') {
- $errorTitle = (string)$response->Notifications->Message;
- } elseif (isset($response->TrackDetails)) {
- $trackInfo = $response->TrackDetails;
- $resultArray['status'] = (string)$trackInfo->StatusDescription;
- $resultArray['service'] = (string)$trackInfo->ServiceInfo;
- $timestamp = isset(
- $trackInfo->EstimatedDeliveryTimestamp
- ) ? $trackInfo->EstimatedDeliveryTimestamp : $trackInfo->ActualDeliveryTimestamp;
- $timestamp = strtotime((string)$timestamp);
- if ($timestamp) {
- $resultArray['deliverydate'] = date('Y-m-d', $timestamp);
- $resultArray['deliverytime'] = date('H:i:s', $timestamp);
- }
-
- $deliveryLocation = isset(
- $trackInfo->EstimatedDeliveryAddress
- ) ? $trackInfo->EstimatedDeliveryAddress : $trackInfo->ActualDeliveryAddress;
- $deliveryLocationArray = [];
- if (isset($deliveryLocation->City)) {
- $deliveryLocationArray[] = (string)$deliveryLocation->City;
- }
- if (isset($deliveryLocation->StateOrProvinceCode)) {
- $deliveryLocationArray[] = (string)$deliveryLocation->StateOrProvinceCode;
- }
- if (isset($deliveryLocation->CountryCode)) {
- $deliveryLocationArray[] = (string)$deliveryLocation->CountryCode;
- }
- if ($deliveryLocationArray) {
- $resultArray['deliverylocation'] = implode(', ', $deliveryLocationArray);
- }
-
- $resultArray['signedby'] = (string)$trackInfo->DeliverySignatureName;
- $resultArray['shippeddate'] = date('Y-m-d', (int)$trackInfo->ShipTimestamp);
- if (isset($trackInfo->PackageWeight) && isset($trackInfo->Units)) {
- $weight = (string)$trackInfo->PackageWeight;
- $unit = (string)$trackInfo->Units;
- $resultArray['weight'] = "{$weight} {$unit}";
- }
-
- $packageProgress = [];
- if (isset($trackInfo->Events)) {
- $events = $trackInfo->Events;
- if (isset($events->Address)) {
- $events = [$events];
- }
- foreach ($events as $event) {
- $tempArray = [];
- $tempArray['activity'] = (string)$event->EventDescription;
- $timestamp = strtotime((string)$event->Timestamp);
- if ($timestamp) {
- $tempArray['deliverydate'] = date('Y-m-d', $timestamp);
- $tempArray['deliverytime'] = date('H:i:s', $timestamp);
- }
- if (isset($event->Address)) {
- $addressArray = [];
- $address = $event->Address;
- if (isset($address->City)) {
- $addressArray[] = (string)$address->City;
- }
- if (isset($address->StateOrProvinceCode)) {
- $addressArray[] = (string)$address->StateOrProvinceCode;
- }
- if (isset($address->CountryCode)) {
- $addressArray[] = (string)$address->CountryCode;
- }
- if ($addressArray) {
- $tempArray['deliverylocation'] = implode(', ', $addressArray);
- }
- }
- $packageProgress[] = $tempArray;
- }
- }
-
- $resultArray['progressdetail'] = $packageProgress;
- }
+ if (!is_object($response) || empty($response->HighestSeverity)) {
+ $this->appendTrackingError($trackingValue, __('Invalid response from carrier'));
+ return;
+ } else if (in_array($response->HighestSeverity, self::$trackingErrors)) {
+ $this->appendTrackingError($trackingValue, (string) $response->Notifications->Message);
+ return;
+ } else if (empty($response->CompletedTrackDetails) || empty($response->CompletedTrackDetails->TrackDetails)) {
+ $this->appendTrackingError($trackingValue, __('No available tracking items'));
+ return;
}
- if (!$this->_result) {
- $this->_result = $this->_trackFactory->create();
+ $trackInfo = $response->CompletedTrackDetails->TrackDetails;
+
+ // Fedex can return tracking details as single object instead array
+ if (is_object($trackInfo)) {
+ $trackInfo = [$trackInfo];
}
- if (isset($resultArray)) {
+ $result = $this->getResult();
+ $carrierTitle = $this->getConfigData('title');
+ $counter = 0;
+ foreach ($trackInfo as $item) {
$tracking = $this->_trackStatusFactory->create();
- $tracking->setCarrier('fedex');
- $tracking->setCarrierTitle($this->getConfigData('title'));
+ $tracking->setCarrier(self::CODE);
+ $tracking->setCarrierTitle($carrierTitle);
$tracking->setTracking($trackingValue);
- $tracking->addData($resultArray);
- $this->_result->append($tracking);
- } else {
- $error = $this->_trackErrorFactory->create();
- $error->setCarrier('fedex');
- $error->setCarrierTitle($this->getConfigData('title'));
- $error->setTracking($trackingValue);
- $error->setErrorMessage(
- $errorTitle ? $errorTitle : __('For some reason we can\'t retrieve tracking info right now.')
+ $tracking->addData($this->processTrackingDetails($item));
+ $result->append($tracking);
+ $counter ++;
+ }
+
+ // no available tracking details
+ if (!$counter) {
+ $this->appendTrackingError(
+ $trackingValue, __('For some reason we can\'t retrieve tracking info right now.')
);
- $this->_result->append($error);
}
}
@@ -1221,7 +1196,7 @@ protected function _getAuthDetails()
'TransactionDetail' => [
'CustomerTransactionId' => '*** Express Domestic Shipping Request v9 using PHP ***',
],
- 'Version' => ['ServiceId' => 'ship', 'Major' => '10', 'Intermediate' => '0', 'Minor' => '0']
+ 'Version' => ['ServiceId' => 'ship', 'Major' => '10', 'Intermediate' => '0', 'Minor' => '0'],
];
}
@@ -1250,7 +1225,6 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request)
$width = $packageParams->getWidth();
$length = $packageParams->getLength();
$weightUnits = $packageParams->getWeightUnits() == \Zend_Measure_Weight::POUND ? 'LB' : 'KG';
- $dimensionsUnits = $packageParams->getDimensionUnits() == \Zend_Measure_Length::INCH ? 'IN' : 'CM';
$unitPrice = 0;
$itemsQty = 0;
$itemsDesc = [];
@@ -1393,12 +1367,12 @@ protected function _formShipmentRequest(\Magento\Framework\DataObject $request)
// set dimensions
if ($length || $width || $height) {
- $requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions'] = [];
- $dimenssions = &$requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions'];
- $dimenssions['Length'] = $length;
- $dimenssions['Width'] = $width;
- $dimenssions['Height'] = $height;
- $dimenssions['Units'] = $dimensionsUnits;
+ $requestClient['RequestedShipment']['RequestedPackageLineItems']['Dimensions'] = [
+ 'Length' => $length,
+ 'Width' => $width,
+ 'Height' => $height,
+ 'Units' => $packageParams->getDimensionUnits() == \Zend_Measure_Length::INCH ? 'IN' : 'CM',
+ ];
}
return $this->_getAuthDetails() + $requestClient;
@@ -1453,9 +1427,10 @@ protected function _doShipmentRequest(\Magento\Framework\DataObject $request)
* @param array|object $trackingIds
* @return string
*/
- private function getTrackingNumber($trackingIds) {
+ private function getTrackingNumber($trackingIds)
+ {
return is_array($trackingIds) ? array_map(
- function($val) {
+ function ($val) {
return $val->TrackingNumber;
},
$trackingIds
@@ -1560,4 +1535,187 @@ public function getDeliveryConfirmationTypes(\Magento\Framework\DataObject $para
{
return $this->getCode('delivery_confirmation_types');
}
+
+ /**
+ * Recursive replace sensitive fields in debug data by the mask
+ * @param string $data
+ * @return string
+ */
+ protected function filterDebugData($data)
+ {
+ foreach (array_keys($data) as $key) {
+ if (is_array($data[$key])) {
+ $data[$key] = $this->filterDebugData($data[$key]);
+ } elseif (in_array($key, $this->_debugReplacePrivateDataKeys)) {
+ $data[$key] = self::DEBUG_KEYS_MASK;
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * Parse track details response from Fedex
+ * @param \stdClass $trackInfo
+ * @return array
+ * @SuppressWarnings(PHPMD.CyclomaticComplexity)
+ * @SuppressWarnings(PHPMD.NPathComplexity)
+ */
+ private function processTrackingDetails(\stdClass $trackInfo)
+ {
+ $result = [
+ 'shippeddate' => null,
+ 'deliverydate' => null,
+ 'deliverytime' => null,
+ 'deliverylocation' => null,
+ 'weight' => null,
+ 'progressdetail' => [],
+ ];
+
+ if (!empty($trackInfo->ShipTimestamp) &&
+ ($datetime = \DateTime::createFromFormat(\DateTime::ISO8601, $trackInfo->ShipTimestamp)) !== false
+ ) {
+ $result['shippeddate'] = $datetime->format('Y-m-d');
+ }
+
+ $result['signedby'] = !empty($trackInfo->DeliverySignatureName) ?
+ (string) $trackInfo->DeliverySignatureName :
+ null;
+
+ $result['status'] = (!empty($trackInfo->StatusDetail) && !empty($trackInfo->StatusDetail->Description)) ?
+ (string) $trackInfo->StatusDetail->Description :
+ null;
+ $result['service'] = (!empty($trackInfo->Service) && !empty($trackInfo->Service->Description)) ?
+ (string) $trackInfo->Service->Description :
+ null;
+
+ $datetime = $this->getDeliveryDateTime($trackInfo);
+ if ($datetime) {
+ $result['deliverydate'] = $datetime->format('Y-m-d');
+ $result['deliverytime'] = $datetime->format('H:i:s');
+ }
+
+ $address = null;
+ if (!empty($trackInfo->EstimatedDeliveryAddress)) {
+ $address = $trackInfo->EstimatedDeliveryAddress;
+ } elseif (!empty($trackInfo->ActualDeliveryAddress)) {
+ $address = $trackInfo->ActualDeliveryAddress;
+ }
+
+ if (!empty($address)) {
+ $result['deliverylocation'] = $this->getDeliveryAddress($address);
+ }
+
+ if (!empty($trackInfo->PackageWeight)) {
+ $result['weight'] = sprintf(
+ '%s %s',
+ (string) $trackInfo->PackageWeight->Value,
+ (string) $trackInfo->PackageWeight->Units
+ );
+ }
+
+ if (!empty($trackInfo->Events)) {
+ $events = $trackInfo->Events;
+ if (is_object($events)) {
+ $events = [$trackInfo->Events];
+ }
+ $result['progressdetail'] = $this->processTrackDetailsEvents($events);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Parse delivery datetime from tracking details
+ * @param \stdClass $trackInfo
+ * @return \Datetime|null
+ */
+ private function getDeliveryDateTime(\stdClass $trackInfo)
+ {
+ $timestamp = null;
+ if (!empty($trackInfo->EstimatedDeliveryTimestamp)) {
+ $timestamp = $trackInfo->EstimatedDeliveryTimestamp;
+ } elseif (!empty($trackInfo->ActualDeliveryTimestamp)) {
+ $timestamp = $trackInfo->ActualDeliveryTimestamp;
+ }
+
+ return $timestamp ? \DateTime::createFromFormat(\DateTime::ISO8601, $timestamp) : null;
+ }
+
+ /**
+ * Get delivery address details in string representation
+ * Return City, State, Country Code
+ *
+ * @param \stdClass $address
+ * @return \Magento\Framework\Phrase|string
+ */
+ private function getDeliveryAddress(\stdClass $address)
+ {
+ $details = [];
+
+ if (!empty($address->City)) {
+ $details[] = (string) $address->City;
+ }
+
+ if (!empty($address->StateOrProvinceCode)) {
+ $details[] = (string) $address->StateOrProvinceCode;
+ }
+
+ if (!empty($address->CountryCode)) {
+ $details[] = (string) $address->CountryCode;
+ }
+
+ return implode(', ', $details);
+ }
+
+ /**
+ * Parse tracking details events from response
+ * Return list of items in such format:
+ * ['activity', 'deliverydate', 'deliverytime', 'deliverylocation']
+ *
+ * @param array $events
+ * @return array
+ */
+ private function processTrackDetailsEvents(array $events)
+ {
+ $result = [];
+ /** @var \stdClass $event */
+ foreach ($events as $event) {
+ $item = [
+ 'activity' => (string) $event->EventDescription,
+ 'deliverydate' => null,
+ 'deliverytime' => null,
+ 'deliverylocation' => null
+ ];
+
+ if (!empty($event->Timestamp)) {
+ $datetime = \DateTime::createFromFormat(\DateTime::ISO8601, $event->Timestamp);
+ $item['deliverydate'] = $datetime->format('Y-m-d');
+ $item['deliverytime'] = $datetime->format('H:i:s');
+ }
+
+ if (!empty($event->Address)) {
+ $item['deliverylocation'] = $this->getDeliveryAddress($event->Address);
+ }
+
+ $result[] = $item;
+ }
+
+ return $result;
+ }
+
+ /**
+ * Append error message to rate result instance
+ * @param string $trackingValue
+ * @param string $errorMessage
+ */
+ private function appendTrackingError($trackingValue, $errorMessage)
+ {
+ $error = $this->_trackErrorFactory->create();
+ $error->setCarrier('fedex');
+ $error->setCarrierTitle($this->getConfigData('title'));
+ $error->setTracking($trackingValue);
+ $error->setErrorMessage($errorMessage);
+ $result = $this->getResult();
+ $result->append($error);
+ }
}
diff --git a/app/code/Magento/Fedex/Test/Unit/Model/CarrierTest.php b/app/code/Magento/Fedex/Test/Unit/Model/CarrierTest.php
index 41a48912ac4e4..385ddf8414832 100644
--- a/app/code/Magento/Fedex/Test/Unit/Model/CarrierTest.php
+++ b/app/code/Magento/Fedex/Test/Unit/Model/CarrierTest.php
@@ -5,131 +5,200 @@
*/
namespace Magento\Fedex\Test\Unit\Model;
-use Magento\Quote\Model\Quote\Address\RateRequest;
-use Magento\Framework\DataObject;
+use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
+use Magento\CatalogInventory\Model\StockRegistry;
+use Magento\Directory\Helper\Data;
+use Magento\Directory\Model\Country;
+use Magento\Directory\Model\CountryFactory;
+use Magento\Directory\Model\CurrencyFactory;
+use Magento\Directory\Model\RegionFactory;
+use Magento\Fedex\Model\Carrier;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\Module\Dir\Reader;
+use Magento\Framework\Pricing\PriceCurrencyInterface;
+use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
use Magento\Framework\Xml\Security;
+use Magento\Quote\Model\Quote\Address\RateRequest;
+use Magento\Quote\Model\Quote\Address\RateResult\Error as RateResultError;
+use Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory as RateErrorFactory;
+use Magento\Quote\Model\Quote\Address\RateResult\Method;
+use Magento\Quote\Model\Quote\Address\RateResult\MethodFactory;
+use Magento\Shipping\Model\Rate\Result as RateResult;
+use Magento\Shipping\Model\Rate\ResultFactory as RateResultFactory;
+use Magento\Shipping\Model\Simplexml\ElementFactory;
+use Magento\Shipping\Model\Tracking\Result;
+use Magento\Shipping\Model\Tracking\Result\Error;
+use Magento\Shipping\Model\Tracking\Result\ErrorFactory;
+use Magento\Shipping\Model\Tracking\Result\Status;
+use Magento\Shipping\Model\Tracking\Result\StatusFactory;
+use Magento\Shipping\Model\Tracking\ResultFactory;
+use Magento\Store\Model\Store;
+use Magento\Store\Model\StoreManagerInterface;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+use Psr\Log\LoggerInterface;
/**
- * Class CarrierTest
- * @package Magento\Fedex\Model
- * TODO refactor me
+ * CarrierTest contains units test for Fedex carrier methods
+ *
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class CarrierTest extends \PHPUnit_Framework_TestCase
{
/**
- * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
+ * @var ObjectManager
*/
- protected $_helper;
+ private $helper;
/**
- * @var \Magento\Fedex\Model\Carrier
+ * @var Carrier|MockObject
*/
- protected $_model;
+ private $model;
/**
- * @var \Magento\Framework\App\Config\ScopeConfigInterface|\PHPUnit_Framework_MockObject_MockObject
+ * @var ScopeConfigInterface|MockObject
*/
- protected $scope;
+ private $scope;
/**
- * Model under test
- *
- * @var \Magento\Quote\Model\Quote\Address\RateResult\Error|\PHPUnit_Framework_MockObject_MockObject
+ * @var Error|MockObject
*/
- protected $error;
+ private $error;
/**
- * @var \Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory|\PHPUnit_Framework_MockObject_MockObject
+ * @var ErrorFactory|MockObject
*/
- protected $errorFactory;
+ private $errorFactory;
/**
- * @return void
+ * @var ErrorFactory|MockObject
*/
- public function setUp()
- {
- $this->scope = $this->getMockBuilder(
- '\Magento\Framework\App\Config\ScopeConfigInterface'
- )->disableOriginalConstructor()->getMock();
-
- $this->scope->expects(
- $this->any()
- )->method(
- 'getValue'
- )->will(
- $this->returnCallback([$this, 'scopeConfiggetValue'])
- );
-
- $country = $this->getMock(
- 'Magento\Directory\Model\Country',
- ['load', 'getData', '__wakeup'],
- [],
- '',
- false
- );
- $country->expects($this->any())->method('load')->will($this->returnSelf());
- $countryFactory = $this->getMock('Magento\Directory\Model\CountryFactory', ['create'], [], '', false);
- $countryFactory->expects($this->any())->method('create')->will($this->returnValue($country));
-
- $rate = $this->getMock('Magento\Shipping\Model\Rate\Result', ['getError'], [], '', false);
- $rateFactory = $this->getMock('Magento\Shipping\Model\Rate\ResultFactory', ['create'], [], '', false);
- $rateFactory->expects($this->any())->method('create')->will($this->returnValue($rate));
-
- $this->error = $this->getMockBuilder('\Magento\Quote\Model\Quote\Address\RateResult\Error')
- ->setMethods(['setCarrier', 'setCarrierTitle', 'setErrorMessage'])
+ private $trackErrorFactory;
+
+ /**
+ * @var StatusFactory|MockObject
+ */
+ private $statusFactory;
+
+ /**
+ * @var Result
+ */
+ private $result;
+
+ /**
+ * @inheritdoc
+ */
+ protected function setUp()
+ {
+ $this->helper = new ObjectManager($this);
+ $this->scope = $this->getMockBuilder(ScopeConfigInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->scope->expects(static::any())
+ ->method('getValue')
+ ->willReturnCallback([$this, 'scopeConfigGetValue']);
+
+ $countryFactory = $this->getCountryFactory();
+ $rateFactory = $this->getRateFactory();
+ $storeManager = $this->getStoreManager();
+ $resultFactory = $this->getResultFactory();
+ $this->initRateErrorFactory();
+
+ $rateMethodFactory = $this->getRateMethodFactory();
+
+ $this->trackErrorFactory = $this->getMockBuilder(ErrorFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
->getMock();
- $this->errorFactory = $this->getMockBuilder('Magento\Quote\Model\Quote\Address\RateResult\ErrorFactory')
+
+ $this->statusFactory = $this->getMockBuilder(StatusFactory::class)
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
- $this->errorFactory->expects($this->any())->method('create')->willReturn($this->error);
-
- $store = $this->getMock('Magento\Store\Model\Store', ['getBaseCurrencyCode', '__wakeup'], [], '', false);
- $storeManager = $this->getMockForAbstractClass('Magento\Store\Model\StoreManagerInterface');
- $storeManager->expects($this->any())->method('getStore')->will($this->returnValue($store));
- $priceCurrency = $this->getMockBuilder('Magento\Framework\Pricing\PriceCurrencyInterface')->getMock();
-
- $rateMethod = $this->getMock(
- 'Magento\Quote\Model\Quote\Address\RateResult\Method',
- null,
- ['priceCurrency' => $priceCurrency]
- );
- $rateMethodFactory = $this->getMock(
- 'Magento\Quote\Model\Quote\Address\RateResult\MethodFactory',
- ['create'],
- [],
- '',
- false
- );
- $rateMethodFactory->expects($this->any())->method('create')->will($this->returnValue($rateMethod));
- $this->_model = $this->getMock(
- 'Magento\Fedex\Model\Carrier',
- ['_getCachedQuotes', '_debug'],
- [
- 'scopeConfig' => $this->scope,
- 'rateErrorFactory' => $this->errorFactory,
- 'logger' => $this->getMock('Psr\Log\LoggerInterface'),
- 'xmlSecurity' => new Security(),
- 'xmlElFactory' => $this->getMock('Magento\Shipping\Model\Simplexml\ElementFactory', [], [], '', false),
- 'rateFactory' => $rateFactory,
- 'rateMethodFactory' => $rateMethodFactory,
- 'trackFactory' => $this->getMock('Magento\Shipping\Model\Tracking\ResultFactory', [], [], '', false),
- 'trackErrorFactory' =>
- $this->getMock('Magento\Shipping\Model\Tracking\Result\ErrorFactory', [], [], '', false),
- 'trackStatusFactory' =>
- $this->getMock('Magento\Shipping\Model\Tracking\Result\StatusFactory', [], [], '', false),
- 'regionFactory' => $this->getMock('Magento\Directory\Model\RegionFactory', [], [], '', false),
- 'countryFactory' => $countryFactory,
- 'currencyFactory' => $this->getMock('Magento\Directory\Model\CurrencyFactory', [], [], '', false),
- 'directoryData' => $this->getMock('Magento\Directory\Helper\Data', [], [], '', false),
- 'stockRegistry' => $this->getMock('Magento\CatalogInventory\Model\StockRegistry', [], [], '', false),
- 'storeManager' => $storeManager,
- 'configReader' => $this->getMock('Magento\Framework\Module\Dir\Reader', [], [], '', false),
- 'productCollectionFactory' =>
- $this->getMock('Magento\Catalog\Model\ResourceModel\Product\CollectionFactory', [], [], '', false),
- 'data' => []
- ]
- );
+
+ $elementFactory = $this->getMockBuilder(ElementFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $collectionFactory = $this->getMockBuilder(CollectionFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $regionFactory = $this->getMockBuilder(RegionFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $currencyFactory = $this->getMockBuilder(CurrencyFactory::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $data = $this->getMockBuilder(Data::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $stockRegistry = $this->getMockBuilder(StockRegistry::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $reader = $this->getMockBuilder(Reader::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->model = $this->getMockBuilder(Carrier::class)
+ ->setMethods(['_getCachedQuotes', '_debug'])
+ ->setConstructorArgs(
+ [
+ 'scopeConfig' => $this->scope,
+ 'rateErrorFactory' => $this->errorFactory,
+ 'logger' => $this->getMock(LoggerInterface::class),
+ 'xmlSecurity' => new Security(),
+ 'xmlElFactory' => $elementFactory,
+ 'rateFactory' => $rateFactory,
+ 'rateMethodFactory' => $rateMethodFactory,
+ 'trackFactory' => $resultFactory,
+ 'trackErrorFactory' => $this->trackErrorFactory,
+ 'trackStatusFactory' => $this->statusFactory,
+ 'regionFactory' => $regionFactory,
+ 'countryFactory' => $countryFactory,
+ 'currencyFactory' => $currencyFactory,
+ 'directoryData' => $data,
+ 'stockRegistry' => $stockRegistry,
+ 'storeManager' => $storeManager,
+ 'configReader' => $reader,
+ 'productCollectionFactory' => $collectionFactory,
+ ]
+ )
+ ->getMock();
+ }
+
+ /**
+ * @covers \Magento\Fedex\Model\Carrier::setRequest
+ */
+ public function testSetRequestWithoutCity()
+ {
+ $request = $this->getMockBuilder(RateRequest::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getDestCity'])
+ ->getMock();
+ $request->expects($this->once())
+ ->method('getDestCity')
+ ->willReturn(null);
+ $this->model->setRequest($request);
+ }
+
+ /**
+ * @covers \Magento\Fedex\Model\Carrier::setRequest
+ */
+ public function testSetRequestWithCity()
+ {
+ $request = $this->getMockBuilder(RateRequest::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getDestCity'])
+ ->getMock();
+ $request->expects(static::exactly(2))
+ ->method('getDestCity')
+ ->willReturn('Small Town');
+ $this->model->setRequest($request);
}
/**
@@ -137,7 +206,7 @@ public function setUp()
* @param $path
* @return null|string
*/
- public function scopeConfiggetValue($path)
+ public function scopeConfigGetValue($path)
{
switch ($path) {
case 'carriers/fedex/showmethod':
@@ -147,44 +216,57 @@ public function scopeConfiggetValue($path)
return 'ServiceType';
break;
}
+ return null;
}
/**
+ * @param float $amount
+ * @param string $rateType
+ * @param float $expected
* @dataProvider collectRatesDataProvider
*/
public function testCollectRatesRateAmountOriginBased($amount, $rateType, $expected)
{
- $this->scope->expects($this->any())->method('isSetFlag')->will($this->returnValue(true));
+ $this->scope->expects(static::any())
+ ->method('isSetFlag')
+ ->willReturn(true);
// @codingStandardsIgnoreStart
- $netAmount = new \Magento\Framework\DataObject([]);
+ $netAmount = new \stdClass();
$netAmount->Amount = $amount;
- $totalNetCharge = new \Magento\Framework\DataObject([]);
+ $totalNetCharge = new \stdClass();
$totalNetCharge->TotalNetCharge = $netAmount;
$totalNetCharge->RateType = $rateType;
- $ratedShipmentDetail = new \Magento\Framework\DataObject([]);
+ $ratedShipmentDetail = new \stdClass();
$ratedShipmentDetail->ShipmentRateDetail = $totalNetCharge;
- $rate = new \Magento\Framework\DataObject([]);
+ $rate = new \stdClass();
$rate->ServiceType = 'ServiceType';
$rate->RatedShipmentDetails = [$ratedShipmentDetail];
- $response = new \Magento\Framework\DataObject([]);
+ $response = new \stdClass();
$response->HighestSeverity = 'SUCCESS';
$response->RateReplyDetails = $rate;
+ // @codingStandardsIgnoreEnd
- $this->_model->expects($this->any())->method('_getCachedQuotes')->will(
- $this->returnValue(serialize($response))
- );
- $request = $this->getMock('Magento\Quote\Model\Quote\Address\RateRequest', [], [], '', false);
- foreach ($this->_model->collectRates($request)->getAllRates() as $allRates) {
+ $this->model->expects(static::any())
+ ->method('_getCachedQuotes')
+ ->willReturn(serialize($response));
+ $request = $this->getMockBuilder(RateRequest::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ foreach ($this->model->collectRates($request)->getAllRates() as $allRates) {
$this->assertEquals($expected, $allRates->getData('cost'));
}
- // @codingStandardsIgnoreEnd
}
+ /**
+ * Get list of rates variations
+ * @return array
+ */
public function collectRatesDataProvider()
{
return [
@@ -201,15 +283,355 @@ public function collectRatesDataProvider()
public function testCollectRatesErrorMessage()
{
- $this->scope->expects($this->once())->method('isSetFlag')->willReturn(false);
+ $this->scope->expects(static::once())
+ ->method('isSetFlag')
+ ->willReturn(false);
- $this->error->expects($this->once())->method('setCarrier')->with('fedex');
- $this->error->expects($this->once())->method('setCarrierTitle');
- $this->error->expects($this->once())->method('setErrorMessage');
+ $this->error->expects(static::once())
+ ->method('setCarrier')
+ ->with('fedex');
+ $this->error->expects(static::once())
+ ->method('setCarrierTitle');
+ $this->error->expects(static::once())
+ ->method('setErrorMessage');
$request = new RateRequest();
$request->setPackageWeight(1);
- $this->assertSame($this->error, $this->_model->collectRates($request));
+ static::assertSame($this->error, $this->model->collectRates($request));
+ }
+
+ /**
+ * @param string $data
+ * @param array $maskFields
+ * @param string $expected
+ * @dataProvider logDataProvider
+ */
+ public function testFilterDebugData($data, array $maskFields, $expected)
+ {
+ $refClass = new \ReflectionClass(Carrier::class);
+ $property = $refClass->getProperty('_debugReplacePrivateDataKeys');
+ $property->setAccessible(true);
+ $property->setValue($this->model, $maskFields);
+
+ $refMethod = $refClass->getMethod('filterDebugData');
+ $refMethod->setAccessible(true);
+ $result = $refMethod->invoke($this->model, $data);
+ static::assertEquals($expected, $result);
+ }
+
+ /**
+ * Get list of variations
+ */
+ public function logDataProvider()
+ {
+ return [
+ [
+ [
+ 'WebAuthenticationDetail' => [
+ 'UserCredential' => [
+ 'Key' => 'testKey',
+ 'Password' => 'testPassword',
+ ],
+ ],
+ 'ClientDetail' => [
+ 'AccountNumber' => 4121213,
+ 'MeterNumber' => 'testMeterNumber',
+ ],
+ ],
+ ['Key', 'Password', 'MeterNumber'],
+ [
+ 'WebAuthenticationDetail' => [
+ 'UserCredential' => [
+ 'Key' => '****',
+ 'Password' => '****',
+ ],
+ ],
+ 'ClientDetail' => [
+ 'AccountNumber' => 4121213,
+ 'MeterNumber' => '****',
+ ],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @covers \Magento\Fedex\Model\Carrier::getTracking
+ */
+ public function testGetTrackingErrorResponse()
+ {
+ $tracking = '123456789012';
+ $errorMessage = 'Tracking information is unavailable.';
+
+ // @codingStandardsIgnoreStart
+ $response = new \stdClass();
+ $response->HighestSeverity = 'ERROR';
+ $response->Notifications = new \stdClass();
+ $response->Notifications->Message = $errorMessage;
+ // @codingStandardsIgnoreEnd
+
+ $this->model->expects(static::once())
+ ->method('_getCachedQuotes')
+ ->willReturn(serialize($response));
+
+ $error = $this->helper->getObject(Error::class);
+ $this->trackErrorFactory->expects(static::once())
+ ->method('create')
+ ->willReturn($error);
+
+ $this->model->getTracking($tracking);
+ $tracks = $this->model->getResult()->getAllTrackings();
+
+ static::assertEquals(1, count($tracks));
+
+ /** @var Error $current */
+ $current = $tracks[0];
+ static::assertInstanceOf(Error::class, $current);
+ static::assertEquals(__($errorMessage), $current->getErrorMessage());
+ }
+
+ /**
+ * @covers \Magento\Fedex\Model\Carrier::getTracking
+ */
+ public function testGetTracking()
+ {
+ $tracking = '123456789012';
+
+ // @codingStandardsIgnoreStart
+ $response = new \stdClass();
+ $response->HighestSeverity = 'SUCCESS';
+ $response->CompletedTrackDetails = new \stdClass();
+
+ $trackDetails = new \stdClass();
+ $trackDetails->ShipTimestamp = '2016-08-05T14:06:35+00:00';
+ $trackDetails->DeliverySignatureName = 'signature';
+
+ $trackDetails->StatusDetail = new \stdClass();
+ $trackDetails->StatusDetail->Description = 'SUCCESS';
+
+ $trackDetails->Service = new \stdClass();
+ $trackDetails->Service->Description = 'ground';
+ $trackDetails->EstimatedDeliveryTimestamp = '2016-08-10T10:20:26+00:00';
+
+ $trackDetails->EstimatedDeliveryAddress = new \stdClass();
+ $trackDetails->EstimatedDeliveryAddress->City = 'Culver City';
+ $trackDetails->EstimatedDeliveryAddress->StateOrProvinceCode = 'CA';
+ $trackDetails->EstimatedDeliveryAddress->CountryCode = 'US';
+
+ $trackDetails->PackageWeight = new \stdClass();
+ $trackDetails->PackageWeight->Value = 23;
+ $trackDetails->PackageWeight->Units = 'LB';
+
+ $response->CompletedTrackDetails->TrackDetails = [$trackDetails];
+ // @codingStandardsIgnoreEnd
+
+ $this->model->expects(static::once())
+ ->method('_getCachedQuotes')
+ ->willReturn(serialize($response));
+
+ $status = $this->helper->getObject(Status::class);
+ $this->statusFactory->expects(static::once())
+ ->method('create')
+ ->willReturn($status);
+
+ $this->model->getTracking($tracking);
+ $tracks = $this->model->getResult()->getAllTrackings();
+ static::assertEquals(1, count($tracks));
+
+ $current = $tracks[0];
+ $fields = [
+ 'signedby',
+ 'status',
+ 'service',
+ 'shippeddate',
+ 'deliverydate',
+ 'deliverytime',
+ 'deliverylocation',
+ 'weight',
+ ];
+ array_walk($fields, function ($field) use ($current) {
+ static::assertNotEmpty($current[$field]);
+ });
+
+ static::assertEquals('2016-08-10', $current['deliverydate']);
+ static::assertEquals('10:20:26', $current['deliverytime']);
+ static::assertEquals('2016-08-05', $current['shippeddate']);
+ }
+
+ /**
+ * @covers \Magento\Fedex\Model\Carrier::getTracking
+ */
+ public function testGetTrackingWithEvents()
+ {
+ $tracking = '123456789012';
+
+ // @codingStandardsIgnoreStart
+ $response = new \stdClass();
+ $response->HighestSeverity = 'SUCCESS';
+ $response->CompletedTrackDetails = new \stdClass();
+
+ $event = new \stdClass();
+ $event->EventDescription = 'Test';
+ $event->Timestamp = '2016-08-05T19:14:53+00:00';
+ $event->Address = new \stdClass();
+
+ $event->Address->City = 'Culver City';
+ $event->Address->StateOrProvinceCode = 'CA';
+ $event->Address->CountryCode = 'US';
+
+ $trackDetails = new \stdClass();
+ $trackDetails->Events = $event;
+
+ $response->CompletedTrackDetails->TrackDetails = $trackDetails;
+ // @codingStandardsIgnoreEnd
+
+ $this->model->expects(static::once())
+ ->method('_getCachedQuotes')
+ ->willReturn(serialize($response));
+
+ $status = $this->helper->getObject(Status::class);
+ $this->statusFactory->expects(static::once())
+ ->method('create')
+ ->willReturn($status);
+
+ $this->model->getTracking($tracking);
+ $tracks = $this->model->getResult()->getAllTrackings();
+ static::assertEquals(1, count($tracks));
+
+ $current = $tracks[0];
+ static::assertNotEmpty($current['progressdetail']);
+ static::assertEquals(1, count($current['progressdetail']));
+
+ $event = $current['progressdetail'][0];
+ $fields = ['activity', 'deliverydate', 'deliverytime', 'deliverylocation'];
+ array_walk($fields, function ($field) use ($event) {
+ static::assertNotEmpty($event[$field]);
+ });
+ static::assertEquals('2016-08-05', $event['deliverydate']);
+ static::assertEquals('19:14:53', $event['deliverytime']);
+ }
+
+ /**
+ * Init RateErrorFactory and RateResultErrors mocks
+ * @return void
+ */
+ private function initRateErrorFactory()
+ {
+ $this->error = $this->getMockBuilder(RateResultError::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['setCarrier', 'setCarrierTitle', 'setErrorMessage'])
+ ->getMock();
+ $this->errorFactory = $this->getMockBuilder(RateErrorFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $this->errorFactory->expects(static::any())
+ ->method('create')
+ ->willReturn($this->error);
+ }
+
+ /**
+ * Creates mock rate result factory
+ * @return RateResultFactory|MockObject
+ */
+ private function getRateFactory()
+ {
+ $rate = $this->getMockBuilder(RateResult::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getError'])
+ ->getMock();
+ $rateFactory = $this->getMockBuilder(RateResultFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $rateFactory->expects(static::any())
+ ->method('create')
+ ->willReturn($rate);
+
+ return $rateFactory;
+ }
+
+ /**
+ * Creates mock object for CountryFactory class
+ * @return CountryFactory|MockObject
+ */
+ private function getCountryFactory()
+ {
+ $country = $this->getMockBuilder(Country::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['load', 'getData'])
+ ->getMock();
+ $country->expects(static::any())
+ ->method('load')
+ ->willReturnSelf();
+
+ $countryFactory = $this->getMockBuilder(CountryFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $countryFactory->expects(static::any())
+ ->method('create')
+ ->willReturn($country);
+
+ return $countryFactory;
+ }
+
+ /**
+ * Creates mock object for ResultFactory class
+ * @return ResultFactory|MockObject
+ */
+ private function getResultFactory()
+ {
+ $resultFactory = $this->getMockBuilder(ResultFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $this->result = $this->helper->getObject(Result::class);
+ $resultFactory->expects(static::any())
+ ->method('create')
+ ->willReturn($this->result);
+
+ return $resultFactory;
+ }
+
+ /**
+ * Creates mock object for store manager
+ * @return StoreManagerInterface|MockObject
+ */
+ private function getStoreManager()
+ {
+ $store = $this->getMockBuilder(Store::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getBaseCurrencyCode'])
+ ->getMock();
+ $storeManager = $this->getMock(StoreManagerInterface::class);
+ $storeManager->expects(static::any())
+ ->method('getStore')
+ ->willReturn($store);
+
+ return $storeManager;
+ }
+
+ /**
+ * Creates mock object for rate method factory
+ * @return MethodFactory|MockObject
+ */
+ private function getRateMethodFactory()
+ {
+ $priceCurrency = $this->getMock(PriceCurrencyInterface::class);
+ $rateMethod = $this->getMockBuilder(Method::class)
+ ->setConstructorArgs(['priceCurrency' => $priceCurrency])
+ ->setMethods(null)
+ ->getMock();
+ $rateMethodFactory = $this->getMockBuilder(MethodFactory::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['create'])
+ ->getMock();
+ $rateMethodFactory->expects(static::any())
+ ->method('create')
+ ->willReturn($rateMethod);
+
+ return $rateMethodFactory;
}
}
diff --git a/app/code/Magento/Fedex/composer.json b/app/code/Magento/Fedex/composer.json
index 7f66babdcc999..88e066ff32158 100644
--- a/app/code/Magento/Fedex/composer.json
+++ b/app/code/Magento/Fedex/composer.json
@@ -15,7 +15,7 @@
"lib-libxml": "*"
},
"type": "magento2-module",
- "version": "100.0.5",
+ "version": "100.0.6",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/code/Magento/Fedex/etc/wsdl/TrackService_v5.wsdl b/app/code/Magento/Fedex/etc/wsdl/TrackService_v10.wsdl
similarity index 63%
rename from app/code/Magento/Fedex/etc/wsdl/TrackService_v5.wsdl
rename to app/code/Magento/Fedex/etc/wsdl/TrackService_v10.wsdl
index f3ceaf5056a20..77f53e02a539a 100644
--- a/app/code/Magento/Fedex/etc/wsdl/TrackService_v5.wsdl
+++ b/app/code/Magento/Fedex/etc/wsdl/TrackService_v10.wsdl
@@ -1,12 +1,12 @@
-
+
-
+
+
+
-
-
@@ -14,7 +14,7 @@
Descriptive data for a physical location. May be used as an actual physical address (place to which one could go), or as a container of "address parts" which should be handled as a unit (such as a city-state-ZIP combination within the US).
-
+ Combination of number, street name, etc. At least one line is required for a valid physical address; empty lines should not be included.
@@ -44,6 +44,11 @@
The two-letter code used to identify a country.
+
+
+ The fully spelt out name of a country.
+
+ Indicates whether this address residential (as opposed to commercial).
@@ -51,6 +56,48 @@
+
+
+ Specifies the different appointment times on a specific date.
+
+
+
+
+
+ Different appointment time windows on the date specified.
+
+
+
+
+
+
+ Specifies the details about the appointment time window.
+
+
+
+
+ The description that FedEx Ground uses for the appointment window being specified.
+
+
+
+
+ Specifies the window of time for an appointment.
+
+
+
+
+
+
+
+ The description that FedEx uses for a given appointment window.
+
+
+
+
+
+
+
+ Identifies where a tracking event occurs.
@@ -73,11 +120,18 @@
+
+
+
+
+
+
+ Identification of a FedEx operating company (transportation).
@@ -108,7 +162,7 @@
- Only used in transactions which require identification of the Fed Ex Office integrator.
+ Only used in transactions which require identification of the FedEx Office integrator.
@@ -118,6 +172,75 @@
+
+
+
+
+ Value used to identify a commodity description; must be unique within the containing shipment.
+
+
+
+
+
+
+
+
+
+
+
+
+ Contains only additional quantitative information other than weight and quantity to calculate duties and taxes.
+
+
+
+
+
+
+ Defines additional characteristic of commodity used to calculate duties and taxes
+
+
+
+
+
+
+
+
+ All data required for this commodity in NAFTA Certificate of Origin.
+
+
+
+
+
+
+
+
+
+
+ True if duplicate packages (more than one package with the same tracking number) have been found, and only limited data will be provided for each one.
+
+
+
+
+ True if additional packages remain to be retrieved.
+
+
+
+
+ Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true).
+
+
+
+
+ Identifies the total number of available track details across all pages.
+
+
+
+
+ Contains detailed tracking information for the requested packages(s).
+
+
+
+ The descriptive data for a point-of-contact person.
@@ -148,6 +271,11 @@
Identifies the phone extension associated with this contact.
+
+
+ Identifies a toll free number, if any, associated with this contact.
+
+ Identifies the pager number associated with this contact.
@@ -171,6 +299,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Unique identifier for the customer exception request.
+
+
+
+
+
+
+
+
+
+
+
+
+ Specifies additional description about customs options. This is a required field when the customs options type is "OTHER".
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Details about the eligibility for a delivery option.
+
+
+
+
+ Type of delivery option.
+
+
+
+
+ Eligibility of the customer for the specific delivery option.
+
+
+
+
+
+
+ Specifies the different option types for delivery.
+
+
+
+
+
+
+
+ The dimensions of this package and the unit type used for the measurements.
@@ -280,6 +486,43 @@
+
+
+
+
+
+ Customer-declared value, with data type and legal values depending on excise condition, used in defining the taxable value of the item.
+
+
+
+
+
+
+ Specifies different values of eligibility status
+
+
+
+
+
+
+
+
+
+ Identifies a kind of FedEx facility.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CM = centimeters, IN = inches
@@ -289,6 +532,15 @@
+
+
+ Time Range specified in local time.
+
+
+
+
+
+ Identifies the representation of human-readable text.
@@ -306,6 +558,73 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Defined by NAFTA regulations.
+
+
+
+
+ Defined by NAFTA regulations.
+
+
+
+
+ Identification of which producer is associated with this commodity (if multiple producers are used in a single shipment).
+
+
+
+
+
+ Date range over which RVC net cost was calculated.
+
+
+
+
+
+
+
+
+
+
+
+
+ See instructions for NAFTA Certificate of Origin for code definitions.
+
+
+
+
+
+
+
+
+
+
+
+
+ See instructions for NAFTA Certificate of Origin for code definitions.
+
+
+
+
+
+
+
+ The descriptive data regarding the result of the submitted transaction.
@@ -382,7 +701,18 @@
Identification for a FedEx operating company (transportation and non-transportation).
+
+
+
+
+
+
+
+
+
+
+
@@ -394,11 +724,42 @@
+
+
+
+
+
+
+
+
+ When the MoreData field = true in a TrackReply the PagingToken must be sent in the subsequent TrackRequest to retrieve the next page of data.
+
+
+
+
+ Specifies the number of results to display per page when the there is more than one page in the subsequent TrackReply.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tracking number and additional shipment data used to identify a unique shipment for proof of delivery.
@@ -431,13 +792,115 @@
-
-
-
-
-
-
-
+
+
+
+
+ This contains the severity type of the most severe Notification in the Notifications array.
+
+
+
+
+ Information about the request/reply such was the transaction successful or not, and any additional information relevant to the request and/or reply. There may be multiple Notifications in a reply.
+
+
+
+
+ Contains the CustomerTransactionDetail that is echoed back to the caller for matching requests and replies and a Localization element for defining the language/translation used in the reply data.
+
+
+
+
+ Contains the version of the reply being used.
+
+
+
+
+ True if duplicate packages (more than one package with the same tracking number) have been found, the packages array contains information about each duplicate. Use this information to determine which of the tracking numbers is the one you need and resend your request using the tracking number and TrackingNumberUniqueIdentifier for that package.
+
+
+
+
+ True if additional packages remain to be retrieved.
+
+
+
+
+ Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true).
+
+
+
+
+ Information about the notifications that are available for this tracking number. If there are duplicates the ship date and destination address information is returned for determining which TrackingNumberUniqueIdentifier to use on a subsequent request.
+
+
+
+
+
+
+
+
+ Descriptive data to be used in authentication of the sender's identity (and right to use FedEx web services).
+
+
+
+
+ Descriptive data identifying the client submitting the transaction.
+
+
+
+
+ Contains a free form field that is echoed back in the reply to match requests with replies and data that governs the data payload language/translations
+
+
+
+
+
+ The tracking number to which the notifications will be triggered from.
+
+
+
+
+ Indicates whether to return tracking information for all associated packages.
+
+
+
+
+ When the MoreDataAvailable field is true in a TrackNotificationReply the PagingToken must be sent in the subsequent TrackNotificationRequest to retrieve the next page of data.
+
+
+
+
+ Use this field when your original request informs you that there are duplicates of this tracking number. If you get duplicates you will also receive some information about each of the duplicate tracking numbers to enable you to chose one and resend that number along with the TrackingNumberUniqueId to get notifications for that tracking number.
+
+
+
+
+ To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates.
+
+
+
+
+ To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates.
+
+
+
+
+ Included in the email notification identifying the requester of this notification.
+
+
+
+
+ Included in the email notification identifying the requester of this notification.
+
+
+
+
+ Who to send the email notifications to and for which events. The notificationRecipientType and NotifyOnShipment fields are not used in this request.
+
+
+
+ The service type of the package/shipment.
@@ -449,11 +912,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -465,10 +951,19 @@
+
+
+
+
+
+
+
+
+ FedEx Signature Proof Of Delivery Fax reply.
@@ -554,6 +1049,7 @@
+
@@ -635,6 +1131,32 @@
+
+
+
+
+ Specifies the status of the track special instructions requested.
+
+
+
+
+ Time when the status was changed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Each instance of this data type represents a barcode whose content must be represented as ASCII text (i.e. not binary data).
@@ -662,22 +1184,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The delivery location at the delivered to address.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Detailed tracking information about a particular package.
@@ -699,16 +1263,12 @@
When duplicate tracking numbers exist this data is returned with summary information for each of the duplicates. The summary information is used to determine which of the duplicates the intended tracking number is. This identifier is used on a subsequent track request to retrieve the tracking data for the desired tracking number.
-
+
- A code that identifies this type of status. This is the most recent status.
-
-
-
-
- A human-readable description of this status.
+ Specifies details about the status of the shipment being tracked.
+ Used to report the status of a piece of a multiple piece shipment which is no longer traveling with the rest of the packages in the shipment or has not been accounted for.
@@ -719,6 +1279,8 @@
Used to convey information such as. 1. FedEx has received information about a package but has not yet taken possession of it. 2. FedEx has handed the package off to a third party for final delivery. 3. The package delivery has been cancelled
+
+ Identifies a FedEx operating company (transportation).
@@ -729,24 +1291,34 @@
Identifies operating transportation company that is the specific to the carrier code.
+
+
+ Specifies a detailed description about the carrier or the operating company.
+
+
+
+
+ If the package was interlined to a cartage agent, this is the name of the cartage agent. (Returned for CSR SL only.)
+
+ Specifies the FXO production centre contact and address.
-
+ Other related identifiers for this package such as reference numbers.
-
+
- Retained for legacy compatibility only. User/screen friendly description of the Service type (e.g. Priority Overnight).
+ (Returned for CSR SL only.)
-
+
- Strict representation of the Service type (e.g. PRIORITY_OVERNIGHT).
+ Specifies details about service such as service description and type.
@@ -789,8 +1361,40 @@
The number of packages in this shipment.
-
-
+
+
+ Specifies the details about the SPOC details.
+
+
+
+
+
+
+
+
+
+
+
+
+ Specifies the reason for return.
+
+
+
+
+
+ List of special handlings that applied to this package. (Returned for CSR SL only.)
+
+
+
+
+ (Returned for CSR SL only.)
+
+
+
+
+ Indicates last-known possession of package (Returned for CSR SL only.)
+
+ The address information for the shipper.
@@ -801,6 +1405,11 @@
The address of the FedEx pickup location/facility.
+
+
+ (Returned for CSR SL only.)
+
+ Estimated package pickup time for shipments that haven't been picked up.
@@ -821,16 +1430,54 @@
Total distance package still has to travel. Returned for Custom Critical shipments.
+
+
+ Provides additional details about package delivery.
+
+
+
+
+ (Returned for CSR SL only.)
+
+
+
+
+ This is the latest updated destination address.
+
+ The address this package is to be (or has been) delivered.
+
+
+
+ The address this package is requested to placed on hold.
+
+
+
+
+ (Returned for CSR SL only.)
+
+ The address of the FedEx delivery location/facility.
+
+
+
+
+ Date and time the package should be (or should have been) delivered. (Returned for CSR SL only.)
+
+
+
+
+ Date and time the package would be delivered if the package has appointment delivery as a special service.
+
+ Projected package delivery time based on ship time stamp, service and destination. Not populated if delivery has already occurred.
@@ -861,16 +1508,28 @@
User/screen friendly representation of the DeliveryLocationType (delivery location at the delivered to address). Can be returned in localized text.
+
+
+ Specifies the number of delivery attempts made to deliver the shipment.
+
+ This is either the name of the person that signed for the package or "Signature not requested" or "Signature on file".
-
+
- True if signed for by signature image is available.
+ Specifies the details about the count of the packages delivered at the delivery location and the count of the packages at the origin.
+
+
+ Specifies the total number of unique addresses on the CRNs in a consolidation.
+
+
+
+ The types of email notifications that are available for the package.
@@ -881,9 +1540,9 @@
Returned for cargo shipments only when they are currently split across vehicles.
-
+
- Indicates redirection eligibility as determined by tracking service, subject to refinement/override by redirect-to-hold service.
+ Specifies the details about the eligibility for different delivery options.
@@ -893,6 +1552,11 @@
+
+
+
+
+ FedEx scanning information about a package.
@@ -928,6 +1592,11 @@
Address information of the station that is responsible for the scan.
+
+
+ FedEx location ID where the scan took place. (Returned for CSR SL only.)
+
+ Indicates where the arrival actually occurred.
@@ -945,6 +1614,7 @@
+
@@ -953,9 +1623,11 @@
+
+
@@ -1010,123 +1682,11 @@
-
-
- FedEx Track Notification reply.
-
-
-
-
- This contains the severity type of the most severe Notification in the Notifications array.
-
-
-
-
- Information about the request/reply such was the transaction successful or not, and any additional information relevant to the request and/or reply. There may be multiple Notifications in a reply.
-
-
-
-
- Contains the CustomerTransactionDetail that is echoed back to the caller for matching requests and replies and a Localization element for defining the language/translation used in the reply data.
-
-
-
-
- Contains the version of the reply being used.
-
-
-
-
- True if duplicate packages (more than one package with the same tracking number) have been found, the packages array contains information about each duplicate. Use this information to determine which of the tracking numbers is the one you need and resend your request using the tracking number and TrackingNumberUniqueIdentifier for that package.
-
-
-
-
- True if additional packages remain to be retrieved.
-
-
-
-
- Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true).
-
-
-
-
- Information about the notifications that are available for this tracking number. If there are duplicates the ship date and destination address information is returned for determining which TrackingNumberUniqueIdentifier to use on a subsequent request.
-
-
-
-
-
-
- FedEx Track Notification request.
-
+
-
-
- Descriptive data to be used in authentication of the sender's identity (and right to use FedEx web services).
-
-
-
-
- Descriptive data identifying the client submitting the transaction.
-
-
-
-
- Contains a free form field that is echoed back in the reply to match requests with replies and data that governs the data payload language/translations
-
-
-
-
- Identifies the version/level of a service operation expected by a caller (in each request) and performed by the callee (in each reply).
-
-
-
-
- The tracking number to which the notifications will be triggered from.
-
-
-
-
- Indicates whether to return tracking information for all associated packages.
-
-
-
-
- When the MoreDataAvailable field is true in a TrackNotificationReply the PagingToken must be sent in the subsequent TrackNotificationRequest to retrieve the next page of data.
-
-
-
-
- Use this field when your original request informs you that there are duplicates of this tracking number. If you get duplicates you will also receive some information about each of the duplicate tracking numbers to enable you to chose one and resend that number along with the TrackingNumberUniqueId to get notifications for that tracking number.
-
-
-
-
- To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates.
-
-
-
-
- To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates.
-
-
-
-
- Included in the email notification identifying the requester of this notification.
-
-
-
-
- Included in the email notification identifying the requester of this notification.
-
-
-
-
- Who to send the email notifications to and for which events. The notificationRecipientType and NotifyOnShipment fields are not used in this request.
-
-
+
+
+
@@ -1134,18 +1694,41 @@
The type and value of the package identifier that is to be used to retrieve the tracking information for a package.
-
+
- The value to be used to retrieve tracking information for a package.
+ The type of the Value to be used to retrieve tracking information for a package (e.g. SHIPPER_REFERENCE, PURCHASE_ORDER, TRACKING_NUMBER_OR_DOORTAG, etc..) .
-
+
- The type of the Value to be used to retrieve tracking information for a package (e.g. SHIPPER_REFERENCE, PURCHASE_ORDER, TRACKING_NUMBER_OR_DOORTAG, etc..) .
+ The value to be used to retrieve tracking information for a package.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Used to report the status of a piece of a multiple piece shipment which is no longer traveling with the rest of the packages in the shipment or has not been accounted for.
@@ -1188,24 +1771,9 @@
Contains the version of the reply being used.
-
-
- True if duplicate packages (more than one package with the same tracking number) have been found, and only limited data will be provided for each one.
-
-
-
-
- True if additional packages remain to be retrieved.
-
-
-
-
- Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true).
-
-
-
+
- Contains detailed tracking information for the requested packages(s).
+ Contains detailed tracking entity information.
@@ -1235,6 +1803,46 @@
The version of the request being used.
+
+
+ Specifies the details needed to select the shipment being requested to be tracked.
+
+
+
+
+ The customer can specify a desired time out value for this particular transaction.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The FedEx operating company (transportation) used for this package's delivery.
@@ -1245,7 +1853,7 @@
Identifies operating transportation company that is the specific to the carrier code.
-
+ The type and value of the package identifier that is to be used to retrieve the tracking information for a package or group of packages.
@@ -1270,29 +1878,181 @@
For tracking by references information either the account number or destination postal code and country must be provided.
+
+
+ Specifies the SPOD account number for the shipment being tracked.
+
+ For tracking by references information either the account number or destination postal code and country must be provided.
-
+
- If false the reply will contain summary/profile data including current status. If true the reply contains profile + detailed scan activity for each package.
+ Specifies the details about how to retrieve the subsequent pages when there is more than one page in the TrackReply.
-
+
- When the MoreData field = true in a TrackReply the PagingToken must be sent in the subsequent TrackRequest to retrieve the next page of data.
+ The customer can specify a desired time out value for this particular tracking number.
-
+
+
+
+
+
+
+ Specifies a shorter description for the service that is calculated per the service code.
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Specifies the status and status update time of the track special instructions.
+
+
+
+
+ Specifies the estimated delivery time that was originally estimated when the shipment was shipped.
+
+
+
+
+ Specifies the time the customer requested a change to the shipment.
+
+
+
+
+ The requested appointment time for delivery.
+
+
+
+ Used when a cargo shipment is split across vehicles. This is used to give the status of each part of the shipment.
@@ -1320,6 +2080,26 @@
+
+
+
+
+
+
+
+
+
+
+ Specifies the details about the status of the track information for the shipments being tracked.
+
+
+
+
+
+
+
+
+ Descriptive data that governs data payload language/translations. The TransactionDetail from the request is echoed back to the caller in the corresponding reply.
@@ -1368,6 +2148,11 @@
Used in authentication of the sender's identity.
+
+
+ This was renamed from cspCredential.
+
+ Credential used to authenticate a specific software application. This value is provided by FedEx after registration.
@@ -1402,7 +2187,7 @@
Identifies a system or sub-system which performs an operation.
-
+ Identifies the service business level.
@@ -1421,6 +2206,9 @@
+
+
+
@@ -1433,11 +2221,8 @@
-
-
-
-
-
+
+
@@ -1446,10 +2231,6 @@
-
-
-
-
@@ -1462,11 +2243,15 @@
+
+
+
+
-
-
+
+
@@ -1474,8 +2259,8 @@
-
-
+
+
@@ -1483,8 +2268,8 @@
-
-
+
+
@@ -1492,8 +2277,8 @@
-
-
+
+
@@ -1504,7 +2289,7 @@
-
+
-
+
\ No newline at end of file
diff --git a/app/code/Magento/Fedex/view/frontend/web/js/model/shipping-rates-validation-rules.js b/app/code/Magento/Fedex/view/frontend/web/js/model/shipping-rates-validation-rules.js
index c6e03eb9ebf84..e51a6cefa2cb0 100644
--- a/app/code/Magento/Fedex/view/frontend/web/js/model/shipping-rates-validation-rules.js
+++ b/app/code/Magento/Fedex/view/frontend/web/js/model/shipping-rates-validation-rules.js
@@ -15,6 +15,9 @@ define(
},
'country_id': {
'required': true
+ },
+ 'city': {
+ 'required': true
}
};
}
diff --git a/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php b/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php
index 84b6e554d4cfb..8616c186555e3 100644
--- a/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php
+++ b/app/code/Magento/OfflinePayments/Block/Info/Checkmo.php
@@ -49,20 +49,13 @@ public function getMailingAddress()
}
/**
- * Enter description here...
- *
+ * @deprecated
* @return $this
*/
protected function _convertAdditionalData()
{
- $details = @unserialize($this->getInfo()->getAdditionalData());
- if (is_array($details)) {
- $this->_payableTo = isset($details['payable_to']) ? (string)$details['payable_to'] : '';
- $this->_mailingAddress = isset($details['mailing_address']) ? (string)$details['mailing_address'] : '';
- } else {
- $this->_payableTo = '';
- $this->_mailingAddress = '';
- }
+ $this->_payableTo = $this->getInfo()->getAdditionalInformation('payable_to');
+ $this->_mailingAddress = $this->getInfo()->getAdditionalInformation('mailing_address');
return $this;
}
diff --git a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php
index cafbad48edc68..27209fc8d3538 100644
--- a/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php
+++ b/app/code/Magento/OfflinePayments/Test/Unit/Block/Info/CheckmoTest.php
@@ -5,81 +5,118 @@
*/
namespace Magento\OfflinePayments\Test\Unit\Block\Info;
+use Magento\Framework\View\Element\Template\Context;
+use Magento\OfflinePayments\Block\Info\Checkmo;
+use Magento\Payment\Model\Info;
+use PHPUnit_Framework_MockObject_MockObject as MockObject;
+
+/**
+ * CheckmoTest contains list of test for block methods testing
+ */
class CheckmoTest extends \PHPUnit_Framework_TestCase
{
/**
- * @var \Magento\OfflinePayments\Block\Info\Checkmo
+ * @var Info|MockObject
+ */
+ private $info;
+
+ /**
+ * @var Checkmo
*/
- protected $_model;
+ private $block;
+ /**
+ * @inheritdoc
+ */
protected function setUp()
{
- $context = $this->getMock('Magento\Framework\View\Element\Template\Context', [], [], '', false);
- $this->_model = new \Magento\OfflinePayments\Block\Info\Checkmo($context);
+ $context = $this->getMockBuilder(Context::class)
+ ->disableOriginalConstructor()
+ ->setMethods([])
+ ->getMock();
+
+ $this->info = $this->getMockBuilder(Info::class)
+ ->disableOriginalConstructor()
+ ->setMethods(['getAdditionalInformation'])
+ ->getMock();
+
+ $this->block = new Checkmo($context);
}
/**
+ * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getPayableTo
+ * @param array $details
+ * @param string|null $expected
* @dataProvider getPayableToDataProvider
*/
public function testGetPayableTo($details, $expected)
{
- $info = $this->getMock('Magento\Payment\Model\Info', ['getAdditionalData'], [], '', false);
- $info->expects($this->once())
- ->method('getAdditionalData')
- ->willReturn(serialize($details));
- $this->_model->setData('info', $info);
+ $this->info->expects(static::at(0))
+ ->method('getAdditionalInformation')
+ ->with('payable_to')
+ ->willReturn($details);
+ $this->block->setData('info', $this->info);
- $this->assertEquals($expected, $this->_model->getPayableTo());
+ static::assertEquals($expected, $this->block->getPayableTo());
}
/**
+ * Get list of variations for payable configuration option testing
* @return array
*/
public function getPayableToDataProvider()
{
return [
- [['payable_to' => 'payable'], 'payable'],
- ['', '']
+ ['payable_to' => 'payable', 'payable'],
+ ['', null]
];
}
/**
+ * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress
+ * @param array $details
+ * @param string|null $expected
* @dataProvider getMailingAddressDataProvider
*/
public function testGetMailingAddress($details, $expected)
{
- $info = $this->getMock('Magento\Payment\Model\Info', ['getAdditionalData'], [], '', false);
- $info->expects($this->once())
- ->method('getAdditionalData')
- ->willReturn(serialize($details));
- $this->_model->setData('info', $info);
+ $this->info->expects(static::at(1))
+ ->method('getAdditionalInformation')
+ ->with('mailing_address')
+ ->willReturn($details);
+ $this->block->setData('info', $this->info);
- $this->assertEquals($expected, $this->_model->getMailingAddress());
+ static::assertEquals($expected, $this->block->getMailingAddress());
}
/**
+ * Get list of variations for mailing address testing
* @return array
*/
public function getMailingAddressDataProvider()
{
return [
- [['mailing_address' => 'blah@blah.com'], 'blah@blah.com'],
- ['', '']
+ ['mailing_address' => 'blah@blah.com', 'blah@blah.com'],
+ ['mailing_address' => '', null]
];
}
+ /**
+ * @covers \Magento\OfflinePayments\Block\Info\Checkmo::getMailingAddress
+ */
public function testConvertAdditionalDataIsNeverCalled()
{
- $info = $this->getMock('Magento\Payment\Model\Info', ['getAdditionalData'], [], '', false);
- $info->expects($this->once())
- ->method('getAdditionalData')
- ->willReturn(serialize(['mailing_address' => 'blah@blah.com']));
- $this->_model->setData('info', $info);
+ $mailingAddress = 'blah@blah.com';
+ $this->info->expects(static::at(1))
+ ->method('getAdditionalInformation')
+ ->with('mailing_address')
+ ->willReturn($mailingAddress);
+ $this->block->setData('info', $this->info);
// First we set the property $this->_mailingAddress
- $this->_model->getMailingAddress();
+ $this->block->getMailingAddress();
// And now we get already setted property $this->_mailingAddress
- $this->assertEquals('blah@blah.com', $this->_model->getMailingAddress());
+ static::assertEquals($mailingAddress, $this->block->getMailingAddress());
}
}
diff --git a/app/code/Magento/OfflinePayments/composer.json b/app/code/Magento/OfflinePayments/composer.json
index 7bf3ac42f0131..7f344520fc626 100644
--- a/app/code/Magento/OfflinePayments/composer.json
+++ b/app/code/Magento/OfflinePayments/composer.json
@@ -8,7 +8,7 @@
"magento/framework": "100.0.*"
},
"type": "magento2-module",
- "version": "100.0.6",
+ "version": "100.0.7",
"license": [
"OSL-3.0",
"AFL-3.0"
diff --git a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml
index d5bff77e002d1..8c5cfd50cc110 100644
--- a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml
+++ b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/checkmo.phtml
@@ -10,7 +10,7 @@
*/
?>
escapeHtml($block->getMethod()->getTitle()) ?>
-getInfo()->getAdditionalData()): ?>
+getInfo()->getAdditionalInformation()): ?>
getPayableTo()): ?>
escapeHtml(__('Make Check payable to: %1', $block->getPayableTo())) ?>
diff --git a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml
index 6195cdbd77654..5587ac239d37e 100644
--- a/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml
+++ b/app/code/Magento/OfflinePayments/view/adminhtml/templates/info/pdf/checkmo.phtml
@@ -11,7 +11,7 @@
?>
escapeHtml($block->getMethod()->getTitle()) ?>
{{pdf_row_separator}}
-getInfo()->getAdditionalData()): ?>
+getInfo()->getAdditionalInformation()): ?>
{{pdf_row_separator}}
getPayableTo()): ?>
escapeHtml(__('Make Check payable to: %1', $block->getPayableTo())) ?>
diff --git a/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml b/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml
index f0dbff1add32b..3c0b6bb23086a 100644
--- a/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml
+++ b/app/code/Magento/OfflinePayments/view/frontend/templates/info/checkmo.phtml
@@ -11,7 +11,7 @@
?>
+ escapeHtml(__('Tracking information is currently not available. Please ')); ?>
+ getContactUsEnabled()) : ?>
+
+ escapeHtml(__('contact us')); ?>
+
+ escapeHtml(__(' for more information or ')); ?>
+
+ escapeHtml(__('email us at ')); ?>
+
+